declare var ASM; var buffer = ASM.buffer; var i32: Int32Array = ASM.HEAP32; var u32: Uint32Array = ASM.HEAPU32; var f32: Float32Array = ASM.HEAPF32; var ref = J2ME.ArrayUtilities.makeDenseArray(buffer.byteLength >> 2, null); var aliasedI32 = J2ME.IntegerUtilities.i32; var aliasedF32 = J2ME.IntegerUtilities.f32; var aliasedF64 = J2ME.IntegerUtilities.f64; module J2ME { import assert = Debug.assert; import Bytecodes = Bytecode.Bytecodes; import toHEX = IntegerUtilities.toHEX; function toName(o) { if (o instanceof MethodInfo) { return "MI: " + o.implKey; } function getArrayInfo(o) { var s = []; var x = []; for (var i = 0; i < Math.min(o.length, 8); i++) { s.push(o[i]); x.push(String.fromCharCode(o[i])); } var suffix = (o.length > 8 ? "..." : ""); return fromUTF8(o.klass.classInfo.utf8Name) + ", length: " + o.length + ", values: [" + s.join(", ") + suffix + "]" + ", chars: \"" + x.join("") + suffix + "\""; } function getObjectInfo(o) { if (o.length !== undefined) { return getArrayInfo(o); } return fromUTF8(o.klass.classInfo.utf8Name) + (o._address ? " " + toHEX(o._address) : ""); } if (o && !o.klass) { return o; } if (o && o.klass === Klasses.java.lang.Class) { return "[" + getObjectInfo(o) + "] " + o.runtimeKlass.templateKlass.classInfo.getClassNameSlow(); } if (o && o.klass === Klasses.java.lang.String) { return "[" + getObjectInfo(o) + "] \"" + fromJavaString(o) + "\""; } return o ? ("[" + getObjectInfo(o) + "]") : "null"; } /** * The number of opcodes executed thus far. */ export var bytecodeCount = 0; /** * The number of times the interpreter method was called thus far. */ export var interpreterCount = 0; export var onStackReplacementCount = 0; /** * The closest floating-point representation to this long value. */ function longToNumber(l: number, h: number): number { return h * Constants.TWO_PWR_32_DBL + ((l >= 0) ? l : Constants.TWO_PWR_32_DBL + l); } function wordsToDouble(l: number, h: number): number { aliasedI32[0] = l; aliasedI32[1] = h; return aliasedF64[0]; } /** * Calling Convention: * * Interpreter -> Interpreter: * This follows the JVM bytecode calling convention. This interpreter is highly * optimized for this calling convention. * * Compiled / Native -> Compiled / Native: * 64-bit floats and single word values can be encoded using only one JS value. However, 64-bit longs cannot and * require a (low, high) JS value pair. For example, the signature: "foo.(IDJi)J" is expressed as: * * function foo(i, d, lowBits, highBits, i) { * return tempReturn0 = highBits, lowBits; * } * * Returning longs is equally problamatic, the convention is to return the lowBits, and save the highBits in a * global |tempReturn0| variable. */ /* * Stack Frame Layout: * * LP ---> +--------------------------------+ * | Parameter 0 | * +--------------------------------+ * | ... | * +--------------------------------+ * | Parameter (P-1) | * +--------------------------------+ * | Non-Parameter Local 0 | * +--------------------------------+ * | ... | * +--------------------------------+ * | Non-Parameter Local (L-1) | * FP ---> +--------------------------------+ * | Caller Return Address | * +--------------------------------+ * | Caller FP | * +--------------------------------+ * | Callee Method Info | * +--------------------------------+ * | Stack slot 0 | * +--------------------------------+ * | ... | * +--------------------------------+ * | Stack slot (S-1) | * SP ---> +--------------------------------+ */ enum FrameLayout { CalleeMethodInfoOffset = 2, CallerFPOffset = 1, CallerRAOffset = 0, CallerSaveSize = 3 } export class FrameView { public fp: number; public sp: number; public pc: number; constructor() { } set(fp: number, sp: number, pc: number) { this.fp = fp; this.sp = sp; this.pc = pc; if (!release) { var callee = ref[this.fp + FrameLayout.CalleeMethodInfoOffset]; assert( callee === null || callee instanceof MethodInfo, "Callee @" + (this.fp + FrameLayout.CalleeMethodInfoOffset) + " is not a MethodInfo, " + toName(callee) ); } } setParameter(kind: Kind, i: number, v: any) { switch (kind) { case Kind.Reference: ref[this.fp + this.parameterOffset + i] = v; break; case Kind.Int: i32[this.fp + this.parameterOffset + i] = v; break; default: release || assert(false, "Cannot set parameter of kind: " + Kind[kind]); } } get methodInfo(): MethodInfo { return ref[this.fp + FrameLayout.CalleeMethodInfoOffset]; } set methodInfo(methodInfo: MethodInfo) { ref[this.fp + FrameLayout.CalleeMethodInfoOffset] = methodInfo; } get parameterOffset() { return this.methodInfo ? -this.methodInfo.codeAttribute.max_locals : 0; } get stackOffset(): number { return FrameLayout.CallerSaveSize; } traceStack(writer: IndentingWriter) { var fp = this.fp; var sp = this.sp; var pc = this.pc; while (this.fp > FrameLayout.CallerSaveSize) { writer.writeLn((this.methodInfo ? this.methodInfo.implKey : "null") + ", FP: " + this.fp + ", SP: " + this.sp + ", PC: " + this.pc); this.set(i32[this.fp + FrameLayout.CallerFPOffset], this.fp + this.parameterOffset, i32[this.fp + FrameLayout.CallerRAOffset]); } this.fp = fp; this.sp = sp; this.pc = pc; } trace(writer: IndentingWriter) { function toNumber(v) { if (v < 0) { return String(v); } else if (v === 0) { return " 0"; } else { return "+" + v; } } function clampString(v, n) { if (v.length > n) { return v.substring(0, n - 3) + "..."; } return v; } writer.writeLn("Frame: " + this.methodInfo.implKey + ", FP: " + this.fp + ", SP: " + this.sp + ", PC: " + this.pc); for (var i = Math.max(0, this.fp + this.parameterOffset); i < this.sp; i++) { var prefix = " "; if (i >= this.fp + this.stackOffset) { prefix = "S" + (i - (this.fp + this.stackOffset)) + ": "; } else if (i === this.fp + FrameLayout.CalleeMethodInfoOffset) { prefix = "MI: "; } else if (i === this.fp + FrameLayout.CallerFPOffset) { prefix = "CF: "; } else if (i === this.fp + FrameLayout.CallerRAOffset) { prefix = "RA: "; } else if (i >= this.fp + this.parameterOffset) { prefix = "L" + (i - (this.fp + this.parameterOffset)) + ": "; } writer.writeLn(" " + prefix.padRight(' ', 5) + " " + toNumber(i - this.fp).padLeft(' ', 3) + " " + String(i).padLeft(' ', 4) + " " + toHEX(i << 2) + ": " + String(i32[i]).padLeft(' ', 12) + " " + toHEX(i32[i]) + " " + ((i32[i] >= 32 && i32[i] < 1024) ? String.fromCharCode(i32[i]) : "?") + " " + clampString(String(f32[i]), 12).padLeft(' ', 12) + " " + clampString(String(wordsToDouble(i32[i], i32[i + 1])), 12).padLeft(' ', 12) + " " + toName(ref[i])); } } } export var interpreterCounter = new Metrics.Counter(true); export class Thread { /** * Thread base pointer. */ tp: number; /** * Stack base pointer. */ bp: number /** * Current frame pointer. */ fp: number /** * Current stack pointer. */ sp: number /** * Current program counter. */ pc: number /** * Context associated with this thread. */ ctx: Context; view: FrameView; constructor(ctx: Context) { this.tp = ASM._gcMalloc(1024 * 128); this.bp = this.tp; this.fp = this.bp; this.sp = this.fp; this.pc = -1; this.view = new FrameView(); this.ctx = ctx; } set(fp: number, sp: number, pc: number) { this.fp = fp; this.sp = sp; this.pc = pc; } get frame(): FrameView { this.view.set(this.fp, this.sp, this.pc); return this.view; } pushFrame(methodInfo: MethodInfo) { var fp = this.fp; if (methodInfo) { this.fp = this.sp + methodInfo.codeAttribute.max_locals; } else { this.fp = this.sp; } this.sp = this.fp; i32[this.sp++] = this.pc; // Caller RA i32[this.sp++] = fp; // Caller FP ref[this.sp++] = methodInfo; // Callee this.pc = 0; } popFrame(methodInfo: MethodInfo): MethodInfo { var mi = ref[this.fp + FrameLayout.CalleeMethodInfoOffset]; release || assert(mi === methodInfo); this.pc = i32[this.fp + FrameLayout.CallerRAOffset]; var maxLocals = mi ? mi.codeAttribute.max_locals : 0; this.sp = this.fp - maxLocals; this.fp = i32[this.fp + FrameLayout.CallerFPOffset]; return ref[this.fp + FrameLayout.CalleeMethodInfoOffset] } run() { return interpret(this); } exceptionUnwind(e: java.lang.Exception) { release || traceWriter && traceWriter.writeLn("exceptionUnwind: " + toName(e)); var pc = -1; var classInfo; var mi = ref[this.fp + FrameLayout.CalleeMethodInfoOffset]; while (mi) { release || traceWriter && traceWriter.writeLn("Looking for handler in: " + mi.implKey); for (var i = 0; i < mi.exception_table_length; i++) { var exceptionEntryView = mi.getExceptionEntryViewByIndex(i); if (this.pc >= exceptionEntryView.start_pc && this.pc < exceptionEntryView.end_pc) { if (exceptionEntryView.catch_type === 0) { pc = exceptionEntryView.handler_pc; break; } else { classInfo = resolveClass(exceptionEntryView.catch_type, mi.classInfo); if (isAssignableTo(e.klass, classInfo.klass)) { pc = exceptionEntryView.handler_pc; break; } } } } if (pc >= 0) { this.pc = pc; this.sp = this.fp + FrameLayout.CallerSaveSize; ref[this.sp++] = e; return; } mi = this.popFrame(mi); release || traceWriter && traceWriter.outdent(); release || traceWriter && traceWriter.writeLn("<< I Unwind"); } release || traceWriter && traceWriter.writeLn("Cannot catch: " + toName(e)); throw e; } } export function prepareInterpretedMethod(methodInfo: MethodInfo): Function { var method = function fastInterpreterFrameAdapter() { var thread = $.ctx.nativeThread; var fp = thread.fp; // release || traceWriter && traceWriter.writeLn(">> I"); thread.pushFrame(null); thread.pushFrame(methodInfo); var frame = thread.frame; var kinds = methodInfo.signatureKinds; var index = 0; if (!methodInfo.isStatic) { frame.setParameter(Kind.Reference, index++, this); } for (var i = 1; i < kinds.length; i++) { frame.setParameter(kinds[i], index++, arguments[i - 1]); } var v = interpret(thread); release || assert(fp === thread.fp); // release || traceWriter && traceWriter.writeLn("<< I"); return v; }; (method).methodInfo = methodInfo; return method; } function resolveClass(index: number, classInfo: ClassInfo): ClassInfo { var classInfo = classInfo.constantPool.resolveClass(index); linkKlass(classInfo); return classInfo; } var args = new Array(16); enum ExceptionType { ArithmeticException, ArrayIndexOutOfBoundsException } /** * Main interpreter loop. This method is carefully written to avoid memory allocation and * function calls on fast paths. Thus, everything is inlined, even if it makes the code * ugly. */ export function interpret(thread: Thread) { var frame = thread.frame; var mi = frame.methodInfo; var maxLocals = mi.codeAttribute.max_locals; var ci = mi.classInfo; var cp = ci.constantPool; var code = mi ? mi.codeAttribute.code : null; var fp = thread.fp; var lp = fp - maxLocals; var sp = thread.sp; var opPC = 0, pc = thread.pc; var tag: TAGS; var type, size; var value, index, array, object, result, constant, offset, buffer, tag: TAGS, targetPC, returnValue, kind; var address = 0, isStatic = false; var ia = 0, ib = 0; // Integer Operands var ll = 0, lh = 0; // Long Low / High var fa = 0, fb = 0; // Float / Double Operands var classInfo: ClassInfo; var fieldInfo: FieldInfo; function loadThreadState() { fp = thread.fp; lp = fp - maxLocals; sp = thread.sp; pc = thread.pc; } function classInitAndUnwindCheck(classInfo: ClassInfo, unusedPC: number) { thread.set(fp, sp, pc); classInitCheck(classInfo); loadThreadState(); //if (U) { // $.ctx.current().pc = pc; // return; //} } /** * Thrown exceptions need to be constructed, and can thus damage the stack if we don't * save the thread state. This is a helper function that does just that. */ function throwException(type: ExceptionType, a?) { thread.set(fp, sp, opPC); switch (type) { case ExceptionType.ArrayIndexOutOfBoundsException: throwArrayIndexOutOfBoundsException(a); break; case ExceptionType.ArithmeticException: throwArithmeticException(); break; } } // HEAD while (true) { opPC = pc, op = code[pc++]; if (!release) { assert(code === mi.codeAttribute.code, "Bad Code."); assert(ci === mi.classInfo, "Bad Class Info."); assert(cp === ci.constantPool, "Bad Constant Pool."); assert(lp === fp - mi.codeAttribute.max_locals, "Bad lp."); bytecodeCount++; if (traceStackWriter) { frame.set(fp, sp, opPC); frame.trace(traceStackWriter); traceStackWriter.writeLn(); traceStackWriter.greenLn(mi.implKey + ": PC: " + opPC + ", FP: " + fp + ", " + Bytecodes[op]); } } try { switch (op) { case Bytecodes.NOP: continue; case Bytecodes.ACONST_NULL: ref[sp++] = null; continue; case Bytecodes.ICONST_M1: case Bytecodes.ICONST_0: case Bytecodes.ICONST_1: case Bytecodes.ICONST_2: case Bytecodes.ICONST_3: case Bytecodes.ICONST_4: case Bytecodes.ICONST_5: i32[sp++] = op - Bytecodes.ICONST_0; continue; case Bytecodes.FCONST_0: case Bytecodes.FCONST_1: case Bytecodes.FCONST_2: f32[sp++] = op - Bytecodes.FCONST_0; continue; case Bytecodes.DCONST_0: i32[sp++] = 0; i32[sp++] = 0; continue; case Bytecodes.DCONST_1: i32[sp++] = 0; i32[sp++] = 1072693248; continue; case Bytecodes.LCONST_0: case Bytecodes.LCONST_1: i32[sp++] = op - Bytecodes.LCONST_0; i32[sp++] = 0; continue; case Bytecodes.BIPUSH: i32[sp++] = code[pc++] << 24 >> 24; continue; case Bytecodes.SIPUSH: i32[sp++] = (code[pc++] << 8 | code[pc++]) << 16 >> 16; continue; case Bytecodes.LDC: case Bytecodes.LDC_W: index = (op === Bytecodes.LDC) ? code[pc++] : code[pc++] << 8 | code[pc++]; offset = cp.entries[index]; buffer = cp.buffer; tag = buffer[offset++]; if (tag === TAGS.CONSTANT_Integer || tag === TAGS.CONSTANT_Float) { i32[sp++] = buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]; } else if (tag === TAGS.CONSTANT_String) { ref[sp++] = constant = ci.constantPool.resolve(index, tag, false);; } else { release || assert(false, TAGS[tag]); } continue; case Bytecodes.LDC2_W: index = code[pc++] << 8 | code[pc++]; offset = cp.entries[index]; buffer = cp.buffer; tag = buffer[offset++]; if (tag === TAGS.CONSTANT_Long || tag === TAGS.CONSTANT_Double) { i32[sp + 1] = buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]; i32[sp ] = buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]; sp += 2; } else { release || assert(false, TAGS[tag]); } continue; case Bytecodes.ILOAD: case Bytecodes.FLOAD: i32[sp++] = i32[lp + code[pc++]]; continue; case Bytecodes.ALOAD: ref[sp++] = ref[lp + code[pc++]]; continue; case Bytecodes.LLOAD: case Bytecodes.DLOAD: offset = lp + code[pc++]; i32[sp++] = i32[offset]; i32[sp++] = i32[offset + 1]; continue; case Bytecodes.ILOAD_0: case Bytecodes.ILOAD_1: case Bytecodes.ILOAD_2: case Bytecodes.ILOAD_3: i32[sp++] = i32[lp + op - Bytecodes.ILOAD_0]; continue; case Bytecodes.FLOAD_0: case Bytecodes.FLOAD_1: case Bytecodes.FLOAD_2: case Bytecodes.FLOAD_3: i32[sp++] = i32[lp + op - Bytecodes.FLOAD_0]; continue; case Bytecodes.ALOAD_0: ref[sp++] = ref[lp]; continue; case Bytecodes.ALOAD_1: case Bytecodes.ALOAD_2: case Bytecodes.ALOAD_3: ref[sp++] = ref[lp + op - Bytecodes.ALOAD_0]; continue; case Bytecodes.LLOAD_0: case Bytecodes.LLOAD_1: case Bytecodes.LLOAD_2: case Bytecodes.LLOAD_3: offset = lp + op - Bytecodes.LLOAD_0; i32[sp++] = i32[offset]; i32[sp++] = i32[offset + 1]; continue; case Bytecodes.DLOAD_0: case Bytecodes.DLOAD_1: case Bytecodes.DLOAD_2: case Bytecodes.DLOAD_3: offset = lp + op - Bytecodes.DLOAD_0; i32[sp++] = i32[offset]; i32[sp++] = i32[offset + 1]; continue; case Bytecodes.IALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } i32[sp++] = array[index]; continue; case Bytecodes.BALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } i32[sp++] = array[index]; continue; case Bytecodes.CALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } i32[sp++] = array[index]; continue; case Bytecodes.SALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } i32[sp++] = array[index]; continue; case Bytecodes.FALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } f32[sp++] = array[index]; continue; case Bytecodes.AALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } ref[sp++] = array[index]; continue; case Bytecodes.DALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } aliasedF64[0] = array[index]; i32[sp++] = aliasedI32[0]; i32[sp++] = aliasedI32[1]; continue; case Bytecodes.ISTORE: case Bytecodes.FSTORE: i32[lp + code[pc++]] = i32[--sp]; continue; case Bytecodes.ASTORE: ref[lp + code[pc++]] = ref[--sp]; continue; case Bytecodes.LSTORE: case Bytecodes.DSTORE: offset = lp + code[pc++]; i32[offset + 1] = i32[--sp]; i32[offset] = i32[--sp]; continue; case Bytecodes.ISTORE_0: case Bytecodes.ISTORE_1: case Bytecodes.ISTORE_2: case Bytecodes.ISTORE_3: i32[lp + op - Bytecodes.ISTORE_0] = i32[--sp]; continue; case Bytecodes.FSTORE_0: case Bytecodes.FSTORE_1: case Bytecodes.FSTORE_2: case Bytecodes.FSTORE_3: i32[lp + op - Bytecodes.FSTORE_0] = i32[--sp]; continue; case Bytecodes.ASTORE_0: case Bytecodes.ASTORE_1: case Bytecodes.ASTORE_2: case Bytecodes.ASTORE_3: ref[lp + op - Bytecodes.ASTORE_0] = ref[--sp]; continue; case Bytecodes.LSTORE_0: case Bytecodes.LSTORE_1: case Bytecodes.LSTORE_2: case Bytecodes.LSTORE_3: offset = lp + op - Bytecodes.LSTORE_0; i32[offset + 1] = i32[--sp]; i32[offset] = i32[--sp]; continue; case Bytecodes.DSTORE_0: case Bytecodes.DSTORE_1: case Bytecodes.DSTORE_2: case Bytecodes.DSTORE_3: offset = lp + op - Bytecodes.DSTORE_0; i32[offset + 1] = i32[--sp]; i32[offset] = i32[--sp]; continue; case Bytecodes.IASTORE: value = i32[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array[index] = value; continue; case Bytecodes.FASTORE: value = f32[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array[index] = value; continue; case Bytecodes.BASTORE: value = i32[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array[index] = value; continue; case Bytecodes.CASTORE: value = i32[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array[index] = value; continue; case Bytecodes.SASTORE: value = i32[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array[index] = value; continue; case Bytecodes.LASTORE: lh = i32[--sp]; ll = i32[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array.value[index * 2 ] = ll; array.value[index * 2 + 1] = lh; continue; case Bytecodes.LALOAD: index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } i32[sp++] = array.value[index * 2 ]; i32[sp++] = array.value[index * 2 + 1]; continue; case Bytecodes.DASTORE: aliasedI32[1] = i32[--sp]; aliasedI32[0] = i32[--sp]; value = aliasedF64[0]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } array[index] = value; continue; case Bytecodes.AASTORE: value = ref[--sp]; index = i32[--sp]; array = ref[--sp]; if ((index >>> 0) >= (array.length >>> 0)) { throwException(ExceptionType.ArrayIndexOutOfBoundsException, index); } checkArrayStore(array, value); array[index] = value; continue; case Bytecodes.POP: --sp; continue; case Bytecodes.POP2: sp -= 2; continue; case Bytecodes.DUP: // ... a -> ... a, a i32[sp] = i32[sp - 1]; ref[sp] = ref[sp - 1]; sp++; continue; case Bytecodes.DUP2: // ... b, a -> ... b, a, b, a i32[sp ] = i32[sp - 2]; ref[sp ] = ref[sp - 2]; // b i32[sp + 1] = i32[sp - 1]; ref[sp + 1] = ref[sp - 1]; // a sp += 2; continue; case Bytecodes.DUP_X1: // ... b, a -> ... a, b, a i32[sp ] = i32[sp - 1]; ref[sp ] = ref[sp - 1]; // a i32[sp - 1] = i32[sp - 2]; ref[sp - 1] = ref[sp - 2]; // b i32[sp - 2] = i32[sp]; ref[sp - 2] = ref[sp]; // a sp++; continue; case Bytecodes.DUP_X2: // ... c, b, a -> ... a, c, b, a i32[sp ] = i32[sp - 1]; ref[sp ] = ref[sp - 1]; // a i32[sp - 1] = i32[sp - 2]; ref[sp - 1] = ref[sp - 2]; // b i32[sp - 2] = i32[sp - 3]; ref[sp - 2] = ref[sp - 3]; // c i32[sp - 3] = i32[sp]; ref[sp - 3] = ref[sp]; // a sp++; continue; case Bytecodes.DUP2_X1: // ... c, b, a -> ... b, a, c, b, a i32[sp + 1] = i32[sp - 1]; ref[sp + 1] = ref[sp - 1]; // a i32[sp ] = i32[sp - 2]; ref[sp ] = ref[sp - 2]; // b i32[sp - 1] = i32[sp - 3]; ref[sp - 1] = ref[sp - 3]; // c i32[sp - 2] = i32[sp + 1]; ref[sp - 2] = ref[sp + 1]; // a i32[sp - 3] = i32[sp ]; ref[sp - 3] = ref[sp ]; // b sp += 2; continue; case Bytecodes.DUP2: // ... b, a -> ... b, a, b, a i32[sp + 1] = i32[sp - 1]; ref[sp + 1] = ref[sp - 1]; // a i32[sp ] = i32[sp - 2]; ref[sp ] = ref[sp - 2]; // b sp += 2; continue; case Bytecodes.DUP2_X2: // ... d, c, b, a -> ... b, a, d, c, b, a i32[sp + 1] = i32[sp - 1]; ref[sp + 1] = ref[sp - 1]; // a i32[sp ] = i32[sp - 2]; ref[sp ] = ref[sp - 2]; // b i32[sp - 1] = i32[sp - 3]; ref[sp - 1] = ref[sp - 3]; // c i32[sp - 2] = i32[sp - 4]; ref[sp - 2] = ref[sp - 4]; // d i32[sp - 3] = i32[sp + 1]; ref[sp - 3] = ref[sp + 1]; // a i32[sp - 4] = i32[sp ]; ref[sp - 4] = ref[sp ]; // b sp += 2; continue; case Bytecodes.SWAP: ia = i32[sp - 1]; object = ref[sp - 1]; i32[sp - 1] = i32[sp - 2]; ref[sp - 1] = ref[sp - 2]; i32[sp - 2] = ia; ref[sp - 2] = object; continue; case Bytecodes.IINC: index = code[pc++]; value = code[pc++] << 24 >> 24; i32[lp + index] = i32[lp + index] + value | 0; continue; // case Bytecodes.IINC_GOTO: // index = frame.read8(); // value = frame.read8Signed(); // frame.local[index] += frame.local[index]; // frame.pc ++; // frame.pc = frame.readTargetPC(); // break; case Bytecodes.IADD: i32[sp - 2] = (i32[sp - 2] + i32[sp - 1]) | 0; sp--; continue; case Bytecodes.LADD: ASM._lAdd(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2; continue; case Bytecodes.FADD: f32[sp - 2] = f32[sp - 2] + f32[sp - 1]; sp--; continue; case Bytecodes.DADD: aliasedI32[0] = i32[sp - 4]; aliasedI32[1] = i32[sp - 3]; ia = aliasedF64[0]; aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; ib = aliasedF64[0]; aliasedF64[0] = ia + ib; i32[sp - 4] = aliasedI32[0]; i32[sp - 3] = aliasedI32[1]; sp -= 2; continue; case Bytecodes.ISUB: i32[sp - 2] = (i32[sp - 2] - i32[sp - 1]) | 0; sp--; continue; case Bytecodes.LSUB: ASM._lSub(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2; continue; case Bytecodes.FSUB: f32[sp - 2] = f32[sp - 2] - f32[sp - 1]; sp--; continue; case Bytecodes.DSUB: aliasedI32[0] = i32[sp - 4]; aliasedI32[1] = i32[sp - 3]; ia = aliasedF64[0]; aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; ib = aliasedF64[0]; aliasedF64[0] = ia - ib; i32[sp - 4] = aliasedI32[0]; i32[sp - 3] = aliasedI32[1]; sp -= 2; continue; case Bytecodes.IMUL: i32[sp - 2] = Math.imul(i32[sp - 2], i32[sp - 1]) | 0; sp--; continue; case Bytecodes.LMUL: ASM._lMul(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2; continue; case Bytecodes.FMUL: f32[sp - 2] = f32[sp - 2] * f32[sp - 1]; sp--; continue; case Bytecodes.DMUL: aliasedI32[0] = i32[sp - 4]; aliasedI32[1] = i32[sp - 3]; ia = aliasedF64[0]; aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; ib = aliasedF64[0]; aliasedF64[0] = ia * ib; i32[sp - 4] = aliasedI32[0]; i32[sp - 3] = aliasedI32[1]; sp -= 2; continue; case Bytecodes.IDIV: if (i32[sp - 1] === 0) { throwException(ExceptionType.ArithmeticException); } ia = i32[sp - 2]; ib = i32[sp - 1]; i32[sp - 2] = (ia === Constants.INT_MIN && ib === -1) ? ia : ((ia / ib) | 0); sp--; continue; case Bytecodes.LDIV: if (i32[sp - 2] === 0 && i32[sp - 1] === 0) { throwException(ExceptionType.ArithmeticException); } ASM._lDiv(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2; continue; case Bytecodes.FDIV: fb = f32[--sp]; fa = f32[--sp]; f32[sp++] = Math.fround(fa / fb); continue; case Bytecodes.DDIV: aliasedI32[0] = i32[sp - 4]; aliasedI32[1] = i32[sp - 3]; ia = aliasedF64[0]; aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; ib = aliasedF64[0]; aliasedF64[0] = ia / ib; i32[sp - 4] = aliasedI32[0]; i32[sp - 3] = aliasedI32[1]; sp -= 2; continue; case Bytecodes.IREM: if (i32[sp - 1] === 0) { throwException(ExceptionType.ArithmeticException); } i32[sp - 2] = (i32[sp - 2] % i32[sp - 1]) | 0; sp--; continue; case Bytecodes.LREM: if (i32[sp - 2] === 0 && i32[sp - 1] === 0) { throwException(ExceptionType.ArithmeticException); } ASM._lRem(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2; continue; case Bytecodes.FREM: fb = f32[--sp]; fa = f32[--sp]; f32[sp++] = Math.fround(fa % fb); continue; case Bytecodes.DREM: aliasedI32[0] = i32[sp - 4]; aliasedI32[1] = i32[sp - 3]; ia = aliasedF64[0]; aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; ib = aliasedF64[0]; aliasedF64[0] = ia % ib; i32[sp - 4] = aliasedI32[0]; i32[sp - 3] = aliasedI32[1]; sp -= 2; continue; case Bytecodes.INEG: i32[sp - 1] = -i32[sp - 1] | 0; continue; case Bytecodes.LNEG: ASM._lNeg(sp - 2 << 2, sp - 2 << 2); continue; case Bytecodes.FNEG: f32[sp - 1] = -f32[sp - 1]; continue; case Bytecodes.DNEG: aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; aliasedF64[0] = -aliasedF64[0]; i32[sp - 2] = aliasedI32[0]; i32[sp - 1] = aliasedI32[1]; continue; case Bytecodes.ISHL: ib = i32[--sp]; ia = i32[--sp]; i32[sp++] = ia << ib; continue; case Bytecodes.LSHL: ASM._lShl(sp - 3 << 2, sp - 3 << 2, i32[sp - 1]); sp -= 1; continue; case Bytecodes.ISHR: ib = i32[--sp]; ia = i32[--sp]; i32[sp++] = ia >> ib; continue; case Bytecodes.LSHR: ASM._lShr(sp - 3 << 2, sp - 3 << 2, i32[sp - 1]); sp -= 1; continue; case Bytecodes.IUSHR: ib = i32[--sp]; ia = i32[--sp]; i32[sp++] = ia >>> ib; continue; case Bytecodes.LUSHR: ASM._lUshr(sp - 3 << 2, sp - 3 << 2, i32[sp - 1]); sp -= 1; continue; case Bytecodes.IAND: i32[sp - 2] &= i32[--sp]; continue; case Bytecodes.LAND: i32[sp - 4] &= i32[sp - 2]; i32[sp - 3] &= i32[sp - 1]; sp -= 2; break; case Bytecodes.IOR: i32[sp - 2] |= i32[--sp]; continue; case Bytecodes.LOR: i32[sp - 4] |= i32[sp - 2]; i32[sp - 3] |= i32[sp - 1]; sp -= 2; continue; case Bytecodes.IXOR: i32[sp - 2] ^= i32[--sp]; continue; case Bytecodes.LXOR: i32[sp - 4] ^= i32[sp - 2]; i32[sp - 3] ^= i32[sp - 1]; sp -= 2; continue; case Bytecodes.LCMP: ASM._lCmp(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 3; continue; case Bytecodes.FCMPL: case Bytecodes.FCMPG: fb = f32[--sp]; fa = f32[--sp]; if (isNaN(fa) || isNaN(fb)) { i32[sp++] = op === Bytecodes.FCMPL ? -1 : 1; } else if (fa > fb) { i32[sp++] = 1; } else if (fa < fb) { i32[sp++] = -1; } else { i32[sp++] = 0; } continue; case Bytecodes.DCMPL: case Bytecodes.DCMPG: aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; fb = aliasedF64[0]; aliasedI32[0] = i32[sp - 4]; aliasedI32[1] = i32[sp - 3]; fa = aliasedF64[0]; sp -= 4; if (isNaN(fa) || isNaN(fb)) { i32[sp++] = op === Bytecodes.DCMPL ? -1 : 1; } else if (fa > fb) { i32[sp++] = 1; } else if (fa < fb) { i32[sp++] = -1; } else { i32[sp++] = 0; } continue; case Bytecodes.IFEQ: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] === 0) { pc = targetPC; } continue; case Bytecodes.IFNE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] !== 0) { pc = targetPC; } continue; case Bytecodes.IFLT: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] < 0) { pc = targetPC; } continue; case Bytecodes.IFGE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] >= 0) { pc = targetPC; } continue; case Bytecodes.IFGT: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] > 0) { pc = targetPC; } continue; case Bytecodes.IFLE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] <= 0) { pc = targetPC; } continue; case Bytecodes.IF_ICMPEQ: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] === i32[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ICMPNE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] !== i32[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ICMPLT: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] > i32[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ICMPGE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] <= i32[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ICMPGT: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] < i32[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ICMPLE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (i32[--sp] >= i32[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ACMPEQ: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (ref[--sp] === ref[--sp]) { pc = targetPC; } continue; case Bytecodes.IF_ACMPNE: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (ref[--sp] !== ref[--sp]) { pc = targetPC; } continue; case Bytecodes.IFNULL: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (!ref[--sp]) { pc = targetPC; } continue; case Bytecodes.IFNONNULL: targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); if (ref[--sp]) { pc = targetPC; } continue; case Bytecodes.GOTO: pc = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16); continue; // case Bytecodes.GOTO_W: // frame.pc = frame.read32Signed() - 1; // break; // case Bytecodes.JSR: // pc = frame.read16(); // stack.push(frame.pc); // frame.pc = pc; // break; // case Bytecodes.JSR_W: // pc = frame.read32(); // stack.push(frame.pc); // frame.pc = pc; // break; // case Bytecodes.RET: // frame.pc = frame.local[frame.read8()]; // break; case Bytecodes.I2L: i32[sp] = i32[sp - 1] < 0 ? -1 : 0; sp++; continue; case Bytecodes.I2F: aliasedF32[0] = i32[--sp]; i32[sp++] = aliasedI32[0]; continue; case Bytecodes.I2D: aliasedF64[0] = i32[--sp]; i32[sp++] = aliasedI32[0]; i32[sp++] = aliasedI32[1]; continue; case Bytecodes.L2I: sp--; continue; case Bytecodes.L2F: aliasedF32[0] = Math.fround(longToNumber(i32[sp - 2], i32[sp - 1])); i32[sp - 2] = aliasedI32[0]; sp --; continue; case Bytecodes.L2D: aliasedF64[0] = longToNumber(i32[sp - 2], i32[sp - 1]); i32[sp - 2] = aliasedI32[0]; i32[sp - 1] = aliasedI32[1]; continue; case Bytecodes.F2I: fa = f32[sp - 1]; if (fa > Constants.INT_MAX) { i32[sp - 1] = Constants.INT_MAX; } else if (fa < Constants.INT_MIN) { i32[sp - 1] = Constants.INT_MIN; } else { i32[sp - 1] = fa | 0; } continue; case Bytecodes.F2L: fa = f32[--sp]; value = Long.fromNumber(fa); i32[sp++] = value.low_; i32[sp++] = value.high_; continue; case Bytecodes.F2D: aliasedF64[0] = f32[--sp]; i32[sp++] = aliasedI32[0]; i32[sp++] = aliasedI32[1]; continue; case Bytecodes.D2I: aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; fa = aliasedF64[0]; if (fa > Constants.INT_MAX) { i32[sp - 2] = Constants.INT_MAX; } else if (fa < Constants.INT_MIN) { i32[sp - 2] = Constants.INT_MIN; } else { i32[sp - 2] = fa | 0; } sp --; continue; case Bytecodes.D2L: aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; fa = aliasedF64[0]; if (fa === Number.POSITIVE_INFINITY) { i32[sp - 2] = Constants.LONG_MAX_LOW; i32[sp - 1] = Constants.LONG_MAX_HIGH; } else if (fa === Number.NEGATIVE_INFINITY) { i32[sp - 2] = Constants.LONG_MIN_LOW; i32[sp - 1] = Constants.LONG_MIN_HIGH; } else { value = Long.fromNumber(fa); i32[sp - 2] = value.low_; i32[sp - 1] = value.high_; } continue; case Bytecodes.D2F: aliasedI32[0] = i32[sp - 2]; aliasedI32[1] = i32[sp - 1]; f32[sp - 2] = Math.fround(aliasedF64[0]); sp --; continue; case Bytecodes.I2B: i32[sp - 1] = (i32[sp - 1] << 24) >> 24; continue; case Bytecodes.I2C: i32[sp - 1] &= 0xffff; continue; case Bytecodes.I2S: i32[sp - 1] = (i32[sp - 1] << 16) >> 16; continue; case Bytecodes.TABLESWITCH: pc = (pc + 3) & ~0x03; // Consume Padding offset = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; ia = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; ib = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; value = i32[--sp]; if (value >= ia && value <= ib) { pc += (value - ia) << 2; offset = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; } pc = opPC + offset; continue; case Bytecodes.LOOKUPSWITCH: pc = (pc + 3) & ~0x03; // Consume Padding offset = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; var npairs = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; value = i32[--sp]; lookup: for (var i = 0; i < npairs; i++) { var key = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; if (key === value) { offset = code[pc++] << 24 | code[pc++] << 16 | code[pc++] << 8 | code[pc++]; } else { pc += 4; } if (key >= value) { break lookup; } } pc = opPC + offset; continue; case Bytecodes.ANEWARRAY: index = code[pc++] << 8 | code[pc++]; classInfo = resolveClass(index, ci); classInitAndUnwindCheck(classInfo, opPC); size = i32[--sp]; ref[sp++] = newArray(classInfo.klass, size); continue; case Bytecodes.MULTIANEWARRAY: index = code[pc++] << 8 | code[pc++]; classInfo = resolveClass(index, ci); var dimensions = code[pc++]; var lengths = new Array(dimensions); for (var i = 0; i < dimensions; i++) { lengths[i] = i32[--sp]; } ref[sp++] = J2ME.newMultiArray(classInfo.klass, lengths.reverse()); continue; case Bytecodes.ARRAYLENGTH: array = ref[--sp]; i32[sp++] = array.length; continue; case Bytecodes.GETFIELD: case Bytecodes.GETSTATIC: index = code[pc++] << 8 | code[pc++]; fieldInfo = cp.resolved[index] || cp.resolveField(index, false); if (op === Bytecodes.GETSTATIC) { classInitAndUnwindCheck(fieldInfo.classInfo, opPC); //if (U) { // return; //} object = fieldInfo.classInfo.getStaticObject($.ctx); } else { object = ref[--sp]; } address = object._address + fieldInfo.byteOffset; switch (fieldInfo.kind) { case Kind.Reference: ref[sp++] = ref[address >> 2]; continue; case Kind.Int: case Kind.Byte: case Kind.Char: case Kind.Short: case Kind.Boolean: case Kind.Float: i32[sp++] = i32[address >> 2]; continue; case Kind.Long: case Kind.Double: i32[sp++] = i32[address >> 2]; i32[sp++] = i32[address + 4 >> 2]; continue; default: release || assert(false); } continue; case Bytecodes.PUTFIELD: case Bytecodes.PUTSTATIC: index = code[pc++] << 8 | code[pc++]; fieldInfo = cp.resolved[index] || cp.resolveField(index, false); isStatic = op === Bytecodes.PUTSTATIC; if (isStatic) { classInitAndUnwindCheck(fieldInfo.classInfo, opPC); //if (U) { // return; //} object = fieldInfo.classInfo.getStaticObject($.ctx); } else { object = ref[sp - (isTwoSlot(fieldInfo.kind) ? 3 : 2)]; } address = object._address + fieldInfo.byteOffset; switch (fieldInfo.kind) { case Kind.Reference: ref[address >> 2] = ref[--sp]; break; case Kind.Int: case Kind.Byte: case Kind.Char: case Kind.Short: case Kind.Boolean: case Kind.Float: i32[address >> 2] = i32[--sp]; break; case Kind.Long: case Kind.Double: i32[address + 4 >> 2] = i32[--sp]; i32[address >> 2] = i32[--sp]; break; default: release || assert(false); } if (!isStatic) { sp--; // Pop Reference } continue; case Bytecodes.NEW: index = code[pc++] << 8 | code[pc++]; release || traceWriter && traceWriter.writeLn(mi.implKey + " " + index); classInfo = resolveClass(index, ci); thread.set(fp, sp, pc); classInitAndUnwindCheck(classInfo, opPC); if (U) { return; } loadThreadState(); ref[sp++] = newObject(classInfo.klass); continue; case Bytecodes.CHECKCAST: index = code[pc++] << 8 | code[pc++]; classInfo = resolveClass(index, mi.classInfo); object = ref[sp - 1]; if (object && !isAssignableTo(object.klass, classInfo.klass)) { throw $.newClassCastException ( object.klass.classInfo.getClassNameSlow() + " is not assignable to " + classInfo.getClassNameSlow() ); } continue; case Bytecodes.INSTANCEOF: index = code[pc++] << 8 | code[pc++]; classInfo = resolveClass(index, ci); object = ref[--sp]; result = !object ? false : isAssignableTo(object.klass, classInfo.klass); i32[sp++] = result ? 1 : 0; continue; case Bytecodes.ATHROW: object = ref[--sp]; if (!object) { throw $.newNullPointerException(); } throw object; continue; case Bytecodes.MONITORENTER: object = ref[--sp]; thread.ctx.monitorEnter(object); if (U === VMState.Pausing || U === VMState.Stopping) { return; } continue; case Bytecodes.MONITOREXIT: object = ref[--sp]; thread.ctx.monitorExit(object); continue; case Bytecodes.WIDE: var op = code[pc++]; switch (op) { case Bytecodes.ILOAD: case Bytecodes.FLOAD: i32[sp++] = i32[lp + (code[pc++] << 8 | code[pc++])]; continue; case Bytecodes.ALOAD: ref[sp++] = ref[lp + (code[pc++] << 8 | code[pc++])]; continue; case Bytecodes.LLOAD: case Bytecodes.DLOAD: offset = lp + (code[pc++] << 8 | code[pc++]); i32[sp++] = i32[offset]; i32[sp++] = i32[offset + 1]; continue; case Bytecodes.ISTORE: case Bytecodes.FSTORE: i32[lp + (code[pc++] << 8 | code[pc++])] = i32[--sp]; continue; case Bytecodes.ASTORE: ref[lp + (code[pc++] << 8 | code[pc++])] = ref[--sp]; continue; case Bytecodes.LSTORE: case Bytecodes.DSTORE: offset = lp + (code[pc++] << 8 | code[pc++]); i32[offset + 1] = i32[--sp]; i32[offset] = i32[--sp]; continue; case Bytecodes.IINC: index = code[pc++] << 8 | code[pc++]; value = code[pc++] << 24 >> 24; i32[lp + index] = i32[lp + index] + value | 0; continue; //case Bytecodes.RET: // this.pc = this.local[this.read16()]; // break; default: var opName = Bytecodes[op]; throw new Error("Wide opcode " + opName + " [" + op + "] not supported."); } continue; case Bytecodes.NEWARRAY: type = code[pc++]; size = i32[--sp]; ref[sp++] = newArray(PrimitiveClassInfo["????ZCFDBSIJ"[type]].klass, size); continue; case Bytecodes.LRETURN: case Bytecodes.DRETURN: case Bytecodes.IRETURN: case Bytecodes.FRETURN: case Bytecodes.ARETURN: case Bytecodes.RETURN: var lastSP = sp; var lastMI = mi; pc = i32[fp + FrameLayout.CallerRAOffset]; sp = fp - maxLocals; fp = i32[fp + FrameLayout.CallerFPOffset]; mi = ref[fp + FrameLayout.CalleeMethodInfoOffset]; if (mi === null) { thread.set(fp, sp, pc); thread.popFrame(null); // REDUX: What do we do about the return value here? return; } maxLocals = mi.codeAttribute.max_locals; lp = fp - maxLocals; release || traceWriter && traceWriter.outdent(); release || traceWriter && traceWriter.writeLn("<< I " + lastMI.implKey); ci = mi.classInfo; cp = ci.constantPool; code = mi.codeAttribute.code; // Push return value. switch (op) { case Bytecodes.LRETURN: case Bytecodes.DRETURN: i32[sp++] = i32[lastSP - 2]; // Low Bits // Fallthrough case Bytecodes.IRETURN: case Bytecodes.FRETURN: i32[sp++] = i32[lastSP - 1]; continue; case Bytecodes.ARETURN: ref[sp++] = ref[lastSP - 1]; continue; } continue; case Bytecodes.INVOKEVIRTUAL: case Bytecodes.INVOKESPECIAL: case Bytecodes.INVOKESTATIC: case Bytecodes.INVOKEINTERFACE: index = code[pc++] << 8 | code[pc++]; if (op === Bytecodes.INVOKEINTERFACE) { pc += 2; // Args Number & Zero } isStatic = (op === Bytecodes.INVOKESTATIC); // Resolve method and do the class init check if necessary. var calleeMethodInfo = cp.resolved[index] || cp.resolveMethod(index, isStatic); var calleeTargetMethodInfo = null; var callee = null; object = null; if (!isStatic) { object = ref[sp - calleeMethodInfo.argumentSlots]; } switch (op) { case Bytecodes.INVOKESPECIAL: checkNull(object); case Bytecodes.INVOKESTATIC: calleeTargetMethodInfo = calleeMethodInfo; break; case Bytecodes.INVOKEVIRTUAL: calleeTargetMethodInfo = object.klass.classInfo.vTable[calleeMethodInfo.vTableIndex]; break; case Bytecodes.INVOKEINTERFACE: calleeTargetMethodInfo = object.klass.classInfo.iTable[calleeMethodInfo.mangledName]; break; default: release || traceWriter && traceWriter.writeLn("Not Implemented: " + Bytecodes[op]); assert(false, "Not Implemented: " + Bytecodes[op]); } // Call Native or Compiled Method. if (calleeTargetMethodInfo.isNative || calleeTargetMethodInfo.state === MethodState.Compiled) { args.length = 0; var signatureKinds = calleeTargetMethodInfo.signatureKinds; for (var i = signatureKinds.length - 1; i > 0; i--) { kind = signatureKinds[i]; switch (kind) { case Kind.Double: // Doubles are passed in as a number value. aliasedI32[1] = i32[--sp]; aliasedI32[0] = i32[--sp]; args.unshift(aliasedF64[0]); break; case Kind.Float: args.unshift(f32[--sp]); break; case Kind.Long: args.unshift(i32[--sp]); // High Bits // Fallthrough case Kind.Int: case Kind.Byte: case Kind.Char: case Kind.Short: case Kind.Boolean: args.unshift(i32[--sp]); break; case Kind.Reference: args.unshift(ref[--sp]); break; default: release || assert(false, "Invalid Kind: " + Kind[kind]); } } if (!isStatic) { --sp; // Pop Reference } thread.set(fp, sp, pc); callee = calleeTargetMethodInfo.fn || getLinkedMethod(calleeTargetMethodInfo); if (!release) { // assert(callee.length === args.length, "Function " + callee + " (" + calleeTargetMethodInfo.implKey + "), should have " + args.length + " arguments."); } result = callee.apply(object, args); //if (!release) { // checkReturnValue(calleeMethodInfo, returnValue); //} if (U) { return; } if (!release) { assert(!(result instanceof Long.constructor), "NO LONGS ALLOWED"); } loadThreadState(); kind = signatureKinds[0]; // Push return value. switch (kind) { case Kind.Double: // Doubles are passed in as a number value. aliasedF64[0] = result; i32[sp++] = aliasedI32[0]; i32[sp++] = aliasedI32[1]; continue; case Kind.Float: f32[sp++] = result; continue; case Kind.Long: i32[sp++] = result; i32[sp++] = tempReturn0; continue; case Kind.Int: case Kind.Byte: case Kind.Char: case Kind.Short: case Kind.Boolean: i32[sp++] = result; continue; case Kind.Reference: ref[sp++] = result; continue; case Kind.Void: continue; default: release || assert(false, "Invalid Kind: " + Kind[kind]); } continue; } release || traceWriter && traceWriter.writeLn(">> I " + calleeMethodInfo.implKey); mi = calleeTargetMethodInfo; maxLocals = mi.codeAttribute.max_locals; ci = mi.classInfo; cp = ci.constantPool; // Reserve space for non-parameter locals. sp += maxLocals - mi.argumentSlots; // Caller saved values. i32[sp++] = pc; i32[sp++] = fp; ref[sp++] = mi; fp = sp - FrameLayout.CallerSaveSize; lp = fp - maxLocals; opPC = pc = 0; code = mi.codeAttribute.code; release || traceWriter && traceWriter.indent(); continue; default: release || traceWriter && traceWriter.writeLn("Not Implemented: " + Bytecodes[op] + ", PC: " + opPC + ", CODE: " + code.length); release || assert(false, "Not Implemented: " + Bytecodes[op]); continue; } } catch (e) { release || traceWriter && traceWriter.redLn("XXX I Caught: " + e + ", details: " + toName(e)); // release || traceWriter && traceWriter.writeLns(e.stack); // release || traceWriter && traceWriter.writeLn(jsGlobal.getBacktrace()); thread.set(fp, sp, opPC); e = translateException(e); if (!e.klass) { // A non-java exception was thrown. Rethrow so it is not handled by exceptionUnwind. throw e; } thread.exceptionUnwind(e); loadThreadState(); mi = thread.frame.methodInfo; maxLocals = mi.codeAttribute.max_locals; lp = fp - maxLocals; ci = mi.classInfo; cp = ci.constantPool; code = mi.codeAttribute.code; continue; } } } // print(disassemble(interpret)); }