diff --git a/actors.ts b/actors.ts index adcccdf6..b088c2a2 100644 --- a/actors.ts +++ b/actors.ts @@ -263,16 +263,9 @@ module J2ME { return this.superClass ? this.superClass.isAssignableTo(toClass) : false; } - getClassObject(ctx: Context) { - var className = this.className; - var classObjects = ctx.runtime.classObjects; - var classObject = classObjects[className]; - if (!classObject) { - classObject = util.newObject(CLASSES.java_lang_Class); - classObject.vmClass = this; - classObjects[className] = classObject; - } - return classObject; + getClassObject(ctx: Context): java.lang.Class { + // TODO: Check to make sure that it doesn't matter if this is done eagerly. + return runtimeKlass(ctx.runtime, this.klass).classObject; } getField(fieldKey: string) : FieldInfo { diff --git a/context.ts b/context.ts index b6677ded..ade72ff9 100644 --- a/context.ts +++ b/context.ts @@ -12,7 +12,7 @@ module J2ME { frameSets: any []; lockTimeout: number; lockLevel: number; - thread: any; + thread: java.lang.Thread; constructor(public runtime: Runtime) { this.frames = []; @@ -248,7 +248,7 @@ module J2ME { window.clearTimeout(this.lockTimeout); this.lockTimeout = null; } - if (obj.lock) { + if (obj.__lock__) { if (!obj.ready) obj.ready = []; obj.ready.push(this); @@ -259,10 +259,10 @@ module J2ME { } } - monitorEnter(obj) { - var lock = obj.lock; + monitorEnter(obj: java.lang.Object) { + var lock = obj.__lock__; if (!lock) { - obj.lock = {thread: this.thread, level: 1}; + obj.__lock__ = new Lock(this.thread, 1); return; } if (lock.thread === this.thread) { @@ -273,20 +273,20 @@ module J2ME { } monitorExit(obj) { - var lock = obj.lock; + var lock = obj.__lock__; if (lock.thread !== this.thread) this.raiseExceptionAndYield("java/lang/IllegalMonitorStateException"); if (--lock.level > 0) { return; } - obj.lock = null; + obj.__lock__ = null; this.unblock(obj, "ready", false, function (ctx) { ctx.wakeup(obj); }); } wait(obj, timeout) { - var lock = obj.lock; + var lock = obj.__lock__; if (timeout < 0) this.raiseExceptionAndYield("java/lang/IllegalArgumentException"); if (!lock || lock.thread !== this.thread) @@ -311,7 +311,7 @@ module J2ME { } notify(obj, notifyAll) { - if (!obj.lock || obj.lock.thread !== this.thread) + if (!obj.__lock__ || obj.__lock__.thread !== this.thread) this.raiseExceptionAndYield("java/lang/IllegalMonitorStateException"); this.unblock(obj, "waiting", notifyAll, function (ctx) { ctx.wakeup(obj); diff --git a/jit/jvm.ir.ts b/jit/jvm.ir.ts index fc9e5da3..7ea4047c 100644 --- a/jit/jvm.ir.ts +++ b/jit/jvm.ir.ts @@ -607,7 +607,7 @@ module J2ME.C4.Backend { var friendlyMangledNames = true; - export function mangleString(s: string) { + export function escapeString(s: string) { var invalidChars = "[];/<>()"; var replaceChars = "abc_defg"; var result = ""; @@ -625,7 +625,7 @@ module J2ME.C4.Backend { export function mangleClassAndMethod(methodInfo: MethodInfo) { var name = methodInfo.classInfo.className + methodInfo.name + methodInfo.signature; if (friendlyMangledNames) { - return mangleString(name); + return escapeString(name); } var hash = hashString(name); return StringUtilities.variableLengthEncodeInt32(hash); @@ -634,7 +634,7 @@ module J2ME.C4.Backend { export function mangleMethod(methodInfo: MethodInfo) { var name = methodInfo.name + methodInfo.signature; if (friendlyMangledNames) { - return mangleString(name); + return escapeString(name); } var hash = hashString(name); return StringUtilities.variableLengthEncodeInt32(hash); @@ -645,24 +645,15 @@ module J2ME.C4.Backend { return "$AK(" + mangleClass(classInfo.elementClass) + ")"; } else { if (friendlyMangledNames) { - return mangleString(classInfo.className); + return escapeString(classInfo.className); } var hash = hashString(classInfo.className); return StringUtilities.variableLengthEncodeInt32(hash); } } - export function mangleClassAndField(fieldInfo: FieldInfo) { - var name = fieldInfo.classInfo.className + fieldInfo.name; - if (friendlyMangledNames) { - return mangleString(name); - } - var hash = hashString(name); - return StringUtilities.variableLengthEncodeInt32(hash); - } - export function mangleField(fieldInfo: FieldInfo) { - return mangleString(fieldInfo.name); + return "$" + escapeString(fieldInfo.name); } function getRuntimeClass(classInfo: ClassInfo) { diff --git a/native.js b/native.js index 0e92bb96..bc9899e7 100644 --- a/native.js +++ b/native.js @@ -229,7 +229,7 @@ Native.create("java/lang/Object.notifyAll.()V", function(ctx) { }); Native.create("java/lang/Class.invoke_clinit.()V", function(ctx) { - var classInfo = this.vmClass; + var classInfo = this.runtimeKlass.classInfo; var className = classInfo.className; var runtime = ctx.runtime; if (runtime.initialized[className] || runtime.pending[className]) @@ -256,7 +256,7 @@ Native.create("java/lang/Class.invoke_clinit.()V", function(ctx) { }); Native.create("java/lang/Class.init9.()V", function(ctx) { - var classInfo = this.vmClass; + var classInfo = this.runtimeKlass.classInfo; var className = classInfo.className; var runtime = ctx.runtime; if (runtime.initialized[className]) @@ -266,7 +266,7 @@ Native.create("java/lang/Class.init9.()V", function(ctx) { }); Native.create("java/lang/Class.getName.()Ljava/lang/String;", function() { - return this.vmClass.className.replace(/\//g, "."); + return this.runtimeKlass.classInfo.className.replace(/\//g, "."); }); Native.create("java/lang/Class.forName.(Ljava/lang/String;)Ljava/lang/Class;", function(name, ctx) { @@ -285,7 +285,7 @@ Native.create("java/lang/Class.forName.(Ljava/lang/String;)Ljava/lang/Class;", f }); Native.create("java/lang/Class.newInstance.()Ljava/lang/Object;", function(ctx) { - var className = this.vmClass.className; + var className = this.runtimeKlass.classInfo.className; var syntheticMethod = new MethodInfo({ name: "ClassNewInstanceSynthetic", signature: "()Ljava/lang/Object;", @@ -315,21 +315,21 @@ Native.create("java/lang/Class.newInstance.()Ljava/lang/Object;", function(ctx) }); Native.create("java/lang/Class.isInterface.()Z", function() { - return ACCESS_FLAGS.isInterface(this.vmClass.access_flags); + return ACCESS_FLAGS.isInterface(this.runtimeKlass.classInfo.access_flags); }); Native.create("java/lang/Class.isArray.()Z", function() { - return !!this.vmClass.isArrayClass; + return !!this.runtimeKlass.classInfo.isArrayClass; }); Native.create("java/lang/Class.isAssignableFrom.(Ljava/lang/Class;)Z", function(fromClass) { if (!fromClass) throw new JavaException("java/lang/NullPointerException"); - return fromClass.vmClass.isAssignableTo(this.vmClass); + return fromClass.runtimeKlass.classInfo.isAssignableTo(this.runtimeKlass.classInfo); }); Native.create("java/lang/Class.isInstance.(Ljava/lang/Object;)Z", function(obj) { - return obj && obj.class.isAssignableTo(this.vmClass); + return obj && obj.class.isAssignableTo(this.runtimeKlass.classInfo); }); Native.create("java/lang/Float.floatToIntBits.(F)I", (function() { @@ -551,7 +551,7 @@ Native.create("com/sun/cldc/io/ResourceInputStream.open.(Ljava/lang/String;)Ljav }); Override.create("com/sun/cldc/io/ResourceInputStream.available.()I", function() { - var handle = this.class.getField("I.fileDecoder.Ljava/lang/Object;").get(this); + var handle = this.$fileDecoder; if (!handle) { throw new JavaException("java/io/IOException"); @@ -561,7 +561,7 @@ Override.create("com/sun/cldc/io/ResourceInputStream.available.()I", function() }); Override.create("com/sun/cldc/io/ResourceInputStream.read.()I", function() { - var handle = this.class.getField("I.fileDecoder.Ljava/lang/Object;").get(this); + var handle = this.$fileDecoder; if (!handle) { throw new JavaException("java/io/IOException"); @@ -674,8 +674,8 @@ Native.create("com/sun/midp/links/LinkPortal.getLinks0.([Lcom/sun/midp/links/Lin var isolateId = ctx.runtime.isolate.id; for (var i = 0; i < links[isolateId].length; i++) { - var nativePointer = links[isolateId][i].class.getField("I.nativePointer.I").get(links[isolateId][i]); - linkArray[i].class.getField("I.nativePointer.I").set(linkArray[i], nativePointer); + var nativePointer = links[isolateId][i].$nativePointer; + linkArray[i].$nativePointer = nativePointer; linkArray[i].sender = links[isolateId][i].sender; linkArray[i].receiver = links[isolateId][i].receiver; } @@ -692,7 +692,7 @@ Native.create("com/sun/midp/links/LinkPortal.setLinks0.(I[Lcom/sun/midp/links/Li Native.create("com/sun/midp/links/Link.init0.(II)V", function(sender, receiver) { this.sender = sender; this.receiver = receiver; - this.class.getField("I.nativePointer.I").set(this, util.id()); + this.$nativePointer = util.id(); }); Native.create("com/sun/midp/links/Link.receive0.(Lcom/sun/midp/links/LinkMessage;Lcom/sun/midp/links/Link;)V", function(linkMessage, link) { @@ -735,7 +735,7 @@ Native.create("java/io/DataInputStream.bytesToUTF.([B)Ljava/lang/String;", funct Native.create("com/sun/cldc/i18n/j2me/UTF_8_Writer.encodeUTF8.([CII)[B", function(cbuf, off, len) { var outputArray = []; - var pendingSurrogate = this.class.getField("I.pendingSurrogate.I").get(this); + var pendingSurrogate = this.$pendingSurrogate; var inputChar = 0; var outputSize = 0; @@ -792,7 +792,7 @@ Native.create("com/sun/cldc/i18n/j2me/UTF_8_Writer.encodeUTF8.([CII)[B", functio count++; } - this.class.getField("I.pendingSurrogate.I").set(this, pendingSurrogate); + this.$pendingSurrogate = pendingSurrogate; var totalSize = outputArray.reduce(function(total, cur) { return total + cur.length; @@ -812,7 +812,7 @@ Native.create("com/sun/cldc/i18n/j2me/UTF_8_Writer.sizeOf.([CII)I", function(cbu var outputSize = 0; var outputCount = 0; var count = 0; - var localPendingSurrogate = this.class.getField("I.pendingSurrogate.I").get(this); + var localPendingSurrogate = this.$pendingSurrogate; while (count < length) { inputChar = 0xffff & cbuf[offset + count]; if (0 != localPendingSurrogate) { diff --git a/runtime.ts b/runtime.ts index 3c7ea080..519d6c94 100644 --- a/runtime.ts +++ b/runtime.ts @@ -151,7 +151,7 @@ module J2ME { new (): java.lang.Object; /** - * Array klass of this klass constructed via \arrayKlass\. + * Array klass of this klass, constructed via \arrayKlass\. */ arrayKlass: ArrayKlass; @@ -186,12 +186,22 @@ module J2ME { /** * Java class object. This is only available on runtime klasses. */ - class: java.lang.Class + classObject: java.lang.Class; + + /** + * Wether this class is a runtime class. + */ + isRuntimeKlass: boolean; } export interface ArrayKlass extends Klass { elementKlass: Klass; + } + export class Lock { + constructor(public thread: java.lang.Thread, public level: number) { + // ... + } } export module java.lang { @@ -206,6 +216,11 @@ module J2ME { */ __hashCode__: number; + /** + * Some objects may have a lock. + */ + __lock__: Lock; + clone(): java.lang.Object; equals(obj: java.lang.Object): boolean; finalize(): void; @@ -220,16 +235,27 @@ module J2ME { } export interface Class extends java.lang.Object { - + runtimeKlass: Klass; } export interface String extends java.lang.Object { } + + export interface Thread extends java.lang.Object { + + } } declare var CLASSES; + function initializeClassObject(klass: Klass) { + assert(klass.isRuntimeKlass, "Can only create class objects for runtime klasses."); + assert(!klass.classObject); + klass.classObject = newObject(Klasses.java.lang.Class); + klass.classObject.runtimeKlass = klass; + } + /** * Called by compiled code to initialize the klass. Klass initializers are reflected as * memoizing getters on the |RuntimeTemplate.prototype|. Once they are first accessed, @@ -241,9 +267,11 @@ module J2ME { Object.defineProperty(RuntimeTemplate.prototype, mangledClassName, { configurable: true, get: function () { + assert(!klass.isRuntimeKlass); var runtimeKlass = klass.bind(null); runtimeKlass.klass = klass; - runtimeKlass.class = new Class(runtimeKlass); + runtimeKlass.isRuntimeKlass = true; + initializeClassObject(runtimeKlass); var classInfo = CLASSES.getClass(className); Object.defineProperty(this, mangledClassName, { configurable: false, @@ -261,6 +289,13 @@ module J2ME { }); } + export function runtimeKlass(runtime: Runtime, klass: Klass): Klass { + assert(!klass.isRuntimeKlass); + var runtimeKlass = runtime[klass.classInfo.mangledName]; + assert(runtimeKlass.isRuntimeKlass); + return runtimeKlass; + } + export function createKlass(classInfo: ClassInfo): Klass { if (!classInfo) { return null; @@ -390,11 +425,20 @@ module J2ME { return arrayKlass; } - export function toDebugString(object: java.lang.Object): string { - if (!object) { + export function toDebugString(value: any): string { + if (typeof value !== "object") { + return String(value); + } + if (!value) { return "null"; } - return "[" + object.klass.classInfo.className + " 0x" + object.__hashCode__.toString(16).toUpperCase() + "]"; + if (!value.klass) { + return "no klass"; + } + if (!value.klass.classInfo) { + return value.klass + " no classInfo" + } + return "[" + value.klass.classInfo.className + " 0x" + value.__hashCode__.toString(16).toUpperCase() + "]"; } } diff --git a/vm.js b/vm.js index df8f9d80..3e2d51a3 100644 --- a/vm.js +++ b/vm.js @@ -18,6 +18,8 @@ VM.trace = function(type, pid, methodInfo, returnVal) { (returnVal ? (" " + returnVal) : "") + "\n"; } +var traceWriter = new J2ME.IndentingWriter(); + VM.execute = function(ctx) { var frame = ctx.current(); @@ -27,6 +29,13 @@ VM.execute = function(ctx) { function pushFrame(methodInfo) { var caller = frame; + if (traceWriter) { + var args = stack.slice(stack.length - methodInfo.consumes).map(function (x) { + return J2ME.toDebugString(x); + }).join(", ") + traceWriter.enter(methodInfo.implKey + " " + args); + } + frame = ctx.pushFrame(methodInfo); stack = frame.stack; cp = frame.cp; @@ -43,6 +52,7 @@ VM.execute = function(ctx) { } function popFrame(consumes) { + traceWriter && traceWriter.outdent(); if (frame.lockObject) ctx.monitorExit(frame.lockObject); var callee = frame;