зеркало из https://github.com/mozilla/pluotsorbet.git
1718 строки
61 KiB
TypeScript
1718 строки
61 KiB
TypeScript
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;
|
|
};
|
|
(<any>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));
|
|
}
|