pluotsorbet/int.ts

1885 строки
70 KiB
TypeScript

module J2ME {
import assert = Debug.assert;
import Bytecodes = Bytecode.Bytecodes;
import isInvoke = Bytecode.isInvoke;
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.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.classInfo.utf8Name) + (o._address ? " " + toHEX(o._address) : "");
}
if (o && !o.classInfo) {
return o;
}
if (o && o.classInfo === CLASSES.java_lang_Class) {
return "[" + getObjectInfo(o) + "] " + classIdToClassInfoMap[o.vmClass].getClassNameSlow();
}
if (o && o.classInfo === CLASSES.java_lang_String) {
return "[" + getObjectInfo(o) + "] \"" + fromStringAddr(o._address) + "\"";
}
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.
*/
export function longToNumber(l: number, h: number): number {
return h * Constants.TWO_PWR_32_DBL + ((l >= 0) ? l : Constants.TWO_PWR_32_DBL + l);
}
export function numberToLong(v: number): number {
// TODO Extract logic from Long so we don't allocate here.
var long = Long.fromNumber(v);
tempReturn0 = long.high_;
return long.low_;
}
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 | // The opPC of the caller's invoke bytecode.
* +--------------------------------+
* | Caller FP |
* +--------------------------------+
* | Callee Method Info |
* +--------------------------------+
* | Monitor |
* +--------------------------------+
* | Stack slot 0 |
* +--------------------------------+
* | ... |
* +--------------------------------+
* | Stack slot (S-1) |
* SP ---> +--------------------------------+
*/
enum FrameLayout {
CalleeMethodInfoOffset = 2,
CallerFPOffset = 1,
CallerRAOffset = 0,
MonitorOffset = 3,
CallerSaveSize = 4
}
export class FrameView {
public fp: number;
public sp: number;
public pc: number;
public thread: Thread;
constructor() {
}
set(thread: Thread, fp: number, sp: number, pc: number) {
this.thread = thread;
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:
i32[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 > (this.thread.tp >> 2)) {
writer.writeLn((this.methodInfo ? this.methodInfo.implKey : "null") + ", FP: " + this.fp + ", SP: " + this.sp + ", PC: " + this.pc);
this.set(this.thread, 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) + " " +
// XXX ref[i] could be an address, so update toName to handle that.
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(4 * 1024);
this.bp = this.tp >> 2;
this.fp = this.bp;
this.sp = this.fp;
this.pc = -1;
this.view = new FrameView();
this.ctx = ctx;
release || threadWriter && threadWriter.writeLn("creatingThread: tp: " + toHEX(this.tp) + " " + toHEX(i32.byteLength));
}
set(fp: number, sp: number, pc: number) {
this.fp = fp;
this.sp = sp;
this.pc = pc;
}
hashFrame(): number {
var fp = this.fp << 2;
var sp = this.sp << 2;
return HashUtilities.hashBytesTo32BitsAdler(u8, fp, sp);
}
/**
* Advances the |pc| to the next |pc| after the current invoke bytecode.
*/
advancePastInvokeBytecode() {
var mi = ref[this.fp + FrameLayout.CalleeMethodInfoOffset];
var code = mi.codeAttribute.code;
var op = code[this.pc];
release || assert(isInvoke(op), "The PC should be at an invoke bytecode.");
this.pc += (op === Bytecodes.INVOKEINTERFACE ? 5 : 3);
}
get frame(): FrameView {
this.view.set(this, 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;
}
i32[this.fp + FrameLayout.CallerRAOffset] = this.pc; // Caller RA
i32[this.fp + FrameLayout.CallerFPOffset] = fp; // Caller FP
ref[this.fp + FrameLayout.CalleeMethodInfoOffset] = methodInfo; // Callee
i32[this.fp + FrameLayout.MonitorOffset] = Constants.NULL; // Monitor
this.sp = this.fp + FrameLayout.CallerSaveSize;
this.pc = 0;
}
popFrame(methodInfo: MethodInfo): MethodInfo {
var mi = ref[this.fp + FrameLayout.CalleeMethodInfoOffset];
release || assert(mi === methodInfo, "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);
release || traceWriter && traceWriter.writeLn("Checking catch range: " + exceptionEntryView.start_pc + " - " + exceptionEntryView.end_pc);
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);
release || traceWriter && traceWriter.writeLn("Checking catch type: " + classInfo);
if (isAssignableTo(e.classInfo, classInfo)) {
pc = exceptionEntryView.handler_pc;
break;
}
}
}
}
if (pc >= 0) {
this.pc = pc;
this.sp = this.fp + FrameLayout.CallerSaveSize;
release || assert(e instanceof Object && "_address" in e, "exception is object with address");
i32[this.sp++] = e._address;
return;
}
if (mi.isSynchronized) {
this.ctx.monitorExit(getMonitor(i32[this.fp + FrameLayout.MonitorOffset]));
}
mi = this.popFrame(mi);
release || traceWriter && traceWriter.outdent();
release || traceWriter && traceWriter.writeLn("<< I Unwind");
}
release || traceWriter && traceWriter.writeLn("Cannot catch: " + toName(e));
throw e;
}
classInitAndUnwindCheck(fp: number, sp: number, pc: number, classInfo: ClassInfo) {
this.set(fp, sp, pc);
classInitCheck(classInfo);
}
throwException(fp: number, sp: number, pc: number, type: ExceptionType, a?) {
this.set(fp, sp, pc);
switch (type) {
case ExceptionType.ArrayIndexOutOfBoundsException:
throwArrayIndexOutOfBoundsException(a);
break;
case ExceptionType.ArithmeticException:
throwArithmeticException();
break;
case ExceptionType.NegativeArraySizeException:
throwNegativeArraySizeException();
break;
case ExceptionType.NullPointerException:
throwNullPointerException();
break;
}
}
}
export function prepareInterpretedMethod(methodInfo: MethodInfo): Function {
var method = function fastInterpreterFrameAdapter() {
var thread = $.ctx.nativeThread;
var callerFP = thread.fp;
var callerPC = thread.pc;
// release || traceWriter && traceWriter.writeLn(">> I");
thread.pushFrame(null);
thread.pushFrame(methodInfo);
var calleeFP = thread.fp;
var frame = thread.frame;
var kinds = methodInfo.signatureKinds;
var index = 0;
if (!methodInfo.isStatic) {
frame.setParameter(Kind.Reference, index++, arguments[0]);
}
for (var i = 1; i < kinds.length; i++) {
frame.setParameter(kinds[i], index++, arguments[i]);
}
if (methodInfo.isSynchronized) {
var monitorAddr = methodInfo.isStatic ? $.getClassObjectAddress(methodInfo.classInfo) : arguments[0];
i32[calleeFP + FrameLayout.MonitorOffset] = monitorAddr;
$.ctx.monitorEnter(getMonitor(monitorAddr));
release || assert(U !== VMState.Yielding, "Monitors should never yield.");
if (U === VMState.Pausing || U === VMState.Stopping) {
return;
}
}
var v = interpret(thread);
if (U) {
release || assert(v === undefined, "Return value must be undefined.");
// Splice out the marker frame so the interpreter doesn't return early when execution is resumed.
i32[calleeFP + FrameLayout.CallerFPOffset] = callerFP;
i32[calleeFP + FrameLayout.CallerRAOffset] = callerPC;
return;
}
release || assert(callerFP === thread.fp, "callerFP === thread.fp");
// release || traceWriter && traceWriter.writeLn("<< I");
return v;
};
(<any>method).methodInfo = methodInfo;
return method;
}
function resolveClass(index: number, classInfo: ClassInfo): ClassInfo {
return classInfo.constantPool.resolveClass(index);
}
var args = new Array(16);
export enum ExceptionType {
ArithmeticException,
ArrayIndexOutOfBoundsException,
NegativeArraySizeException,
NullPointerException
}
/**
* Debugging helper to make sure native methods were implemented correctly.
*/
function checkReturnValue(methodInfo: MethodInfo, l: any, h: number) {
if (U) {
if (typeof l !== "undefined") {
assert(false, "Expected undefined return value during unwind, got " + l + " in " + methodInfo.implKey);
}
return;
}
if (!(getKindCheck(methodInfo.returnKind)(l, h))) {
assert(false, "Expected " + Kind[methodInfo.returnKind] + " return value, got " + l + " in " + methodInfo.implKey);
}
}
/**
* Main interpreter loop. This method is carefully written to avoid memory allocation and
* function calls on fast paths. Therefore, everything is inlined, even if it makes the code
* look ugly.
*
* The interpreter loop caches the thread state in local variables. Doing so avoids a lot of
* property accesses but also makes the code brittle since you need to manually sync up the
* thread state with the local thead state at precise points.
*
* At call sites, caller frame |pc|s are always at the beggining of invoke bytecodes. In the
* interpreter return bytecodes advance the pc past the invoke bytecode. Native code that
* unwinds and resumes execution at a later point needs to adjust the pc accordingly.
*
* Bytecodes that construct exception objects must save the tread state before executing any
* code that may overwrite the frame. Use the |throwException| helper method to ensure that
* the thread state is property saved.
*/
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, arrayAddr: number, offset, buffer, tag: TAGS, targetPC;
var address = 0, isStatic = false;
var ia = 0, ib = 0; // Integer Operands
var ll = 0, lh = 0; // Long Low / High
var classInfo: ClassInfo;
var otherClassInfo: ClassInfo;
var fieldInfo: FieldInfo;
var monitorAddr: number;
// HEAD
while (true) {
opPC = pc, op = code[pc], pc = pc + 1 | 0;
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(thread, 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:
i32[sp++] = Constants.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) {
i32[sp++] = 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:
i32[sp++] = i32[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:
i32[sp++] = i32[lp];
continue;
case Bytecodes.ALOAD_1:
case Bytecodes.ALOAD_2:
case Bytecodes.ALOAD_3:
i32[sp++] = i32[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];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[sp++] = i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index];
continue;
case Bytecodes.BALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[sp++] = i8[arrayAddr + Constants.ARRAY_HDR_SIZE + index];
continue;
case Bytecodes.CALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[sp++] = u16[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 1) + index];
continue;
case Bytecodes.SALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[sp++] = i16[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 1) + index];
continue;
case Bytecodes.FALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
f32[sp++] = f32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index];
continue;
case Bytecodes.AALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if (arrayAddr === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
continue;
}
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[sp++] = i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index];
continue;
case Bytecodes.DALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
aliasedF64[0] = f64[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 3) + 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:
i32[lp + code[pc++]] = i32[--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:
i32[lp + op - Bytecodes.ASTORE_0] = i32[--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];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index] = value;
continue;
case Bytecodes.FASTORE:
value = f32[--sp];
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
f32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index] = value;
continue;
case Bytecodes.BASTORE:
value = i32[--sp];
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i8[arrayAddr + Constants.ARRAY_HDR_SIZE + index] = value;
continue;
case Bytecodes.CASTORE:
value = i32[--sp];
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
u16[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 1) + index] = value;
continue;
case Bytecodes.SASTORE:
value = i32[--sp];
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i16[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 1) + index] = value;
continue;
case Bytecodes.LASTORE:
lh = i32[--sp];
ll = i32[--sp];
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index * 2 ] = ll;
i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index * 2 + 1] = lh;
continue;
case Bytecodes.LALOAD:
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
i32[sp++] = i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index * 2 ];
i32[sp++] = i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index * 2 + 1];
continue;
case Bytecodes.DASTORE:
aliasedI32[1] = i32[--sp];
aliasedI32[0] = i32[--sp];
value = aliasedF64[0];
index = i32[--sp];
arrayAddr = i32[--sp];
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
f64[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 3) + index] = value;
continue;
case Bytecodes.AASTORE:
address = i32[--sp];
index = i32[--sp];
arrayAddr = i32[--sp];
if (arrayAddr === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
continue;
}
if ((index >>> 0) >= (i32[arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2] >>> 0)) {
thread.throwException(fp, sp, opPC, ExceptionType.ArrayIndexOutOfBoundsException, index);
}
checkArrayStore(arrayAddr, address);
i32[(arrayAddr + Constants.ARRAY_HDR_SIZE >> 2) + index] = address;
continue;
case Bytecodes.POP:
--sp;
continue;
case Bytecodes.POP2:
sp -= 2;
continue;
case Bytecodes.DUP: // ... a -> ... a, a
i32[sp] = i32[sp - 1];
sp++;
continue;
case Bytecodes.DUP2: // ... b, a -> ... b, a, b, a
i32[sp ] = i32[sp - 2]; // b
i32[sp + 1] = i32[sp - 1]; // a
sp += 2;
continue;
case Bytecodes.DUP_X1: // ... b, a -> ... a, b, a
i32[sp ] = i32[sp - 1]; // a
i32[sp - 1] = i32[sp - 2]; // b
i32[sp - 2] = i32[sp]; // a
sp++;
continue;
case Bytecodes.DUP_X2: // ... c, b, a -> ... a, c, b, a
i32[sp ] = i32[sp - 1]; // a
i32[sp - 1] = i32[sp - 2]; // b
i32[sp - 2] = i32[sp - 3]; // c
i32[sp - 3] = i32[sp]; // a
sp++;
continue;
case Bytecodes.DUP2_X1: // ... c, b, a -> ... b, a, c, b, a
i32[sp + 1] = i32[sp - 1]; // a
i32[sp ] = i32[sp - 2]; // b
i32[sp - 1] = i32[sp - 3]; // c
i32[sp - 2] = i32[sp + 1]; // a
i32[sp - 3] = i32[sp ]; // b
sp += 2;
continue;
case Bytecodes.DUP2_X2: // ... d, c, b, a -> ... b, a, d, c, b, a
i32[sp + 1] = i32[sp - 1]; // a
i32[sp ] = i32[sp - 2]; // b
i32[sp - 1] = i32[sp - 3]; // c
i32[sp - 2] = i32[sp - 4]; // d
i32[sp - 3] = i32[sp + 1]; // a
i32[sp - 4] = i32[sp ]; // b
sp += 2;
continue;
case Bytecodes.SWAP:
ia = i32[sp - 1];
i32[sp - 1] = i32[sp - 2];
i32[sp - 2] = ia;
continue;
case Bytecodes.IINC:
index = code[pc++];
value = code[pc++] << 24 >> 24;
i32[lp + index] = i32[lp + index] + value | 0;
continue;
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) {
thread.throwException(fp, sp, opPC, 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) {
thread.throwException(fp, sp, opPC, ExceptionType.ArithmeticException);
}
ASM._lDiv(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2;
continue;
case Bytecodes.FDIV:
f32[sp - 2] = Math.fround(f32[sp - 2] / f32[sp - 1]); sp--;
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) {
thread.throwException(fp, sp, opPC, 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) {
thread.throwException(fp, sp, opPC, ExceptionType.ArithmeticException);
}
ASM._lRem(sp - 4 << 2, sp - 4 << 2, sp - 2 << 2); sp -= 2;
continue;
case Bytecodes.FREM:
f32[sp - 2] = Math.fround(f32[sp - 2] % f32[sp - 1]); sp--;
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:
var FCMP_fb = f32[--sp];
var FCMP_fa = f32[--sp];
if (isNaN(FCMP_fa) || isNaN(FCMP_fb)) {
i32[sp++] = op === Bytecodes.FCMPL ? -1 : 1;
} else if (FCMP_fa > FCMP_fb) {
i32[sp++] = 1;
} else if (FCMP_fa < FCMP_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];
var DCMP_fb = aliasedF64[0];
aliasedI32[0] = i32[sp - 4];
aliasedI32[1] = i32[sp - 3];
var DCMP_fa = aliasedF64[0];
sp -= 4;
if (isNaN(DCMP_fa) || isNaN(DCMP_fb)) {
i32[sp++] = op === Bytecodes.DCMPL ? -1 : 1;
} else if (DCMP_fa > DCMP_fb) {
i32[sp++] = 1;
} else if (DCMP_fa < DCMP_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 (i32[--sp] === i32[--sp]) {
pc = targetPC;
}
continue;
case Bytecodes.IF_ACMPNE:
targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16);
if (i32[--sp] !== i32[--sp]) {
pc = targetPC;
}
continue;
case Bytecodes.IFNULL:
targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16);
if (i32[--sp] === Constants.NULL) {
pc = targetPC;
}
continue;
case Bytecodes.IFNONNULL:
targetPC = opPC + ((code[pc++] << 8 | code[pc++]) << 16 >> 16);
if (i32[--sp] !== Constants.NULL) {
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:
var F2I_fa = f32[sp - 1];
if (F2I_fa > Constants.INT_MAX) {
i32[sp - 1] = Constants.INT_MAX;
} else if (F2I_fa < Constants.INT_MIN) {
i32[sp - 1] = Constants.INT_MIN;
} else {
i32[sp - 1] = F2I_fa | 0;
}
continue;
case Bytecodes.F2L:
value = Long.fromNumber(f32[--sp]);
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];
var D2I_fa = aliasedF64[0];
if (D2I_fa > Constants.INT_MAX) {
i32[sp - 2] = Constants.INT_MAX;
} else if (D2I_fa < Constants.INT_MIN) {
i32[sp - 2] = Constants.INT_MIN;
} else {
i32[sp - 2] = D2I_fa | 0;
}
sp --;
continue;
case Bytecodes.D2L:
aliasedI32[0] = i32[sp - 2];
aliasedI32[1] = i32[sp - 1];
var D2L_fa = aliasedF64[0];
if (D2L_fa === Number.POSITIVE_INFINITY) {
i32[sp - 2] = Constants.LONG_MAX_LOW;
i32[sp - 1] = Constants.LONG_MAX_HIGH;
} else if (D2L_fa === Number.NEGATIVE_INFINITY) {
i32[sp - 2] = Constants.LONG_MIN_LOW;
i32[sp - 1] = Constants.LONG_MIN_HIGH;
} else {
value = Long.fromNumber(D2L_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);
size = i32[--sp];
if (size < 0) {
thread.throwException(fp, sp, opPC, ExceptionType.NegativeArraySizeException);
}
i32[sp++] = newArray(classInfo, 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];
if (size < 0) {
thread.throwException(fp, sp, opPC, ExceptionType.NegativeArraySizeException);
}
}
i32[sp++] = J2ME.newMultiArray(classInfo, lengths.reverse());
continue;
case Bytecodes.ARRAYLENGTH:
arrayAddr = i32[--sp];
if (arrayAddr === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
continue;
}
i32[sp++] = i32[(arrayAddr + Constants.ARRAY_LENGTH_OFFSET >> 2)];
continue;
case Bytecodes.GETFIELD:
case Bytecodes.GETSTATIC:
index = code[pc++] << 8 | code[pc++];
fieldInfo = cp.resolved[index] || cp.resolveField(index, op === Bytecodes.GETSTATIC);
if (op === Bytecodes.GETSTATIC) {
thread.classInitAndUnwindCheck(fp, sp, opPC, fieldInfo.classInfo);
if (U) {
return;
}
address = $.getStaticObjectAddress(fieldInfo.classInfo) + fieldInfo.byteOffset;
if (address === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
continue;
}
} else {
address = i32[--sp];
if (address === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
continue;
}
address += fieldInfo.byteOffset;
}
switch (fieldInfo.kind) {
case Kind.Reference:
i32[sp++] = i32[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, "fieldInfo.kind");
}
continue;
case Bytecodes.PUTFIELD:
case Bytecodes.PUTSTATIC:
index = code[pc++] << 8 | code[pc++];
fieldInfo = cp.resolved[index] || cp.resolveField(index, op === Bytecodes.PUTSTATIC);
isStatic = op === Bytecodes.PUTSTATIC;
if (isStatic) {
thread.classInitAndUnwindCheck(fp, sp, opPC, fieldInfo.classInfo);
if (U) {
return;
}
address = $.getStaticObjectAddress(fieldInfo.classInfo) + fieldInfo.byteOffset;
} else {
address = i32[sp - (isTwoSlot(fieldInfo.kind) ? 3 : 2)];
if (address === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
continue;
}
address += fieldInfo.byteOffset;
}
switch (fieldInfo.kind) {
case Kind.Reference:
i32[address >> 2] = i32[--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, "fieldInfo.kind");
}
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.classInitAndUnwindCheck(fp, sp, opPC, classInfo);
if (U) {
return;
}
i32[sp++] = allocObject(classInfo);
continue;
case Bytecodes.CHECKCAST:
index = code[pc++] << 8 | code[pc++];
classInfo = resolveClass(index, mi.classInfo);
address = i32[sp - 1];
if (address === Constants.NULL) {
continue;
}
otherClassInfo = classIdToClassInfoMap[i32[address >> 2]];
if (!isAssignableTo(otherClassInfo, classInfo)) {
thread.set(fp, sp, opPC);
throw $.newClassCastException (
otherClassInfo.getClassNameSlow() + " is not assignable to " + classInfo.getClassNameSlow()
);
}
continue;
case Bytecodes.INSTANCEOF:
index = code[pc++] << 8 | code[pc++];
classInfo = resolveClass(index, ci);
address = i32[--sp];
if (address === Constants.NULL) {
i32[sp++] = 0;
} else {
otherClassInfo = classIdToClassInfoMap[i32[address >> 2]];
i32[sp++] = isAssignableTo(otherClassInfo, classInfo) ? 1 : 0;
}
continue;
case Bytecodes.ATHROW:
address = i32[--sp];
if (address === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
}
throw getHandle(address);
continue;
case Bytecodes.MONITORENTER:
thread.ctx.monitorEnter(getMonitor(i32[--sp]));
release || assert(U !== VMState.Yielding, "Monitors should never yield.");
if (U === VMState.Pausing || U === VMState.Stopping) {
thread.set(fp, sp, pc); // We need to resume past the MONITORENTER bytecode.
return;
}
continue;
case Bytecodes.MONITOREXIT:
thread.ctx.monitorExit(getMonitor(i32[--sp]));
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:
i32[sp++] = i32[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:
i32[lp + (code[pc++] << 8 | code[pc++])] = i32[--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++] << 8 | code[pc++]) << 16 >> 16;
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];
if (size < 0) {
thread.throwException(fp, sp, opPC, ExceptionType.NegativeArraySizeException);
}
i32[sp++] = newArray(PrimitiveClassInfo["????ZCFDBSIJ"[type]], 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;
var lastOP = op;
if (lastMI.isSynchronized) {
$.ctx.monitorExit(getMonitor(i32[fp + FrameLayout.MonitorOffset]));
}
opPC = i32[fp + FrameLayout.CallerRAOffset];
sp = fp - maxLocals;
fp = i32[fp + FrameLayout.CallerFPOffset];
mi = ref[fp + FrameLayout.CalleeMethodInfoOffset];
if (mi === null) {
thread.set(fp, sp, opPC);
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;
var op = code[opPC];
if (op >= Bytecodes.FIRST_INVOKE && op <= Bytecodes.LAST_INVOKE) {
// Calculate the PC based on the size of the caller's invoke bytecode.
pc = opPC + (code[opPC] === Bytecodes.INVOKEINTERFACE ? 5 : 3);
// Push return value.
switch (lastOP) {
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:
i32[sp++] = i32[lastSP - 1];
continue;
}
} else {
// We are returning to a bytecode that trapped.
// REDUX: I need to think through this some more, why do we need to subtract the CallerSaveSize? Is it
// because of the the null frame? This frame should have been spliced out, ... is this a coincidence?
sp -= FrameLayout.CallerSaveSize;
pc = opPC;
}
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: MethodInfo = cp.resolved[index] || cp.resolveMethod(index, isStatic);
var calleeTargetMethodInfo: MethodInfo = null;
var callee = null;
if (isStatic) {
address = Constants.NULL;
} else {
address = i32[sp - calleeMethodInfo.argumentSlots];
classInfo = (address !== Constants.NULL) ? classIdToClassInfoMap[i32[address >> 2]] : null;
}
if (isStatic) {
thread.classInitAndUnwindCheck(fp, sp, opPC, calleeMethodInfo.classInfo);
if (U) {
return;
}
}
switch (op) {
case Bytecodes.INVOKESPECIAL:
if (address === Constants.NULL) {
thread.throwException(fp, sp, opPC, ExceptionType.NullPointerException);
}
case Bytecodes.INVOKESTATIC:
calleeTargetMethodInfo = calleeMethodInfo;
break;
case Bytecodes.INVOKEVIRTUAL:
calleeTargetMethodInfo = classInfo.vTable[calleeMethodInfo.vTableIndex];
break;
case Bytecodes.INVOKEINTERFACE:
calleeTargetMethodInfo = 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) {
var kind = Kind.Void;
var signatureKinds = calleeTargetMethodInfo.signatureKinds;
callee = calleeTargetMethodInfo.fn || getLinkedMethod(calleeTargetMethodInfo);
var returnValue;
// Fast path for the no-argument case.
if (signatureKinds.length === 1) {
if (!isStatic) {
--sp; // Pop Reference
}
thread.set(fp, sp, opPC);
returnValue = callee(address);
} else {
args.length = 0;
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(i32[--sp]);
break;
default:
release || assert(false, "Invalid Kind: " + Kind[kind]);
}
}
if (!isStatic) {
--sp; // Pop Reference
}
thread.set(fp, sp, opPC);
if (!release) {
// assert(callee.length === args.length, "Function " + callee + " (" + calleeTargetMethodInfo.implKey + "), should have " + args.length + " arguments.");
}
args.unshift(address);
returnValue = callee.apply(null, args);
}
if (!release) {
// checkReturnValue(calleeMethodInfo, returnValue, tempReturn0);
}
if (U) {
release || traceWriter && traceWriter.writeLn("<< U Unwind: " + VMState[U]);
return;
}
kind = signatureKinds[0];
// Push return value.
switch (kind) {
case Kind.Double: // Doubles are passed in as a number value.
aliasedF64[0] = returnValue;
i32[sp++] = aliasedI32[0];
i32[sp++] = aliasedI32[1];
continue;
case Kind.Float:
f32[sp++] = returnValue;
continue;
case Kind.Long:
i32[sp++] = returnValue;
i32[sp++] = tempReturn0;
continue;
case Kind.Int:
case Kind.Byte:
case Kind.Char:
case Kind.Short:
case Kind.Boolean:
i32[sp++] = returnValue;
continue;
case Kind.Reference:
release || assert(returnValue !== "number", "native return value is a number");
i32[sp++] = returnValue;
continue;
case Kind.Void:
continue;
default:
release || assert(false, "Invalid Kind: " + Kind[kind]);
}
continue;
}
// Call Interpreted Method.
release || traceWriter && traceWriter.writeLn(">> I " + calleeTargetMethodInfo.implKey);
mi = calleeTargetMethodInfo;
maxLocals = mi.codeAttribute.max_locals;
ci = mi.classInfo;
cp = ci.constantPool;
var callerFPOffset = fp;
// Reserve space for non-parameter locals.
lp = sp - mi.argumentSlots;
fp = lp + maxLocals;
sp = fp + FrameLayout.CallerSaveSize;
// Caller saved values.
i32[fp + FrameLayout.CallerRAOffset] = opPC;
i32[fp + FrameLayout.CallerFPOffset] = callerFPOffset;
ref[fp + FrameLayout.CalleeMethodInfoOffset] = mi;
i32[fp + FrameLayout.MonitorOffset] = Constants.NULL; // Monitor
// Reset PC.
opPC = pc = 0;
if (calleeTargetMethodInfo.isSynchronized) {
monitorAddr = calleeTargetMethodInfo.isStatic
? $.getClassObjectAddress(calleeTargetMethodInfo.classInfo)
: address;
i32[fp + FrameLayout.MonitorOffset] = monitorAddr;
$.ctx.monitorEnter(getMonitor(monitorAddr));
release || assert(U !== VMState.Yielding, "Monitors should never yield.");
if (U === VMState.Pausing || U === VMState.Stopping) {
thread.set(fp, sp, opPC);
return;
}
}
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.classInfo) {
// A non-java exception was thrown. Rethrow so it is not handled by exceptionUnwind.
throw e;
}
thread.exceptionUnwind(e);
// Load thread state after exception unwind.
fp = thread.fp;
sp = thread.sp;
pc = thread.pc;
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));
}