Use Class.java static initialization code.

This commit is contained in:
Brendan Dahl 2015-02-24 12:45:18 -08:00
Родитель 5e79167004
Коммит 764c00beb4
15 изменённых файлов: 1787 добавлений и 196 удалений

Просмотреть файл

@ -448,29 +448,20 @@ module J2ME {
* are stored for this class.
*/
getClassObject(): java.lang.Class {
return getRuntimeKlass($, this.klass).classObject;
return $.getRuntimeKlass(this.klass).classObject;
}
/**
* Object that holds static properties for this class.
*/
getStaticObject(ctx: Context): java.lang.Object {
return <java.lang.Object><any>getRuntimeKlass(ctx.runtime, this.klass);
return <java.lang.Object><any>ctx.runtime.getRuntimeKlass(this.klass);
}
getField(fieldKey: string) : FieldInfo {
return CLASSES.getField(this, fieldKey);
}
getClassInitLockObject(ctx: Context) {
if (!(this.className in ctx.runtime.classInitLockObjects)) {
ctx.runtime.classInitLockObjects[this.className] = {
classInfo: this
};
}
return ctx.runtime.classInitLockObjects[this.className];
}
toString() {
return "[class " + this.className + "]";
}

Просмотреть файл

@ -320,6 +320,7 @@ com/sun/midp/lcdui/DisplayDevice.isPrimaryDisplay.()Z
com/sun/midp/main/MIDletProxy.getClassName.()Ljava/lang/String;
com/sun/midp/rms/RmsEnvironment.getSecureFilenameBase.(I)Ljava/lang/String;
com/sun/midp/midletsuite/SuiteContainerAdapter.getSecureFilenameBase.(I)Ljava/lang/String;
java/lang/Class.initialize.()V
com/nokia/mid/ui/gestures/GestureEventImpl.getType.()I
com/nokia/mid/ui/gestures/GestureEventImpl.getDragDistanceX.()I
com/nokia/mid/ui/gestures/GestureEventImpl.getDragDistanceY.()I

Просмотреть файл

@ -11,6 +11,18 @@ module J2ME {
}
}
},
"java/lang/Class": {
fields: {
instanceSymbols: {
"status.I": "status"
}
},
methods: {
instanceSymbols: {
"initialize.()V": "initialize"
}
}
},
"java/lang/String": {
fields: {
instanceSymbols: {
@ -195,6 +207,8 @@ module J2ME {
* RuntimeKlass associated with this Class object.
*/
runtimeKlass: RuntimeKlass;
status: number;
initialize(): void;
}
export interface String extends java.lang.Object {

Просмотреть файл

@ -26,6 +26,14 @@ module J2ME {
traceWriter.writeLn(toDebugString(array) + "[" + index + "] (" + toDebugString(array[index]) + ")");
}
function classInitAndUnwindCheck(classInfo: ClassInfo, pc: number) {
classInitCheck(classInfo);
if (U) {
$.ctx.current().pc = pc;
return;
}
}
/**
* Optimize method bytecode.
*/
@ -937,7 +945,7 @@ module J2ME {
case Bytecodes.ANEWARRAY:
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
classInitCheck(classInfo, frame.pc - 3);
classInitAndUnwindCheck(classInfo, frame.pc - 3);
size = stack.pop();
stack.push(newArray(classInfo.klass, size));
break;
@ -992,7 +1000,7 @@ module J2ME {
case Bytecodes.GETSTATIC:
index = frame.read16();
fieldInfo = resolveField(index, mi.classInfo, true);
classInitCheck(fieldInfo.classInfo, frame.pc - 3);
classInitAndUnwindCheck(fieldInfo.classInfo, frame.pc - 3);
if (U) {
return;
}
@ -1002,7 +1010,7 @@ module J2ME {
case Bytecodes.PUTSTATIC:
index = frame.read16();
fieldInfo = resolveField(index, mi.classInfo, true);
classInitCheck(fieldInfo.classInfo, frame.pc - 3);
classInitAndUnwindCheck(fieldInfo.classInfo, frame.pc - 3);
if (U) {
return;
}
@ -1011,7 +1019,7 @@ module J2ME {
case Bytecodes.NEW:
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
classInitCheck(classInfo, frame.pc - 3);
classInitAndUnwindCheck(classInfo, frame.pc - 3);
if (U) {
return;
}
@ -1140,7 +1148,7 @@ module J2ME {
}
if (isStatic) {
classInitCheck(calleeMethodInfo.classInfo, lastPC);
classInitAndUnwindCheck(calleeMethodInfo.classInfo, lastPC);
if (U) {
return;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,425 @@
/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package java.lang;
/**
* Instances of the class <code>Class</code> represent classes and interfaces
* in a running Java application. Every array also belongs to a class that is
* reflected as a <code>Class</code> object that is shared by all arrays with
* the same element type and number of dimensions.
*
* <p> <code>Class</code> has no public constructor. Instead <code>Class</code>
* objects are constructed automatically by the Java Virtual Machine as classes
* are loaded.
*
* <p> The following example uses a <code>Class</code> object to print the
* class name of an object:
*
* <p> <blockquote><pre>
* void printClassName(Object obj) {
* System.out.println("The class of " + obj +
* " is " + obj.getClass().getName());
* }
* </pre></blockquote>
*
* @version 12/17/01 (CLDC 1.1)
* @since JDK1.0, CLDC 1.0
*/
public final
class Class {
/*
* Constructor. Only the Java Virtual Machine creates Class
* objects.
*/
private Class() {}
/**
* Converts the object to a string. The string representation is the
* string "class" or "interface", followed by a space, and then by the
* fully qualified name of the class in the format returned by
* <code>getName</code>. If this <code>Class</code> object represents a
* primitive type, this method returns the name of the primitive type. If
* this <code>Class</code> object represents void this method returns
* "void".
*
* @return a string representation of this class object.
*/
public String toString() {
return (isInterface() ? "interface " : "class ") + getName();
}
/**
* Returns the <code>Class</code> object associated with the class
* with the given string name. Given the fully-qualified name for
* a class or interface, this method attempts to locate, load and
* link the class.
* <p>
* For example, the following code fragment returns the runtime
* <code>Class</code> descriptor for the class named
* <code>java.lang.Thread</code>:
* <ul><code>
* Class&nbsp;t&nbsp;= Class.forName("java.lang.Thread")
* </code></ul>
*
* @param className the fully qualified name of the desired class.
* @return the <code>Class</code> object for the class with the
* specified name.
* @exception ClassNotFoundException if the class could not be found.
* @exception Error if the function fails for any other reason.
* @since JDK1.0
*/
public static Class forName(String className) throws ClassNotFoundException {
// forName is broken into two parts since forName0 may unwind.
forName0(className);
return forName1(className);
}
private static native void forName0(String className) throws ClassNotFoundException;
private static native Class forName1(String className);
/**
* Creates a new instance of a class.
*
* @return a newly allocated instance of the class represented by this
* object. This is done exactly as if by a <code>new</code>
* expression with an empty argument list.
* @exception IllegalAccessException if the class or initializer is
* not accessible.
* @exception InstantiationException if an application tries to
* instantiate an abstract class or an interface, or if the
* instantiation fails for some other reason.
* @since JDK1.0
*/
public native Object newInstance()
throws InstantiationException, IllegalAccessException;
/**
* Determines if the specified <code>Object</code> is assignment-compatible
* with the object represented by this <code>Class</code>. This method is
* the dynamic equivalent of the Java language <code>instanceof</code>
* operator. The method returns <code>true</code> if the specified
* <code>Object</code> argument is non-null and can be cast to the
* reference type represented by this <code>Class</code> object without
* raising a <code>ClassCastException.</code> It returns <code>false</code>
* otherwise.
*
* <p> Specifically, if this <code>Class</code> object represents a
* declared class, this method returns <code>true</code> if the specified
* <code>Object</code> argument is an instance of the represented class (or
* of any of its subclasses); it returns <code>false</code> otherwise. If
* this <code>Class</code> object represents an array class, this method
* returns <code>true</code> if the specified <code>Object</code> argument
* can be converted to an object of the array class by an identity
* conversion or by a widening reference conversion; it returns
* <code>false</code> otherwise. If this <code>Class</code> object
* represents an interface, this method returns <code>true</code> if the
* class or any superclass of the specified <code>Object</code> argument
* implements this interface; it returns <code>false</code> otherwise. If
* this <code>Class</code> object represents a primitive type, this method
* returns <code>false</code>.
*
* @param obj the object to check
* @return true if <code>obj</code> is an instance of this class
*
* @since JDK1.1
*/
public native boolean isInstance(Object obj);
/**
* Determines if the class or interface represented by this
* <code>Class</code> object is either the same as, or is a superclass or
* superinterface of, the class or interface represented by the specified
* <code>Class</code> parameter. It returns <code>true</code> if so;
* otherwise it returns <code>false</code>. If this <code>Class</code>
* object represents a primitive type, this method returns
* <code>true</code> if the specified <code>Class</code> parameter is
* exactly this <code>Class</code> object; otherwise it returns
* <code>false</code>.
*
* <p> Specifically, this method tests whether the type represented by the
* specified <code>Class</code> parameter can be converted to the type
* represented by this <code>Class</code> object via an identity conversion
* or via a widening reference conversion. See <em>The Java Language
* Specification</em>, sections 5.1.1 and 5.1.4 , for details.
*
* @param cls the <code>Class</code> object to be checked
* @return the <code>boolean</code> value indicating whether objects of the
* type <code>cls</code> can be assigned to objects of this class
* @exception NullPointerException if the specified Class parameter is
* null.
* @since JDK1.1
*/
public native boolean isAssignableFrom(Class cls);
/**
* Determines if the specified <code>Class</code> object represents an
* interface type.
*
* @return <code>true</code> if this object represents an interface;
* <code>false</code> otherwise.
*/
public native boolean isInterface();
/**
* Determines if this <code>Class</code> object represents an array class.
*
* @return <code>true</code> if this object represents an array class;
* <code>false</code> otherwise.
* @since JDK1.1
*/
public native boolean isArray();
/**
* Returns the fully-qualified name of the entity (class, interface, array
* class, primitive type, or void) represented by this <code>Class</code>
* object, as a <code>String</code>.
*
* <p> If this <code>Class</code> object represents a class of arrays, then
* the internal form of the name consists of the name of the element type
* in Java signature format, preceded by one or more "<tt>[</tt>"
* characters representing the depth of array nesting. Thus:
*
* <blockquote><pre>
* (new Object[3]).getClass().getName()
* </pre></blockquote>
*
* returns "<code>[Ljava.lang.Object;</code>" and:
*
* <blockquote><pre>
* (new int[3][4][5][6][7][8][9]).getClass().getName()
* </pre></blockquote>
*
* returns "<code>[[[[[[[I</code>". The encoding of element type names
* is as follows:
*
* <blockquote><pre>
* B byte
* C char
* D double
* F float
* I int
* J long
* L<i>classname;</i> class or interface
* S short
* Z boolean
* </pre></blockquote>
*
* The class or interface name <tt><i>classname</i></tt> is given in fully
* qualified form as shown in the example above.
*
* @return the fully qualified name of the class or interface
* represented by this object.
*/
public native String getName();
/**
* Finds a resource with a given name in the application's
* JAR file. This method returns
* <code>null</code> if no resource with this name is found
* in the application's JAR file.
* <p>
* The resource names can be represented in two
* different formats: absolute or relative.
* <p>
* Absolute format:
* <ul><code>/packagePathName/resourceName</code></ul>
* <p>
* Relative format:
* <ul><code>resourceName</code></ul>
* <p>
* In the absolute format, the programmer provides a fully
* qualified name that includes both the full path and the
* name of the resource inside the JAR file. In the path names,
* the character "/" is used as the separator.
* <p>
* In the relative format, the programmer provides only
* the name of the actual resource. Relative names are
* converted to absolute names by the system by prepending
* the resource name with the fully qualified package name
* of class upon which the <code>getResourceAsStream</code>
* method was called.
*
* @param name name of the desired resource
* @return a <code>java.io.InputStream</code> object.
*/
public java.io.InputStream getResourceAsStream(String name) {
try {
if (name.length() > 0 && name.charAt(0) == '/') {
/* Absolute format */
name = name.substring(1);
} else {
/* Relative format */
String className = this.getName();
int dotIndex = className.lastIndexOf('.');
if (dotIndex >= 0) {
name = className.substring(0, dotIndex + 1).replace('.', '/')
+ name;
}
}
return new com.sun.cldc.io.ResourceInputStream(name);
} catch (java.io.IOException x) {
return null;
}
}
/*
* This private function is used during virtual machine initialization.
* The user does not normally see this function.
*/
// private static void runCustomCode() {}
/* The code below is specific to this VM */
/**
* Returns the <code>Class</code> representing the superclass of the entity
* (class, interface, primitive type or void) represented by this
* <code>Class</code>. If this <code>Class</code> represents either the
* <code>Object</code> class, an interface, a primitive type, or void, then
* null is returned. If this object represents an array class then the
* <code>Class</code> object representing the <code>Object</code> class is
* returned.
*
* Note that this method is not supported by CLDC.
* We have made the method private, since it is
* needed by our implementation.
*
* @return the superclass of the class represented by this object.
*/
private native Class getSuperclass();
/*
* This private variable is used by the VM.
* Users never see it.
*/
private transient Object vmClass;
private int status;
private Thread thread;
private static final int IN_PROGRESS = 1;
private static final int VERIFIED = 2;
private static final int INITIALIZED = 4;
private static final int ERROR = 8;
// Native for invoking <clinit>
private native void invoke_clinit();
/**
* Initialization at step 9:
* If ENABLE_ISOLATES == false
* Remove the <clinit> method after the class is initialized.
* If ENABLE_ISOLATES == true, clear class initialization
* barrier.
*/
private native void init9();
private native void invoke_verify();
/*
* Implements the 11 step program detailed in Java Language Specification
* 12.4.2
*/
void initialize() throws Throwable {
// Step 1
synchronized (this) {
// Step 2
while ((status & IN_PROGRESS) != 0 && thread != Thread.currentThread()) {
try{
wait();
} catch (InterruptedException e) {
}
}
// Step 3
if ((status & IN_PROGRESS) != 0 && thread == Thread.currentThread()) {
return;
}
// Step 4
if ((status & INITIALIZED) != 0) {
return;
}
// Step 5
if (status == ERROR) {
throw new NoClassDefFoundError(getName());
}
/* Note: CLDC 1.0 does not have NoClassDefFoundError class */
// Step 6
status |= IN_PROGRESS;
thread = Thread.currentThread();
}
try {
// Step 7
invoke_verify();
Class s = getSuperclass();
if (s != null && (s.status & INITIALIZED) == 0) {
// The test of s.status is not part of the spec, but
// it saves us doing a lot of work in the most common
// case.
s.initialize();
}
// Step 8
invoke_clinit();
// Step 9
synchronized (this) {
status &= ~IN_PROGRESS;
status |= INITIALIZED;
thread = null;
init9();
notifyAll();
}
} catch(Throwable e) {
// Step 10 and 11
// CR 6224346, The cldc_vm threading mechanism is such that
// we can just jam these values in without fear of another
// thread doing the same since only this thread can be
// executing the initialize() method and the scheduler is
// non-preemptive. We do this here in case the monitorenter
// fails due to OOME because some other thread holds the lock,
// memory is low and we need to allocate a ConditionDesc to
// wait for the lock.
status = ERROR;
thread = null;
synchronized (this) {
notifyAll();
throwError(e);
}
}
}
private Error throwError(Throwable e) throws Error {
throw (e instanceof Error) ? (Error)e
: new Error("Static initializer: " + e.getClass().getName() +
", " + e.getMessage());
}
}

Просмотреть файл

@ -63,6 +63,7 @@ module J2ME {
"java/lang/Class.newInstance.()Ljava/lang/Object;": YieldReason.Root,
"java/lang/Thread.yield.()V": YieldReason.Root,
"java/lang/Thread.start0.()V": YieldReason.Root,
"java/lang/Class.forName0.(Ljava/lang/String;)V": YieldReason.Root,
// Test Files:
"gnu/testlet/vm/NativeTest.throwExceptionAfterPause.()V": YieldReason.Root,
"gnu/testlet/vm/NativeTest.returnAfterPause.()I": YieldReason.Root,
@ -192,6 +193,22 @@ module J2ME {
}
export function canStaticInitializerYield(classInfo: ClassInfo): YieldReason {
var result = YieldReason.None;
while (classInfo) {
var staticInitializer = classInfo.staticInitializer;
classInfo = classInfo.superClass;
if (!staticInitializer) {
continue;
}
result = canYield(staticInitializer);
if (result !== YieldReason.None) {
return result;
}
}
return result;
}
export function canYield(methodInfo: MethodInfo): YieldReason {
yieldWriter && yieldWriter.enter("> " + methodInfo.implKey);
if (yieldMap[methodInfo.implKey] !== undefined) {
@ -221,6 +238,14 @@ module J2ME {
while (stream.currentBCI < methodInfo.code.length) {
var op: Bytecodes = stream.currentBC();
switch (op) {
case Bytecodes.NEW:
case Bytecodes.GETSTATIC:
case Bytecodes.PUTSTATIC:
var cpi = stream.readCPI();
var fieldInfo = methodInfo.classInfo.resolve(cpi, true);
var classInfo = fieldInfo.classInfo;
result = canStaticInitializerYield(classInfo);
break;
case Bytecodes.MONITORENTER:
case Bytecodes.MONITOREXIT:
result = YieldReason.MonitorEnterExit;
@ -246,6 +271,13 @@ module J2ME {
}
}
if (op === Bytecodes.INVOKESTATIC) {
result = canStaticInitializerYield(methodInfo.classInfo);
if (result !== YieldReason.None) {
break;
}
}
if (!isStaticallyBound(op, callee)) {
var callees = [];
result = YieldReason.Virtual;

Просмотреть файл

@ -671,12 +671,18 @@ module J2ME {
}
emitGetField(fieldInfo: FieldInfo, isStatic: boolean) {
if (isStatic) {
this.emitClassInitializationCheck(fieldInfo.classInfo);
}
var signature = TypeDescriptor.makeTypeDescriptor(fieldInfo.signature);
var object = isStatic ? this.runtimeClass(fieldInfo.classInfo) : this.pop(Kind.Reference);
this.emitPush(signature.kind, object + "." + fieldInfo.mangledName);
}
emitPutField(fieldInfo: FieldInfo, isStatic: boolean) {
if (isStatic) {
this.emitClassInitializationCheck(fieldInfo.classInfo);
}
var signature = TypeDescriptor.makeTypeDescriptor(fieldInfo.signature);
var value = this.pop(signature.kind);
var object = isStatic ? this.runtimeClass(fieldInfo.classInfo) : this.pop(Kind.Reference);
@ -740,14 +746,14 @@ module J2ME {
var message = "Optimized ClassInitializationCheck: " + classInfo.className + ", self access.";
emitDebugInfoComments && this.blockEmitter.writeLn("// " + message);
baselineCounter && baselineCounter.count(message);
} else if (this.methodInfo.classInfo.isAssignableTo(classInfo)) {
} else if (!classInfo.isInterface && this.methodInfo.classInfo.isAssignableTo(classInfo)) {
var message = "Optimized ClassInitializationCheck: " + classInfo.className + ", base access.";
emitDebugInfoComments && this.blockEmitter.writeLn("// " + message);
baselineCounter && baselineCounter.count(message);
} else {
baselineCounter && baselineCounter.count("ClassInitializationCheck: " + classInfo.className);
this.blockEmitter.writeLn(this.runtimeClass(classInfo) + ";");
if (classInfo.staticInitializer && canYield(classInfo.staticInitializer)) {
this.blockEmitter.writeLn("if ($.initialized[\"" + classInfo.className + "\"] === undefined) { " + this.runtimeClassObject(classInfo) + ".initialize(); }");
if (canStaticInitializerYield(classInfo)) {
this.emitUnwind(this.blockEmitter, String(this.pc), String(this.pc));
} else {
emitCompilerAssertions && this.emitNoUnwindAssertion();
@ -800,6 +806,7 @@ module J2ME {
if (calleeCanYield) {
this.emitUnwind(this.blockEmitter, String(this.pc), String(nextPC));
} else {
emitCompilerAssertions && this.emitUndefinedReturnAssertion();
emitCompilerAssertions && this.emitNoUnwindAssertion();
}
if (types[0].kind !== Kind.Void) {
@ -947,6 +954,10 @@ module J2ME {
this.blockEmitter.writeLn("if (U) { J2ME.Debug.assert(false, 'Unexpected unwind.'); }");
}
emitUndefinedReturnAssertion() {
this.blockEmitter.writeLn("if (U && re !== undefined) { J2ME.Debug.assert(false, 'Unexpected return value during unwind.'); }");
}
private emitMonitorEnter(emitter: Emitter, nextPC: number, object: string) {
this.hasMonitorEnter = true;

Просмотреть файл

@ -260,7 +260,7 @@ Native["com/sun/cldchi/jvm/JVM.monotonicTimeMillis.()J"] = function() {
};
Native["java/lang/Object.getClass.()Ljava/lang/Class;"] = function() {
return J2ME.getRuntimeKlass($.ctx.runtime, this.klass).classObject;
return $.getRuntimeKlass(this.klass).classObject;
};
Native["java/lang/Object.wait.(J)V"] = function(timeout) {
@ -275,65 +275,56 @@ Native["java/lang/Object.notifyAll.()V"] = function() {
$.ctx.notify(this, true);
};
Native["java/lang/Class.invoke_clinit.()V"] = function() {
var classInfo = this.classInfo;
var className = classInfo.className;
var runtime = $.ctx.runtime;
if (runtime.initialized[className] || runtime.pending[className])
return;
runtime.pending[className] = true;
if (className === "com/sun/cldc/isolate/Isolate") {
// The very first isolate is granted access to the isolate API.
var isolate = classInfo.getStaticObject($.ctx);
CLASSES.getField(classInfo, "S._API_access_ok.I").set(isolate, 1);
Native["java/lang/Class.getSuperclass.()Ljava/lang/Class;"] = function() {
var superKlass = this.runtimeKlass.templateKlass.superKlass;
if (!superKlass) {
return null;
}
var clinit = CLASSES.getMethod(classInfo, "S.<clinit>.()V");
return superKlass.classInfo.getClassObject();
};
var frames = [];
Native["java/lang/Class.invoke_clinit.()V"] = function() {
var classInfo = this.runtimeKlass.templateKlass.classInfo;
var className = classInfo.className;
var clinit = CLASSES.getMethod(classInfo, "S.<clinit>.()V");
if (clinit && clinit.classInfo.className === className) {
frames.push(Frame.create(clinit, [], 0));
}
if (classInfo.superClass) {
var classInitFrame = $.ctx.getClassInitFrame(classInfo.superClass);
if (classInitFrame) {
frames.push(classInitFrame);
}
}
if (frames.length) {
$.ctx.executeFrames(frames);
$.ctx.executeFrames([Frame.create(clinit, [], 0)]);
}
};
Native["java/lang/Class.invoke_verify.()V"] = function() {
// There is currently no verification.
};
Native["java/lang/Class.init9.()V"] = function() {
var classInfo = this.classInfo;
var className = classInfo.className;
var runtime = $.ctx.runtime;
if (runtime.initialized[className])
return;
runtime.pending[className] = false;
runtime.initialized[className] = true;
$.setClassInitialized(this.runtimeKlass);
};
Native["java/lang/Class.getName.()Ljava/lang/String;"] = function() {
return J2ME.newString(this.runtimeKlass.templateKlass.classInfo.className.replace(/\//g, "."));
};
Native["java/lang/Class.forName.(Ljava/lang/String;)Ljava/lang/Class;"] = function(name) {
try {
if (!name)
throw new J2ME.ClassNotFoundException();
var className = util.fromJavaString(name).replace(/\./g, "/");
var classInfo = null;
classInfo = CLASSES.getClass(className);
} catch (e) {
if (e instanceof (J2ME.ClassNotFoundException))
throw $.newClassNotFoundException("'" + e.message + "' not found.");
throw e;
}
J2ME.linkKlass(classInfo);
var classObject = classInfo.getClassObject();
J2ME.Debug.assert(!U, "Unwinding isn't currently supported here.");
return classObject;
Native["java/lang/Class.forName0.(Ljava/lang/String;)V"] = function(name) {
var classInfo = null;
try {
if (!name)
throw new J2ME.ClassNotFoundException();
var className = util.fromJavaString(name).replace(/\./g, "/");
classInfo = CLASSES.getClass(className);
} catch (e) {
if (e instanceof (J2ME.ClassNotFoundException))
throw $.newClassNotFoundException("'" + e.message + "' not found.");
throw e;
}
// The following can trigger an unwind.
J2ME.classInitCheck(classInfo);
};
Native["java/lang/Class.forName1.(Ljava/lang/String;)Ljava/lang/Class;"] = function(name) {
var className = util.fromJavaString(name).replace(/\./g, "/");
var classInfo = CLASSES.getClass(className);
var classObject = classInfo.getClassObject();
return classObject;
};
Native["java/lang/Class.newInstance.()Ljava/lang/Object;"] = function() {
@ -564,7 +555,7 @@ Native["java/lang/Thread.start0.()V"] = function() {
])
});
newCtx.start(new Frame(syntheticMethod, [ this ], 0));
newCtx.start([new Frame(syntheticMethod, [ this ], 0)]);
};
Native["java/lang/Thread.internalExit.()V"] = function() {

Просмотреть файл

@ -162,29 +162,29 @@ casper.test.begin("unit tests", 19 + gfxTests.length, function(test) {
casper.waitForText("DONE", function() {
test.assertTextExists("I m\n" +
"I a ma\n" +
"I 2\n" +
"I 3\n" +
"I ma\n" +
"I 2\n" +
"I 3\n" +
"I 1 isolate\n" +
"I Isolate ID correct\n" +
"I 4\n" +
"I 5\n" +
"I 6\n" +
"I 1 isolate\n" +
"I ma\n" +
"I ma\n" +
"I 3 isolates\n" +
"I 1 m1\n" +
"I 4\n" +
"I 2 m2\n" +
"I 5\n" +
"I 2 m2\n" +
"I 6\n" +
"I ma\n" +
"I 1 isolate\n" +
"I Isolates terminated\n" +
"I r mar\n" +
"I 2\n" +
"I 3\n" +
"I mar\n" +
"I c marc\n" +
"I 2\n" +
"I 3\n" +
"I marc\n" +
"I Main isolate still running");
});

Просмотреть файл

@ -0,0 +1,38 @@
package java.lang;
import gnu.testlet.Testlet;
import gnu.testlet.TestHarness;
class Monkey {
public static final String name;
static {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
name = "Monkey";
}
}
public class TestStaticInitRaceCondition extends Thread implements Testlet {
private TestHarness th;
public int getExpectedPass() { return 2; }
public int getExpectedFail() { return 0; }
public int getExpectedKnownFail() { return 0; }
public void test(TestHarness th) {
this.th = th;
start();
run();
try {
this.join();
} catch (InterruptedException e) {
th.fail();
}
}
public void run() {
th.check(Monkey.name, "Monkey");
}
}

Просмотреть файл

@ -58,6 +58,7 @@ module J2ME {
* because they don't have any static state.
*/
var classNames = [
"com/sun/cldc/isolate/Isolate", // Not used frequently, but needs setup before we start the isolates.
"java/lang/Integer",
"java/lang/Character",
"java/lang/Math",

Просмотреть файл

@ -439,85 +439,14 @@ module J2ME {
return returnValue;
}
getClassInitFrame(classInfo: ClassInfo) {
if (this.runtime.initialized[classInfo.className]) {
return;
}
classInfo.thread = this.thread;
var syntheticMethod = new MethodInfo({
name: "ClassInitSynthetic",
signature: "()V",
isStatic: false,
classInfo: ClassInfo.createFromObject({
className: {value: classInfo.className},
vmc: {value: {}},
vfc: {value: {}},
constant_pool: {value: [
null,
{tag: TAGS.CONSTANT_Methodref, class_index: 2, name_and_type_index: 4},
{tag: TAGS.CONSTANT_Class, name_index: 3},
{bytes: "java/lang/Class"},
{name_index: 5, signature_index: 6},
{bytes: "invoke_clinit"},
{bytes: "()V"},
{tag: TAGS.CONSTANT_Methodref, class_index: 2, name_and_type_index: 8},
{name_index: 9, signature_index: 10},
{bytes: "init9"},
{bytes: "()V"},
]},
}),
code: new Uint8Array([
0x2a, // aload_0
0x59, // dup
0x59, // dup
0x59, // dup
0xc2, // monitorenter
0xb7, 0x00, 0x01, // invokespecial <idx=1>
0xb7, 0x00, 0x07, // invokespecial <idx=7>
0xc3, // monitorexit
0xb1, // return
])
});
return Frame.create(syntheticMethod, [classInfo.getClassInitLockObject(this)], 0);
}
pushClassInitFrame(classInfo: ClassInfo) {
if (this.runtime.initialized[classInfo.className] ||
this.runtime.pending[classInfo.className]) {
return;
}
var needsInitialization = true;
if (!classInfo.staticInitializer) {
needsInitialization = false;
// Special case Isolate.
if (classInfo.className === "com/sun/cldc/isolate/Isolate") {
needsInitialization = true;
}
var superClass = classInfo.superClass;
while (superClass) {
if (!this.runtime.initialized[superClass.className] &&
superClass.staticInitializer) {
needsInitialization = true;
break;
}
superClass = superClass.superClass;
}
}
linkKlass(classInfo);
if (!needsInitialization) {
this.runtime.initialized[classInfo.className] = true;
return;
}
var classInitFrame = this.getClassInitFrame(classInfo);
this.executeFrames([classInitFrame]);
}
createException(className: string, message?: string) {
if (!message) {
message = "";
}
message = "" + message;
var classInfo = CLASSES.loadAndLinkClass(className);
classInitCheck(classInfo);
release || Debug.assert(!U, "Unexpected unwind during createException.");
runtimeCounter && runtimeCounter.count("createException " + className);
var exception = new classInfo.klass();
var methodInfo = CLASSES.getMethod(classInfo, "I.<init>.(Ljava/lang/String;)V");
@ -547,8 +476,9 @@ module J2ME {
Context.setWriters(Context.writer);
}
start(frame: Frame) {
this.frames = [Frame.Start, frame];
start(frames: Frame[]) {
frames.unshift(Frame.Start);
this.frames = frames;
this.resume();
}

Просмотреть файл

@ -9,41 +9,48 @@ module J2ME {
// ...
}
startIsolate0(className: string, args: string []) {
private createIsolateCtx(): Context {
var runtime = new Runtime(this);
var ctx = new Context(runtime);
ctx.setAsCurrentContext();
ctx.thread = runtime.mainThread = <java.lang.Thread>newObject(CLASSES.java_lang_Thread.klass);
ctx.thread.pid = util.id();
ctx.thread.alive = true;
// The constructor will set the real priority, however one is needed for the scheduler.
ctx.thread.priority = NORMAL_PRIORITY;
runtime.preInitializeClasses(ctx);
return ctx;
}
startIsolate0(className: string, args: string []) {
var ctx = this.createIsolateCtx();
var isolateClassInfo = CLASSES.getClass("com/sun/cldc/isolate/Isolate");
linkKlass(isolateClassInfo);
var isolate: Isolate = <Isolate>newObject(isolateClassInfo.klass);
isolate.id = util.id();
var array = newStringArray(args.length);
for (var n = 0; n < args.length; ++n)
array[n] = args[n] ? J2ME.newString(args[n]) : null;
ctx.executeFrames([
// The <init> frames go at the end of the array so they are executed first to initialize the thread and isolate.
ctx.start([
Frame.create(CLASSES.getMethod(isolateClassInfo, "I.start.()V"), [ isolate ], 0),
Frame.create(CLASSES.getMethod(isolateClassInfo, "I.<init>.(Ljava/lang/String;[Ljava/lang/String;)V"),
[ isolate, J2ME.newString(className.replace(/\./g, "/")), array ], 0)
[ isolate, J2ME.newString(className.replace(/\./g, "/")), array ], 0)
]);
ctx.start(Frame.create(CLASSES.getMethod(isolateClassInfo, "I.start.()V"), [ isolate ], 0));
release || Debug.assert(!U, "Unexpected unwind during isolate initialization.");
}
startIsolate(isolate: Isolate) {
var mainClass = util.fromJavaString(isolate.klass.classInfo.getField("I._mainClass.Ljava/lang/String;").get(isolate)).replace(/\./g, "/");
var mainArgs = isolate.klass.classInfo.getField("I._mainArgs.[Ljava/lang/String;").get(isolate);
var runtime = new J2ME.Runtime(this);
var ctx = new Context(runtime);
var ctx = this.createIsolateCtx();
var runtime = ctx.runtime;
isolate.runtime = runtime;
runtime.isolate = isolate;
runtime.updateStatus(RuntimeStatus.Started);
var mainClass = util.fromJavaString(isolate.klass.classInfo.getField("I._mainClass.Ljava/lang/String;").get(isolate)).replace(/\./g, "/");
var mainArgs = isolate.klass.classInfo.getField("I._mainArgs.[Ljava/lang/String;").get(isolate);
var classInfo = CLASSES.getClass(mainClass);
linkKlass(classInfo);
if (!classInfo)
@ -53,22 +60,17 @@ module J2ME {
if (!entryPoint)
throw new Error("Could not find main method in class " + mainClass);
ctx.thread = runtime.mainThread = <java.lang.Thread>newObject(CLASSES.java_lang_Thread.klass);
ctx.thread.pid = util.id();
ctx.thread.alive = true;
var oldCtx = $.ctx;
ctx.setAsCurrentContext();
ctx.executeFrames([Frame.create(CLASSES.getMethod(CLASSES.java_lang_Thread, "I.<init>.(Ljava/lang/String;)V"),
[ runtime.mainThread, J2ME.newString("main") ], 0)])
oldCtx.setAsCurrentContext();
var args = J2ME.newStringArray(mainArgs.length);
for (var n = 0; n < mainArgs.length; ++n) {
args[n] = mainArgs[n];
}
ctx.start(Frame.create(entryPoint, [ args ], 0));
ctx.start([
Frame.create(entryPoint, [ args ], 0),
Frame.create(CLASSES.getMethod(CLASSES.java_lang_Thread, "I.<init>.(Ljava/lang/String;)V"),
[ runtime.mainThread, J2ME.newString("main") ], 0)
]);
release || Debug.assert(!U, "Unexpected unwind during isolate initialization.");
}
}

Просмотреть файл

@ -441,7 +441,6 @@ module J2ME {
pending: any;
staticFields: any;
classObjects: any;
classInitLockObjects: any;
ctx: Context;
isolate: com.sun.cldc.isolate.Isolate;
@ -461,11 +460,43 @@ module J2ME {
this.staticFields = {};
this.classObjects = {};
this.ctx = null;
this.classInitLockObjects = {};
this._runtimeId = RuntimeTemplate._nextRuntimeId ++;
this._nextHashCode = this._runtimeId << 24;
}
preInitializeClasses(ctx: Context) {
var prevCtx = $ ? $.ctx : null;
var preInit = CLASSES.preInitializedClasses;
ctx.setAsCurrentContext();
for (var i = 0; i < preInit.length; i++) {
var runtimeKlass = this.getRuntimeKlass(preInit[i].klass);
runtimeKlass.classObject.initialize();
release || Debug.assert(!U, "Unexpected unwind during preInitializeClasses.");
}
ctx.clearCurrentContext();
if (prevCtx) {
prevCtx.setAsCurrentContext();
}
}
/**
* After class intialization is finished the init9 method will invoke this so
* any further initialize calls can be avoided. This isn't set on the first call
* to a class initializer because there can be multiple calls into initialize from
* different threads that need trigger the Class.initialize() code so they block.
*/
setClassInitialized(runtimeKlass: RuntimeKlass) {
var className = runtimeKlass.templateKlass.classInfo.className;
this.initialized[className] = true;
}
getRuntimeKlass(klass: Klass): RuntimeKlass {
release || assert(!(klass instanceof RuntimeKlass));
release || assert(klass.classInfo.mangledName);
var runtimeKlass = this[klass.classInfo.mangledName];
return runtimeKlass;
}
/**
* Generates a new hash code for the specified |object|.
*/
@ -854,6 +885,15 @@ module J2ME {
release || assert(!runtimeKlass.classObject);
runtimeKlass.classObject = <java.lang.Class><any>new Klasses.java.lang.Class();
runtimeKlass.classObject.runtimeKlass = runtimeKlass;
var className = runtimeKlass.templateKlass.classInfo.className;
if (className === "java/lang/Object" ||
className === "java/lang/Class" ||
className === "java/lang/String" ||
className === "java/lang/Thread") {
(<any>runtimeKlass.classObject).status = 4;
$.setClassInitialized(runtimeKlass);
return;
}
var fields = runtimeKlass.templateKlass.classInfo.fields;
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
@ -892,10 +932,6 @@ module J2ME {
Object.defineProperty(this, classInfo.mangledName, {
value: runtimeKlass
});
initWriter && initWriter.writeLn("Running Static Constructor: " + classInfo.className);
$.ctx.pushClassInitFrame(classInfo);
release || assert(!U, "Unwinding during static initializer not supported.");
return runtimeKlass;
}
});
@ -952,14 +988,6 @@ module J2ME {
}
}
export function getRuntimeKlass(runtime: Runtime, klass: Klass): RuntimeKlass {
release || assert(!(klass instanceof RuntimeKlass));
release || assert(klass.classInfo.mangledName);
var runtimeKlass = runtime[klass.classInfo.mangledName];
// assert(runtimeKlass instanceof RuntimeKlass);
return runtimeKlass;
}
function setKlassSymbol(mangledName: string, klass: Klass) {
Object.defineProperty(jsGlobal, mangledName, {
value: klass
@ -1340,6 +1368,7 @@ module J2ME {
function linkKlassMethods(klass: Klass) {
linkWriter && linkWriter.enter("Link Klass Methods: " + klass);
var methods = klass.classInfo.methods;
var classBindings = Bindings[klass.classInfo.className];
for (var i = 0; i < methods.length; i++) {
var methodInfo = methods[i];
if (methodInfo.isAbstract) {
@ -1389,6 +1418,12 @@ module J2ME {
}
if (!methodInfo.isStatic) {
klass.prototype[methodInfo.mangledName] = fn;
if (classBindings && classBindings.methods && classBindings.methods.instanceSymbols) {
var methodKey = classBindings.methods.instanceSymbols[methodInfo.name + "." + methodInfo.signature];
if (methodKey) {
klass.prototype[methodKey] = fn;
}
}
}
}
@ -1843,15 +1878,13 @@ module J2ME {
return e;
}
export function classInitCheck(classInfo: ClassInfo, pc: number) {
if (classInfo.isArrayClass) {
return;
}
$.ctx.pushClassInitFrame(classInfo);
if (U) {
$.ctx.current().pc = pc;
export function classInitCheck(classInfo: ClassInfo) {
if (classInfo.isArrayClass || $.initialized[classInfo.className]) {
return;
}
linkKlass(classInfo);
var runtimeKlass = $.getRuntimeKlass(classInfo.klass);
runtimeKlass.classObject.initialize();
}
/**