зеркало из https://github.com/mozilla/pluotsorbet.git
2581 строка
83 KiB
JavaScript
2581 строка
83 KiB
JavaScript
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* vim: set shiftwidth=4 tabstop=4 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
var VM = {};
|
|
|
|
VM.Yield = {};
|
|
VM.Pause = {};
|
|
|
|
VM.DEBUG = false;
|
|
VM.DEBUG_PRINT_ALL_EXCEPTIONS = false;
|
|
|
|
VM.traceLog = "";
|
|
VM.trace = function(type, pid, methodInfo, returnVal) {
|
|
VM.traceLog += type + " " + pid + " " + methodInfo.classInfo.className + "." +
|
|
methodInfo.name + ":" + methodInfo.signature +
|
|
(returnVal ? (" " + returnVal) : "") + "\n";
|
|
}
|
|
|
|
function checkArrayAccess(ctx, refArray, idx) {
|
|
if (!refArray) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
if (idx < 0 || idx >= refArray.length) {
|
|
ctx.raiseExceptionAndYield("java/lang/ArrayIndexOutOfBoundsException", idx);
|
|
}
|
|
}
|
|
|
|
function resolve(ctx, cp, idx, isStatic) {
|
|
var constant = cp[idx];
|
|
if (!constant.tag)
|
|
return constant;
|
|
switch(constant.tag) {
|
|
case 3: // TAGS.CONSTANT_Integer
|
|
constant = constant.integer;
|
|
break;
|
|
case 4: // TAGS.CONSTANT_Float
|
|
constant = constant.float;
|
|
break;
|
|
case 8: // TAGS.CONSTANT_String
|
|
constant = util.newString(cp[constant.string_index].bytes);
|
|
break;
|
|
case 5: // TAGS.CONSTANT_Long
|
|
constant = Long.fromBits(constant.lowBits, constant.highBits);
|
|
break;
|
|
case 6: // TAGS.CONSTANT_Double
|
|
constant = constant.double;
|
|
break;
|
|
case 7: // TAGS.CONSTANT_Class
|
|
constant = CLASSES.getClass(cp[constant.name_index].bytes);
|
|
break;
|
|
case 9: // TAGS.CONSTANT_Fieldref
|
|
var classInfo = resolve(ctx, cp, constant.class_index, isStatic);
|
|
var fieldName = cp[cp[constant.name_and_type_index].name_index].bytes;
|
|
var signature = cp[cp[constant.name_and_type_index].signature_index].bytes;
|
|
constant = CLASSES.getField(classInfo, (isStatic ? "S" : "I") + "." + fieldName + "." + signature);
|
|
if (!constant)
|
|
ctx.raiseExceptionAndYield("java/lang/RuntimeException",
|
|
classInfo.className + "." + fieldName + "." + signature + " not found");
|
|
break;
|
|
case 10: // TAGS.CONSTANT_Methodref
|
|
case 11: // TAGS.CONSTANT_InterfaceMethodref
|
|
var classInfo = resolve(ctx, cp, constant.class_index, isStatic);
|
|
var methodName = cp[cp[constant.name_and_type_index].name_index].bytes;
|
|
var signature = cp[cp[constant.name_and_type_index].signature_index].bytes;
|
|
constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature);
|
|
if (!constant)
|
|
ctx.raiseExceptionAndYield("java/lang/RuntimeException",
|
|
classInfo.className + "." + methodName + "." + signature + " not found");
|
|
break;
|
|
default:
|
|
throw new Error("not supported constant type");
|
|
}
|
|
return cp[idx] = constant;
|
|
}
|
|
|
|
function classInitCheck(ctx, frame, classInfo, ip) {
|
|
if (classInfo.isArrayClass || ctx.runtime.initialized[classInfo.className])
|
|
return;
|
|
frame.ip = ip;
|
|
ctx.pushClassInitFrame(classInfo);
|
|
throw VM.Yield;
|
|
}
|
|
|
|
function pushFrame(ctx, methodInfo) {
|
|
var frame = ctx.pushFrame(methodInfo);
|
|
if (methodInfo.isSynchronized) {
|
|
if (!frame.lockObject) {
|
|
frame.lockObject = methodInfo.isStatic
|
|
? methodInfo.classInfo.getClassObject(ctx)
|
|
: frame.getLocal(0);
|
|
}
|
|
|
|
ctx.monitorEnter(frame.lockObject);
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
function popFrame(ctx, callee, consumes) {
|
|
if (callee.lockObject)
|
|
ctx.monitorExit(callee.lockObject);
|
|
var frame = ctx.popFrame();
|
|
var stack = frame.stack;
|
|
switch (consumes) {
|
|
case 2:
|
|
stack.push2(callee.stack.pop2());
|
|
break;
|
|
case 1:
|
|
stack.push(callee.stack.pop());
|
|
break;
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
function buildExceptionLog(ex, stackTrace) {
|
|
var className = ex.class.className;
|
|
var detailMessage = util.fromJavaString(CLASSES.getField(ex.class, "I.detailMessage.Ljava/lang/String;").get(ex));
|
|
return className + ": " + (detailMessage || "") + "\n" + stackTrace.join("\n") + "\n\n";
|
|
}
|
|
|
|
function throw_(ex, ctx) {
|
|
var exClass = ex.class;
|
|
|
|
var frame = ctx.current();
|
|
var stack = frame.stack;
|
|
var cp = frame.cp;
|
|
|
|
var stackTrace = [];
|
|
|
|
do {
|
|
var exception_table = frame.methodInfo.exception_table;
|
|
var handler_pc = null;
|
|
for (var i=0; exception_table && i<exception_table.length; i++) {
|
|
if (frame.ip >= exception_table[i].start_pc && frame.ip <= exception_table[i].end_pc) {
|
|
if (exception_table[i].catch_type === 0) {
|
|
handler_pc = exception_table[i].handler_pc;
|
|
} else {
|
|
var classInfo = resolve(ctx, cp, exception_table[i].catch_type);
|
|
if (ex.class.isAssignableTo(classInfo)) {
|
|
handler_pc = exception_table[i].handler_pc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var classInfo = frame.methodInfo.classInfo;
|
|
if (classInfo && classInfo.className) {
|
|
stackTrace.push(" - " + classInfo.className + "." + frame.methodInfo.name + "(), bci=" + frame.ip);
|
|
}
|
|
|
|
if (handler_pc != null) {
|
|
stack.length = 0;
|
|
stack.push(ex);
|
|
frame.ip = handler_pc;
|
|
|
|
if (VM.DEBUG_PRINT_ALL_EXCEPTIONS) {
|
|
console.error(buildExceptionLog(ex, stackTrace));
|
|
}
|
|
|
|
while (frame.methodInfo.compiled) {
|
|
frame = frame.methodInfo.compiled(ctx);
|
|
}
|
|
|
|
return frame;
|
|
}
|
|
|
|
if (ctx.frames.length == 1) {
|
|
break;
|
|
}
|
|
|
|
frame = popFrame(ctx, frame, 0);
|
|
stack = frame.stack;
|
|
cp = frame.cp;
|
|
} while (frame.methodInfo);
|
|
|
|
ctx.kill();
|
|
|
|
if (ctx.thread && ctx.thread.waiting && ctx.thread.waiting.length > 0) {
|
|
console.error(buildExceptionLog(ex, stackTrace));
|
|
|
|
ctx.thread.waiting.forEach(function(waitingCtx, n) {
|
|
ctx.thread.waiting[n] = null;
|
|
waitingCtx.wakeup(ctx.thread);
|
|
});
|
|
|
|
return frame;
|
|
} else {
|
|
throw new Error(buildExceptionLog(ex, stackTrace));
|
|
}
|
|
}
|
|
|
|
VM.execute = function(ctx) {
|
|
var frame = ctx.current();
|
|
|
|
while (frame && frame.methodInfo.compiled) {
|
|
frame = frame.methodInfo.compiled(ctx);
|
|
}
|
|
|
|
|
|
if (!frame) {
|
|
return;
|
|
}
|
|
|
|
var cp = frame.cp;
|
|
var stack = frame.stack;
|
|
|
|
while (true) {
|
|
var op = frame.read8();
|
|
switch (op) {
|
|
case 0x00: // nop
|
|
break;
|
|
case 0x01: // aconst_null
|
|
stack.push(null);
|
|
break;
|
|
case 0x02: // aconst_m1
|
|
stack.push(-1);
|
|
break;
|
|
case 0x03: // iconst_0
|
|
case 0x0b: // fconst_0
|
|
stack.push(0);
|
|
break;
|
|
case 0x0e: // dconst_0
|
|
stack.push2(0);
|
|
break;
|
|
case 0x04: // iconst_1
|
|
case 0x0c: // fconst_1
|
|
stack.push(1);
|
|
break;
|
|
case 0x0f: // dconst_1
|
|
stack.push2(1);
|
|
break;
|
|
case 0x05: // iconst_2
|
|
case 0x0d: // fconst_2
|
|
stack.push(2);
|
|
break;
|
|
case 0x06: // iconst_3
|
|
stack.push(3);
|
|
break;
|
|
case 0x07: // iconst_4
|
|
stack.push(4);
|
|
break;
|
|
case 0x08: // iconst_5
|
|
stack.push(5);
|
|
break;
|
|
case 0x09: // lconst_0
|
|
stack.push2(Long.fromInt(0));
|
|
break;
|
|
case 0x0a: // lconst_1
|
|
stack.push2(Long.fromInt(1));
|
|
break;
|
|
case 0x10: // bipush
|
|
stack.push(frame.read8signed());
|
|
break;
|
|
case 0x11: // sipush
|
|
stack.push(frame.read16signed());
|
|
break;
|
|
case 0x12: // ldc
|
|
case 0x13: // ldc_w
|
|
var idx = (op === 0x12) ? frame.read8() : frame.read16();
|
|
var constant = cp[idx];
|
|
if (constant.tag)
|
|
constant = resolve(ctx, cp, idx);
|
|
stack.push(constant);
|
|
break;
|
|
case 0x14: // ldc2_w
|
|
var idx = frame.read16();
|
|
var constant = cp[idx];
|
|
if (constant.tag)
|
|
constant = resolve(ctx, cp, idx);
|
|
stack.push2(constant);
|
|
break;
|
|
case 0x15: // iload
|
|
case 0x17: // fload
|
|
case 0x19: // aload
|
|
stack.push(frame.getLocal(frame.read8()));
|
|
break;
|
|
case 0x16: // lload
|
|
case 0x18: // dload
|
|
stack.push2(frame.getLocal(frame.read8()));
|
|
break;
|
|
case 0x1a: // iload_0
|
|
case 0x22: // fload_0
|
|
case 0x2a: // aload_0
|
|
stack.push(frame.getLocal(0));
|
|
break;
|
|
case 0x1b: // iload_1
|
|
case 0x23: // fload_1
|
|
case 0x2b: // aload_1
|
|
stack.push(frame.getLocal(1));
|
|
break;
|
|
case 0x1c: // iload_2
|
|
case 0x24: // fload_2
|
|
case 0x2c: // aload_2
|
|
stack.push(frame.getLocal(2));
|
|
break;
|
|
case 0x1d: // iload_3
|
|
case 0x25: // fload_3
|
|
case 0x2d: // aload_3
|
|
stack.push(frame.getLocal(3));
|
|
break;
|
|
case 0x1e: // lload_0
|
|
case 0x26: // dload_0
|
|
stack.push2(frame.getLocal(0));
|
|
break;
|
|
case 0x1f: // lload_1
|
|
case 0x27: // dload_1
|
|
stack.push2(frame.getLocal(1));
|
|
break;
|
|
case 0x20: // lload_2
|
|
case 0x28: // dload_2
|
|
stack.push2(frame.getLocal(2));
|
|
break;
|
|
case 0x21: // lload_3
|
|
case 0x29: // dload_3
|
|
stack.push2(frame.getLocal(3));
|
|
break;
|
|
case 0x2e: // iaload
|
|
case 0x30: // faload
|
|
case 0x32: // aaload
|
|
case 0x33: // baload
|
|
case 0x34: // caload
|
|
case 0x35: // saload
|
|
var idx = stack.pop();
|
|
var refArray = stack.pop();
|
|
checkArrayAccess(ctx, refArray, idx);
|
|
stack.push(refArray[idx]);
|
|
break;
|
|
case 0x2f: // laload
|
|
case 0x31: // daload
|
|
var idx = stack.pop();
|
|
var refArray = stack.pop();
|
|
checkArrayAccess(ctx, refArray, idx);
|
|
stack.push2(refArray[idx]);
|
|
break;
|
|
case 0x36: // istore
|
|
case 0x38: // fstore
|
|
case 0x3a: // astore
|
|
frame.setLocal(frame.read8(), stack.pop());
|
|
break;
|
|
case 0x37: // lstore
|
|
case 0x39: // dstore
|
|
frame.setLocal(frame.read8(), stack.pop2());
|
|
break;
|
|
case 0x3b: // istore_0
|
|
case 0x43: // fstore_0
|
|
case 0x4b: // astore_0
|
|
frame.setLocal(0, stack.pop());
|
|
break;
|
|
case 0x3c: // istore_1
|
|
case 0x44: // fstore_1
|
|
case 0x4c: // astore_1
|
|
frame.setLocal(1, stack.pop());
|
|
break;
|
|
case 0x3d: // istore_2
|
|
case 0x45: // fstore_2
|
|
case 0x4d: // astore_2
|
|
frame.setLocal(2, stack.pop());
|
|
break;
|
|
case 0x3e: // istore_3
|
|
case 0x46: // fstore_3
|
|
case 0x4e: // astore_3
|
|
frame.setLocal(3, stack.pop());
|
|
break;
|
|
case 0x3f: // lstore_0
|
|
case 0x47: // dstore_0
|
|
frame.setLocal(0, stack.pop2());
|
|
break;
|
|
case 0x40: // lstore_1
|
|
case 0x48: // dstore_1
|
|
frame.setLocal(1, stack.pop2());
|
|
break;
|
|
case 0x41: // lstore_2
|
|
case 0x49: // dstore_2
|
|
frame.setLocal(2, stack.pop2());
|
|
break;
|
|
case 0x42: // lstore_3
|
|
case 0x4a: // dstore_3
|
|
frame.setLocal(3, stack.pop2());
|
|
break;
|
|
case 0x4f: // iastore
|
|
case 0x51: // fastore
|
|
case 0x54: // bastore
|
|
case 0x55: // castore
|
|
case 0x56: // sastore
|
|
var val = stack.pop();
|
|
var idx = stack.pop();
|
|
var refArray = stack.pop();
|
|
checkArrayAccess(ctx, refArray, idx);
|
|
refArray[idx] = val;
|
|
break;
|
|
case 0x50: // lastore
|
|
case 0x52: // dastore
|
|
var val = stack.pop2();
|
|
var idx = stack.pop();
|
|
var refArray = stack.pop();
|
|
checkArrayAccess(ctx, refArray, idx);
|
|
refArray[idx] = val;
|
|
break;
|
|
case 0x53: // aastore
|
|
var val = stack.pop();
|
|
var idx = stack.pop();
|
|
var refArray = stack.pop();
|
|
checkArrayAccess(ctx, refArray, idx);
|
|
if (val && !val.class.isAssignableTo(refArray.class.elementClass)) {
|
|
ctx.raiseExceptionAndYield("java/lang/ArrayStoreException");
|
|
}
|
|
refArray[idx] = val;
|
|
break;
|
|
case 0x57: // pop
|
|
stack.pop();
|
|
break;
|
|
case 0x58: // pop2
|
|
stack.pop2();
|
|
break;
|
|
case 0x59: // dup
|
|
var val = stack.pop();
|
|
stack.push(val);
|
|
stack.push(val);
|
|
break;
|
|
case 0x5a: // dup_x1
|
|
var a = stack.pop();
|
|
var b = stack.pop();
|
|
stack.push(a);
|
|
stack.push(b);
|
|
stack.push(a);
|
|
break;
|
|
case 0x5b: // dup_x2
|
|
var a = stack.pop();
|
|
var b = stack.pop();
|
|
var c = stack.pop();
|
|
stack.push(a);
|
|
stack.push(c);
|
|
stack.push(b);
|
|
stack.push(a);
|
|
break;
|
|
case 0x5c: // dup2
|
|
var a = stack.pop();
|
|
var b = stack.pop();
|
|
stack.push(b);
|
|
stack.push(a);
|
|
stack.push(b);
|
|
stack.push(a);
|
|
break;
|
|
case 0x5d: // dup2_x1
|
|
var a = stack.pop();
|
|
var b = stack.pop();
|
|
var c = stack.pop();
|
|
stack.push(b);
|
|
stack.push(a);
|
|
stack.push(c);
|
|
stack.push(b);
|
|
stack.push(a);
|
|
break;
|
|
case 0x5e: // dup2_x2
|
|
var a = stack.pop();
|
|
var b = stack.pop();
|
|
var c = stack.pop();
|
|
var d = stack.pop();
|
|
stack.push(b);
|
|
stack.push(a);
|
|
stack.push(d);
|
|
stack.push(c);
|
|
stack.push(b);
|
|
stack.push(a);
|
|
break;
|
|
case 0x5f: // swap
|
|
var a = stack.pop();
|
|
var b = stack.pop();
|
|
stack.push(a);
|
|
stack.push(b);
|
|
break;
|
|
case 0x84: // iinc
|
|
var idx = frame.read8();
|
|
var val = frame.read8signed();
|
|
frame.setLocal(idx, frame.getLocal(idx) + val);
|
|
break;
|
|
case 0x60: // iadd
|
|
stack.push((stack.pop() + stack.pop())|0);
|
|
break;
|
|
case 0x61: // ladd
|
|
stack.push2(stack.pop2().add(stack.pop2()));
|
|
break;
|
|
case 0x62: // fadd
|
|
stack.push(Math.fround(stack.pop() + stack.pop()));
|
|
break;
|
|
case 0x63: // dadd
|
|
stack.push2(stack.pop2() + stack.pop2());
|
|
break;
|
|
case 0x64: // isub
|
|
stack.push((- stack.pop() + stack.pop())|0);
|
|
break;
|
|
case 0x65: // lsub
|
|
stack.push2(stack.pop2().negate().add(stack.pop2()));
|
|
break;
|
|
case 0x66: // fsub
|
|
stack.push(Math.fround(- stack.pop() + stack.pop()));
|
|
break;
|
|
case 0x67: // dsub
|
|
stack.push2(- stack.pop2() + stack.pop2());
|
|
break;
|
|
case 0x68: // imul
|
|
stack.push(Math.imul(stack.pop(), stack.pop()));
|
|
break;
|
|
case 0x69: // lmul
|
|
stack.push2(stack.pop2().multiply(stack.pop2()));
|
|
break;
|
|
case 0x6a: // fmul
|
|
stack.push(Math.fround(stack.pop() * stack.pop()));
|
|
break;
|
|
case 0x6b: // dmul
|
|
stack.push2(stack.pop2() * stack.pop2());
|
|
break;
|
|
case 0x6c: // idiv
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
if (!b) {
|
|
ctx.raiseExceptionAndYield("java/lang/ArithmeticException", "/ by zero");
|
|
}
|
|
stack.push((a === util.INT_MIN && b === -1) ? a : ((a / b)|0));
|
|
break;
|
|
case 0x6d: // ldiv
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
if (b.isZero()) {
|
|
ctx.raiseExceptionAndYield("java/lang/ArithmeticException", "/ by zero");
|
|
}
|
|
stack.push2(a.div(b));
|
|
break;
|
|
case 0x6e: // fdiv
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
stack.push(Math.fround(a / b));
|
|
break;
|
|
case 0x6f: // ddiv
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
stack.push2(a / b);
|
|
break;
|
|
case 0x70: // irem
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
if (!b) {
|
|
ctx.raiseExceptionAndYield("java/lang/ArithmeticException", "/ by zero");
|
|
}
|
|
stack.push(a % b);
|
|
break;
|
|
case 0x71: // lrem
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
if (b.isZero()) {
|
|
ctx.raiseExceptionAndYield("java/lang/ArithmeticException", "/ by zero");
|
|
}
|
|
stack.push2(a.modulo(b));
|
|
break;
|
|
case 0x72: // frem
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
stack.push(Math.fround(a % b));
|
|
break;
|
|
case 0x73: // drem
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
stack.push2(a % b);
|
|
break;
|
|
case 0x74: // ineg
|
|
stack.push((- stack.pop())|0);
|
|
break;
|
|
case 0x75: // lneg
|
|
stack.push2(stack.pop2().negate());
|
|
break;
|
|
case 0x76: // fneg
|
|
stack.push(- stack.pop());
|
|
break;
|
|
case 0x77: // dneg
|
|
stack.push2(- stack.pop2());
|
|
break;
|
|
case 0x78: // ishl
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
stack.push(a << b);
|
|
break;
|
|
case 0x79: // lshl
|
|
var b = stack.pop();
|
|
var a = stack.pop2();
|
|
stack.push2(a.shiftLeft(b));
|
|
break;
|
|
case 0x7a: // ishr
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
stack.push(a >> b);
|
|
break;
|
|
case 0x7b: // lshr
|
|
var b = stack.pop();
|
|
var a = stack.pop2();
|
|
stack.push2(a.shiftRight(b));
|
|
break;
|
|
case 0x7c: // iushr
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
stack.push(a >>> b);
|
|
break;
|
|
case 0x7d: // lushr
|
|
var b = stack.pop();
|
|
var a = stack.pop2();
|
|
stack.push2(a.shiftRightUnsigned(b));
|
|
break;
|
|
case 0x7e: // iand
|
|
stack.push(stack.pop() & stack.pop());
|
|
break;
|
|
case 0x7f: // land
|
|
stack.push2(stack.pop2().and(stack.pop2()));
|
|
break;
|
|
case 0x80: // ior
|
|
stack.push(stack.pop() | stack.pop());
|
|
break;
|
|
case 0x81: // lor
|
|
stack.push2(stack.pop2().or(stack.pop2()));
|
|
break;
|
|
case 0x82: // ixor
|
|
stack.push(stack.pop() ^ stack.pop());
|
|
break;
|
|
case 0x83: // lxor
|
|
stack.push2(stack.pop2().xor(stack.pop2()));
|
|
break;
|
|
case 0x94: // lcmp
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
if (a.greaterThan(b)) {
|
|
stack.push(1);
|
|
} else if (a.lessThan(b)) {
|
|
stack.push(-1);
|
|
} else {
|
|
stack.push(0);
|
|
}
|
|
break;
|
|
case 0x95: // fcmpl
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
if (isNaN(a) || isNaN(b)) {
|
|
stack.push(-1);
|
|
} else if (a > b) {
|
|
stack.push(1);
|
|
} else if (a < b) {
|
|
stack.push(-1);
|
|
} else {
|
|
stack.push(0);
|
|
}
|
|
break;
|
|
case 0x96: // fcmpg
|
|
var b = stack.pop();
|
|
var a = stack.pop();
|
|
if (isNaN(a) || isNaN(b)) {
|
|
stack.push(1);
|
|
} else if (a > b) {
|
|
stack.push(1);
|
|
} else if (a < b) {
|
|
stack.push(-1);
|
|
} else {
|
|
stack.push(0);
|
|
}
|
|
break;
|
|
case 0x97: // dcmpl
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
if (isNaN(a) || isNaN(b)) {
|
|
stack.push(-1);
|
|
} else if (a > b) {
|
|
stack.push(1);
|
|
} else if (a < b) {
|
|
stack.push(-1);
|
|
} else {
|
|
stack.push(0);
|
|
}
|
|
break;
|
|
case 0x98: // dcmpg
|
|
var b = stack.pop2();
|
|
var a = stack.pop2();
|
|
if (isNaN(a) || isNaN(b)) {
|
|
stack.push(1);
|
|
} else if (a > b) {
|
|
stack.push(1);
|
|
} else if (a < b) {
|
|
stack.push(-1);
|
|
} else {
|
|
stack.push(0);
|
|
}
|
|
break;
|
|
case 0x99: // ifeq
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() === 0 ? jmp : frame.ip;
|
|
break;
|
|
case 0x9a: // ifne
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() !== 0 ? jmp : frame.ip;
|
|
break;
|
|
case 0x9b: // iflt
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() < 0 ? jmp : frame.ip;
|
|
break;
|
|
case 0x9c: // ifge
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() >= 0 ? jmp : frame.ip;
|
|
break;
|
|
case 0x9d: // ifgt
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() > 0 ? jmp : frame.ip;
|
|
break;
|
|
case 0x9e: // ifle
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() <= 0 ? jmp : frame.ip;
|
|
break;
|
|
case 0x9f: // if_icmpeq
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() === stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa0: // if_cmpne
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() !== stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa1: // if_icmplt
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() > stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa2: // if_icmpge
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() <= stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa3: // if_icmpgt
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() < stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa4: // if_icmple
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() >= stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa5: // if_acmpeq
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() === stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa6: // if_acmpne
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() !== stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xc6: // ifnull
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = !stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xc7: // ifnonnull
|
|
var jmp = frame.ip - 1 + frame.read16signed();
|
|
frame.ip = stack.pop() ? jmp : frame.ip;
|
|
break;
|
|
case 0xa7: // goto
|
|
frame.ip += frame.read16signed() - 1;
|
|
break;
|
|
case 0xc8: // goto_w
|
|
frame.ip += frame.read32signed() - 1;
|
|
break;
|
|
case 0xa8: // jsr
|
|
var jmp = frame.read16();
|
|
stack.push(frame.ip);
|
|
frame.ip = jmp;
|
|
break;
|
|
case 0xc9: // jsr_w
|
|
var jmp = frame.read32();
|
|
stack.push(frame.ip);
|
|
frame.ip = jmp;
|
|
break;
|
|
case 0xa9: // ret
|
|
frame.ip = frame.getLocal(frame.read8());
|
|
break;
|
|
case 0x85: // i2l
|
|
stack.push2(Long.fromInt(stack.pop()));
|
|
break;
|
|
case 0x86: // i2f
|
|
break;
|
|
case 0x87: // i2d
|
|
stack.push2(stack.pop());
|
|
break;
|
|
case 0x88: // l2i
|
|
stack.push(stack.pop2().toInt());
|
|
break;
|
|
case 0x89: // l2f
|
|
stack.push(Math.fround(stack.pop2().toNumber()));
|
|
break;
|
|
case 0x8a: // l2d
|
|
stack.push2(stack.pop2().toNumber());
|
|
break;
|
|
case 0x8b: // f2i
|
|
stack.push(util.double2int(stack.pop()));
|
|
break;
|
|
case 0x8c: // f2l
|
|
stack.push2(Long.fromNumber(stack.pop()));
|
|
break;
|
|
case 0x8d: // f2d
|
|
stack.push2(stack.pop());
|
|
break;
|
|
case 0x8e: // d2i
|
|
stack.push(util.double2int(stack.pop2()));
|
|
break;
|
|
case 0x8f: // d2l
|
|
stack.push2(util.double2long(stack.pop2()));
|
|
break;
|
|
case 0x90: // d2f
|
|
stack.push(Math.fround(stack.pop2()));
|
|
break;
|
|
case 0x91: // i2b
|
|
stack.push((stack.pop() << 24) >> 24);
|
|
break;
|
|
case 0x92: // i2c
|
|
stack.push(stack.pop() & 0xffff);
|
|
break;
|
|
case 0x93: // i2s
|
|
stack.push((stack.pop() << 16) >> 16);
|
|
break;
|
|
case 0xaa: // tableswitch
|
|
var startip = frame.ip;
|
|
while ((frame.ip & 3) != 0)
|
|
frame.ip++;
|
|
var def = frame.read32signed();
|
|
var low = frame.read32signed();
|
|
var high = frame.read32signed();
|
|
var val = stack.pop();
|
|
var jmp;
|
|
if (val < low || val > high) {
|
|
jmp = def;
|
|
} else {
|
|
frame.ip += (val - low) << 2;
|
|
jmp = frame.read32signed();
|
|
}
|
|
frame.ip = startip - 1 + jmp;
|
|
break;
|
|
case 0xab: // lookupswitch
|
|
var startip = frame.ip;
|
|
while ((frame.ip & 3) != 0)
|
|
frame.ip++;
|
|
var jmp = frame.read32signed();
|
|
var size = frame.read32();
|
|
var val = frame.stack.pop();
|
|
lookup:
|
|
for (var i=0; i<size; i++) {
|
|
var key = frame.read32signed();
|
|
var offset = frame.read32signed();
|
|
if (key === val) {
|
|
jmp = offset;
|
|
}
|
|
if (key >= val) {
|
|
break lookup;
|
|
}
|
|
}
|
|
frame.ip = startip - 1 + jmp;
|
|
break;
|
|
case 0xbc: // newarray
|
|
var type = frame.read8();
|
|
var size = stack.pop();
|
|
if (size < 0) {
|
|
ctx.raiseExceptionAndYield("java/lang/NegativeArraySizeException", size);
|
|
}
|
|
stack.push(util.newPrimitiveArray("????ZCFDBSIJ"[type], size));
|
|
break;
|
|
case 0xbd: // anewarray
|
|
var idx = frame.read16();
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag)
|
|
classInfo = resolve(ctx, cp, idx);
|
|
var size = stack.pop();
|
|
if (size < 0) {
|
|
ctx.raiseExceptionAndYield("java/lang/NegativeArraySizeException", size);
|
|
}
|
|
var className = classInfo.className;
|
|
if (className[0] !== "[")
|
|
className = "L" + className + ";";
|
|
className = "[" + className;
|
|
stack.push(util.newArray(className, size));
|
|
break;
|
|
case 0xc5: // multianewarray
|
|
var idx = frame.read16();
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag)
|
|
classInfo = resolve(ctx, cp, idx);
|
|
var dimensions = frame.read8();
|
|
var lengths = new Array(dimensions);
|
|
for (var i=0; i<dimensions; i++)
|
|
lengths[i] = stack.pop();
|
|
stack.push(util.newMultiArray(classInfo.className, lengths.reverse()));
|
|
break;
|
|
case 0xbe: // arraylength
|
|
var obj = stack.pop();
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
stack.push(obj.length);
|
|
break;
|
|
case 0xb4: // getfield
|
|
var idx = frame.read16();
|
|
var field = cp[idx];
|
|
if (field.tag)
|
|
field = resolve(ctx, cp, idx, false);
|
|
var obj = stack.pop();
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
stack.pushType(field.signature, field.get(obj));
|
|
break;
|
|
case 0xb5: // putfield
|
|
var idx = frame.read16();
|
|
var field = cp[idx];
|
|
if (field.tag)
|
|
field = resolve(ctx, cp, idx, false);
|
|
var val = stack.popType(field.signature);
|
|
var obj = stack.pop();
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
field.set(obj, val);
|
|
break;
|
|
case 0xb2: // getstatic
|
|
var idx = frame.read16();
|
|
var field = cp[idx];
|
|
if (field.tag)
|
|
field = resolve(ctx, cp, idx, true);
|
|
classInitCheck(ctx, frame, field.classInfo, frame.ip-3);
|
|
var value = ctx.runtime.getStatic(field);
|
|
if (typeof value === "undefined") {
|
|
value = util.defaultValue(field.signature);
|
|
}
|
|
stack.pushType(field.signature, value);
|
|
break;
|
|
case 0xb3: // putstatic
|
|
var idx = frame.read16();
|
|
var field = cp[idx];
|
|
if (field.tag)
|
|
field = resolve(ctx, cp, idx, true);
|
|
classInitCheck(ctx, frame, field.classInfo, frame.ip-3);
|
|
ctx.runtime.setStatic(field, stack.popType(field.signature));
|
|
break;
|
|
case 0xbb: // new
|
|
var idx = frame.read16();
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag)
|
|
classInfo = resolve(ctx, cp, idx);
|
|
classInitCheck(ctx, frame, classInfo, frame.ip-3);
|
|
stack.push(util.newObject(classInfo));
|
|
break;
|
|
case 0xc0: // checkcast
|
|
var idx = frame.read16();
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag)
|
|
classInfo = resolve(ctx, cp, idx);
|
|
var obj = stack[stack.length - 1];
|
|
if (obj && !obj.class.isAssignableTo(classInfo)) {
|
|
ctx.raiseExceptionAndYield("java/lang/ClassCastException",
|
|
obj.class.className + " is not assignable to " +
|
|
classInfo.className);
|
|
}
|
|
break;
|
|
case 0xc1: // instanceof
|
|
var idx = frame.read16();
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag)
|
|
classInfo = resolve(ctx, cp, idx);
|
|
var obj = stack.pop();
|
|
var result = !obj ? false : obj.class.isAssignableTo(classInfo);
|
|
stack.push(result ? 1 : 0);
|
|
break;
|
|
case 0xbf: // athrow
|
|
var obj = stack.pop();
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
frame = throw_(obj, ctx);
|
|
stack = frame.stack;
|
|
cp = frame.cp;
|
|
break;
|
|
case 0xc2: // monitorenter
|
|
var obj = stack.pop();
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
ctx.monitorEnter(obj);
|
|
break;
|
|
case 0xc3: // monitorexit
|
|
var obj = stack.pop();
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
ctx.monitorExit(obj);
|
|
break;
|
|
case 0xc4: // wide
|
|
switch (op = frame.read8()) {
|
|
case 0x15: // iload
|
|
case 0x17: // fload
|
|
case 0x19: // aload
|
|
stack.push(frame.getLocal(frame.read16()));
|
|
break;
|
|
case 0x16: // lload
|
|
case 0x18: // dload
|
|
stack.push2(frame.getLocal(frame.read16()));
|
|
break;
|
|
case 0x36: // istore
|
|
case 0x38: // fstore
|
|
case 0x3a: // astore
|
|
frame.setLocal(frame.read16(), stack.pop());
|
|
break;
|
|
case 0x37: // lstore
|
|
case 0x39: // dstore
|
|
frame.setLocal(frame.read16(), stack.pop2());
|
|
break;
|
|
case 0x84: // iinc
|
|
var idx = frame.read16();
|
|
var val = frame.read16signed();
|
|
frame.setLocal(idx, frame.getLocal(idx) + val);
|
|
break;
|
|
case 0xa9: // ret
|
|
frame.ip = frame.getLocal(frame.read16());
|
|
break;
|
|
default:
|
|
var opName = OPCODES[op];
|
|
throw new Error("Wide opcode " + opName + " [" + op + "] not supported.");
|
|
}
|
|
break;
|
|
case 0xb6: // invokevirtual
|
|
case 0xb7: // invokespecial
|
|
case 0xb8: // invokestatic
|
|
case 0xb9: // invokeinterface
|
|
var startip = frame.ip - 1;
|
|
var idx = frame.read16();
|
|
if (op === 0xb9) {
|
|
var argsNumber = frame.read8();
|
|
var zero = frame.read8();
|
|
}
|
|
var isStatic = (op === 0xb8);
|
|
var methodInfo = cp[idx];
|
|
if (methodInfo.tag) {
|
|
methodInfo = resolve(ctx, cp, idx, isStatic);
|
|
if (isStatic)
|
|
classInitCheck(ctx, frame, methodInfo.classInfo, startip);
|
|
}
|
|
if (!isStatic) {
|
|
var obj = stack[stack.length - methodInfo.consumes];
|
|
if (!obj) {
|
|
ctx.raiseExceptionAndYield("java/lang/NullPointerException");
|
|
}
|
|
|
|
switch (op) {
|
|
case OPCODES.invokevirtual:
|
|
case OPCODES.invokeinterface:
|
|
if (methodInfo.classInfo != obj.class) {
|
|
// Check if the method is already in the virtual method cache
|
|
if (obj.class.vmc[methodInfo.key]) {
|
|
methodInfo = obj.class.vmc[methodInfo.key];
|
|
} else {
|
|
methodInfo = CLASSES.getMethod(obj.class, methodInfo.key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VM.DEBUG) {
|
|
VM.trace("invoke", ctx.thread.pid, methodInfo);
|
|
}
|
|
|
|
var alternateImpl = methodInfo.alternateImpl;
|
|
if (alternateImpl) {
|
|
Instrument.callPauseHooks(ctx.current());
|
|
Instrument.measure(alternateImpl, ctx, methodInfo);
|
|
Instrument.callResumeHooks(ctx.current());
|
|
break;
|
|
}
|
|
|
|
Instrument.callPauseHooks(frame);
|
|
if (!methodInfo.compiled && methodInfo.numCalled >= 100 && !methodInfo.dontCompile) {
|
|
try {
|
|
methodInfo.compiled = new Function("ctx", VM.compile(methodInfo, ctx));
|
|
} catch (e) {
|
|
methodInfo.dontCompile = true;
|
|
console.warn("Can't compile function: " + e);
|
|
}
|
|
}
|
|
Instrument.callResumeHooks(frame);
|
|
|
|
frame = pushFrame(ctx, methodInfo);
|
|
|
|
if (methodInfo.compiled) {
|
|
frame = methodInfo.compiled(ctx);
|
|
}
|
|
stack = frame.stack;
|
|
cp = frame.cp;
|
|
break;
|
|
case 0xb1: // return
|
|
if (VM.DEBUG) {
|
|
VM.trace("return", ctx.thread.pid, frame.methodInfo);
|
|
}
|
|
if (ctx.frames.length == 1)
|
|
return;
|
|
frame.methodInfo.numCalled++;
|
|
frame = popFrame(ctx, frame, 0);
|
|
// Return if the caller is compiled
|
|
if (frame.methodInfo.compiled) {
|
|
return frame;
|
|
}
|
|
stack = frame.stack;
|
|
cp = frame.cp;
|
|
break;
|
|
case 0xac: // ireturn
|
|
case 0xae: // freturn
|
|
case 0xb0: // areturn
|
|
if (VM.DEBUG) {
|
|
VM.trace("return", ctx.thread.pid, frame.methodInfo, stack[stack.length-1]);
|
|
}
|
|
if (ctx.frames.length == 1)
|
|
return;
|
|
frame.methodInfo.numCalled++;
|
|
frame = popFrame(ctx, frame, 1);
|
|
// Return if the caller is compiled
|
|
if (frame.methodInfo.compiled) {
|
|
return frame;
|
|
}
|
|
stack = frame.stack;
|
|
cp = frame.cp;
|
|
break;
|
|
case 0xad: // lreturn
|
|
case 0xaf: // dreturn
|
|
if (VM.DEBUG) {
|
|
VM.trace("return", ctx.thread.pid, frame.methodInfo, stack[stack.length-1]);
|
|
}
|
|
if (ctx.frames.length == 1)
|
|
return;
|
|
frame.methodInfo.numCalled++;
|
|
frame = popFrame(ctx, frame, 2);
|
|
// Return if the caller is compiled
|
|
if (frame.methodInfo.compiled) {
|
|
return frame;
|
|
}
|
|
stack = frame.stack;
|
|
cp = frame.cp;
|
|
break;
|
|
default:
|
|
var opName = OPCODES[op];
|
|
throw new Error("Opcode " + opName + " [" + op + "] not supported. (" + ctx.thread.pid + ")");
|
|
}
|
|
};
|
|
}
|
|
|
|
VM.compile = function(methodInfo, ctx, debugStmtEnabled) {
|
|
var depth = 0, maxDepth = 0;
|
|
var locals = 0, maxLocals = 0;
|
|
var ip = 0;
|
|
|
|
var frame = new Frame(methodInfo);
|
|
var cp = frame.cp;
|
|
|
|
var targetIPs = new Set([0]);
|
|
var stackLayout = new Map();
|
|
var generatedCases = [];
|
|
|
|
function generateStackPush(val) {
|
|
var gen = " S" + (depth++) + " = " + val + ";\n";
|
|
if (depth > maxDepth) {
|
|
maxDepth = depth;
|
|
}
|
|
return gen;
|
|
}
|
|
|
|
function generateStackPush2(val) {
|
|
var gen = " S" + (depth++) + " = " + val + ";\n S" + (depth++) + " = null;\n";
|
|
if (depth > maxDepth) {
|
|
maxDepth = depth;
|
|
}
|
|
return gen;
|
|
}
|
|
|
|
function generateStackPop() {
|
|
return "S" + (--depth);
|
|
}
|
|
|
|
function generateStackPop2() {
|
|
--depth;
|
|
return "S" + (--depth);
|
|
}
|
|
|
|
function generateSetLocal(num, val) {
|
|
if (num > maxLocals) {
|
|
maxLocals = num;
|
|
}
|
|
return " L" + num + " = " + val + ";\n";
|
|
}
|
|
|
|
function generateGetLocal(num) {
|
|
if (num > maxLocals) {
|
|
maxLocals = num;
|
|
}
|
|
return "L" + num;
|
|
}
|
|
|
|
function generateIf(jmp, cond) {
|
|
stackLayout.set(jmp, depth);
|
|
targetIPs.add(jmp);
|
|
return " if (" + cond + ") {\n\
|
|
ip = " + jmp + ";\n\
|
|
continue;\n\
|
|
}\n";
|
|
}
|
|
|
|
function generateStoreLocals() {
|
|
var gen = "";
|
|
for (var i = 0; i <= maxLocals; i++) {
|
|
gen += " frame.setLocal(" + i + ", L" + i + ");\n";
|
|
}
|
|
return gen;
|
|
}
|
|
|
|
function generateStoreState(ip) {
|
|
targetIPs.add(ip);
|
|
|
|
var gen = " frame.ip = " + ip + ";\n";
|
|
for (var i = 0; i < depth; i++) {
|
|
gen += " frame.stack.push(S" + i + ");\n";
|
|
}
|
|
gen += generateStoreLocals();
|
|
|
|
return gen + "\n";
|
|
}
|
|
|
|
function generateCheckArrayAccess(ip, idx, refArray) {
|
|
return "\
|
|
if (!" + refArray + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n\
|
|
if (" + idx + " < 0 || " + idx + " >= " + refArray + ".length) {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/ArrayIndexOutOfBoundsException', " + idx + ");\n\
|
|
}\n";
|
|
}
|
|
|
|
function resolveCompiled(cp, idx, isStatic) {
|
|
var constant = cp[idx];
|
|
|
|
if (!constant.tag)
|
|
return constant;
|
|
|
|
switch(constant.tag) {
|
|
case 3: // TAGS.CONSTANT_Integer
|
|
constant = constant.integer;
|
|
break;
|
|
case 4: // TAGS.CONSTANT_Float
|
|
constant = constant.float;
|
|
break;
|
|
case 8: // TAGS.CONSTANT_String
|
|
constant = util.newString(cp[constant.string_index].bytes);
|
|
break;
|
|
case 5: // TAGS.CONSTANT_Long
|
|
constant = Long.fromBits(constant.lowBits, constant.highBits);
|
|
break;
|
|
case 6: // TAGS.CONSTANT_Double
|
|
constant = constant.double;
|
|
break;
|
|
case 7: // TAGS.CONSTANT_Class
|
|
constant = CLASSES.getClass(cp[constant.name_index].bytes);
|
|
break;
|
|
case 9: // TAGS.CONSTANT_Fieldref
|
|
var classInfo = resolveCompiled(cp, constant.class_index, isStatic);
|
|
var fieldName = cp[cp[constant.name_and_type_index].name_index].bytes;
|
|
var signature = cp[cp[constant.name_and_type_index].signature_index].bytes;
|
|
constant = CLASSES.getField(classInfo, (isStatic ? "S." : "I.") + fieldName + "." + signature);
|
|
if (!constant) {
|
|
throw new Error("java/lang/RuntimeException");
|
|
}
|
|
break;
|
|
case 10: // TAGS.CONSTANT_Methodref
|
|
case 11: // TAGS.CONSTANT_InterfaceMethodref
|
|
var classInfo = resolveCompiled(cp, constant.class_index, isStatic);
|
|
var methodName = cp[cp[constant.name_and_type_index].name_index].bytes;
|
|
var signature = cp[cp[constant.name_and_type_index].signature_index].bytes;
|
|
constant = CLASSES.getMethod(classInfo, (isStatic ? "S." : "I.") + methodName + "." + signature);
|
|
if (!constant) {
|
|
throw new Error("java/lang/RuntimeException");
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error("not supported constant type");
|
|
}
|
|
|
|
return cp[idx] = constant;
|
|
}
|
|
|
|
var exception_table = frame.methodInfo.exception_table;
|
|
var handlerIPs = new Set();
|
|
for (var i = 0; exception_table && i < exception_table.length; i++) {
|
|
targetIPs.add(exception_table[i].handler_pc);
|
|
handlerIPs.add(exception_table[i].handler_pc);
|
|
// On handler IPs, an exception is put on the stack
|
|
stackLayout.set(exception_table[i].handler_pc, 1);
|
|
}
|
|
|
|
while (frame.ip !== frame.code.byteLength) {
|
|
ip = frame.ip;
|
|
|
|
var op = frame.read8();
|
|
|
|
var code = "";
|
|
|
|
if (debugStmtEnabled) {
|
|
code += " // " + OPCODES[op] + " [0x" + op.toString(16) + "]\n";
|
|
}
|
|
|
|
var newDepth = stackLayout.get(ip);
|
|
if (typeof newDepth !== "undefined") {
|
|
depth = newDepth;
|
|
}
|
|
|
|
switch (op) {
|
|
case 0x00: // nop
|
|
break;
|
|
case 0x01: // aconst_null
|
|
code += generateStackPush("null");
|
|
break;
|
|
case 0x02: // aconst_m1
|
|
code += generateStackPush(-1);
|
|
break;
|
|
case 0x03: // iconst_0
|
|
case 0x0b: // fconst_0
|
|
code += generateStackPush(0);
|
|
break;
|
|
case 0x0e: // dconst_0
|
|
code += generateStackPush2(0);
|
|
break;
|
|
case 0x04: // iconst_1
|
|
case 0x0c: // fconst_1
|
|
code += generateStackPush(1);
|
|
break;
|
|
case 0x0f: // dconst_1
|
|
code += generateStackPush2(1);
|
|
break;
|
|
case 0x05: // iconst_2
|
|
case 0x0d: // fconst_2
|
|
code += generateStackPush(2);
|
|
break;
|
|
case 0x06: // iconst_3
|
|
code += generateStackPush(3);
|
|
break;
|
|
case 0x07: // iconst_4
|
|
code += generateStackPush(4);
|
|
break;
|
|
case 0x08: // iconst_5
|
|
code += generateStackPush(5);
|
|
break;
|
|
case 0x09: // lconst_0
|
|
code += generateStackPush2("Long.fromInt(0)");
|
|
break;
|
|
case 0x0a: // lconst_1
|
|
code += generateStackPush2("Long.fromInt(1)");
|
|
break;
|
|
case 0x10: // bipush
|
|
code += generateStackPush(frame.read8signed());
|
|
break;
|
|
case 0x11: // sipush
|
|
code += generateStackPush(frame.read16signed());
|
|
break;
|
|
case 0x12: // ldc
|
|
case 0x13: // ldc_w
|
|
var idx = (op === 0x12) ? frame.read8() : frame.read16();
|
|
|
|
// Resolve the constant in advance.
|
|
var constant = cp[idx];
|
|
|
|
if (constant.tag) {
|
|
constant = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
code += generateStackPush("cp[" + idx + "]");
|
|
break;
|
|
case 0x14: // ldc2_w
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the constant in advance.
|
|
var constant = cp[idx];
|
|
if (constant.tag) {
|
|
constant = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
code += generateStackPush2("cp[" + idx + "]");
|
|
break;
|
|
case 0x15: // iload
|
|
case 0x17: // fload
|
|
case 0x19: // aload
|
|
code += generateStackPush(generateGetLocal(frame.read8()));
|
|
break;
|
|
case 0x16: // lload
|
|
case 0x18: // dload
|
|
code += generateStackPush2(generateGetLocal(frame.read8()));
|
|
break;
|
|
case 0x1a: // iload_0
|
|
case 0x22: // fload_0
|
|
case 0x2a: // aload_0
|
|
code += generateStackPush(generateGetLocal(0));
|
|
break;
|
|
case 0x1b: // iload_1
|
|
case 0x23: // fload_1
|
|
case 0x2b: // aload_1
|
|
code += generateStackPush(generateGetLocal(1));
|
|
break;
|
|
case 0x1c: // iload_2
|
|
case 0x24: // fload_2
|
|
case 0x2c: // aload_2
|
|
code += generateStackPush(generateGetLocal(2));
|
|
break;
|
|
case 0x1d: // iload_3
|
|
case 0x25: // fload_3
|
|
case 0x2d: // aload_3
|
|
code += generateStackPush(generateGetLocal(3));
|
|
break;
|
|
case 0x1e: // lload_0
|
|
case 0x26: // dload_0
|
|
code += generateStackPush2(generateGetLocal(0));
|
|
break;
|
|
case 0x1f: // lload_1
|
|
case 0x27: // dload_1
|
|
code += generateStackPush2(generateGetLocal(1));
|
|
break;
|
|
case 0x20: // lload_2
|
|
case 0x28: // dload_2
|
|
code += generateStackPush2(generateGetLocal(2));
|
|
break;
|
|
case 0x21: // lload_3
|
|
case 0x29: // dload_3
|
|
code += generateStackPush2(generateGetLocal(3));
|
|
break;
|
|
case 0x2e: // iaload
|
|
case 0x30: // faload
|
|
case 0x32: // aaload
|
|
case 0x33: // baload
|
|
case 0x34: // caload
|
|
case 0x35: // saload
|
|
var idx = generateStackPop();
|
|
var refArray = generateStackPop();
|
|
code += generateCheckArrayAccess(ip, idx, refArray) +
|
|
generateStackPush(refArray + "[" + idx + "]");
|
|
break;
|
|
case 0x2f: // laload
|
|
case 0x31: // daload
|
|
var idx = generateStackPop();
|
|
var refArray = generateStackPop();
|
|
code += generateCheckArrayAccess(ip, idx, refArray) +
|
|
generateStackPush2(refArray + "[" + idx + "]");
|
|
break;
|
|
case 0x36: // istore
|
|
case 0x38: // fstore
|
|
case 0x3a: // astore
|
|
code += generateSetLocal(frame.read8(), generateStackPop());
|
|
break;
|
|
case 0x37: // lstore
|
|
case 0x39: // dstore
|
|
code += generateSetLocal(frame.read8(), generateStackPop2());
|
|
break;
|
|
case 0x3b: // istore_0
|
|
case 0x43: // fstore_0
|
|
case 0x4b: // astore_0
|
|
code += generateSetLocal(0, generateStackPop());
|
|
break;
|
|
case 0x3c: // istore_1
|
|
case 0x44: // fstore_1
|
|
case 0x4c: // astore_1
|
|
code += generateSetLocal(1, generateStackPop());
|
|
break;
|
|
case 0x3d: // istore_2
|
|
case 0x45: // fstore_2
|
|
case 0x4d: // astore_2
|
|
code += generateSetLocal(2, generateStackPop());
|
|
break;
|
|
case 0x3e: // istore_3
|
|
case 0x46: // fstore_3
|
|
case 0x4e: // astore_3
|
|
code += generateSetLocal(3, generateStackPop());
|
|
break;
|
|
case 0x3f: // lstore_0
|
|
case 0x47: // dstore_0
|
|
code += generateSetLocal(0, generateStackPop2());
|
|
break;
|
|
case 0x40: // lstore_1
|
|
case 0x48: // dstore_1
|
|
code += generateSetLocal(1, generateStackPop2());
|
|
break;
|
|
case 0x41: // lstore_2
|
|
case 0x49: // dstore_2
|
|
code += generateSetLocal(2, generateStackPop2());
|
|
break;
|
|
case 0x42: // lstore_3
|
|
case 0x4a: // dstore_3
|
|
code += generateSetLocal(3, generateStackPop2());
|
|
break;
|
|
case 0x4f: // iastore
|
|
case 0x51: // fastore
|
|
case 0x54: // bastore
|
|
case 0x55: // castore
|
|
case 0x56: // sastore
|
|
var val = generateStackPop();
|
|
var idx = generateStackPop();
|
|
var refArray = generateStackPop();
|
|
code += generateCheckArrayAccess(ip, idx, refArray) + "\
|
|
" + refArray + "[" + idx + "] = " + val + ";\n";
|
|
break;
|
|
case 0x50: // lastore
|
|
case 0x52: // dastore
|
|
var val = generateStackPop2();
|
|
var idx = generateStackPop();
|
|
var refArray = generateStackPop();
|
|
code += generateCheckArrayAccess(ip, idx, refArray) + "\
|
|
" + refArray + "[" + idx + "] = " + val + ";\n";
|
|
break;
|
|
case 0x53: // aastore
|
|
var val = generateStackPop();
|
|
var idx = generateStackPop();
|
|
var refArray = generateStackPop();
|
|
code += generateCheckArrayAccess(ip, idx, refArray) + "\
|
|
if (" + val + " && !" + val + ".class.isAssignableTo(" + refArray + ".class.elementClass)) {\n\
|
|
ctx.raiseExceptionAndYield('java/lang/ArrayStoreException');\n\
|
|
}\n\
|
|
" + refArray + "[" + idx + "] = " + val + ";\n";
|
|
break;
|
|
case 0x57: // pop
|
|
depth--;
|
|
break;
|
|
case 0x58: // pop2
|
|
depth -= 2;
|
|
break;
|
|
case 0x59: // dup
|
|
code += generateStackPush("S" + (depth - 1));
|
|
break;
|
|
case 0x5a: // dup_x1
|
|
var a = generateStackPop();
|
|
var b = generateStackPop();
|
|
code += "var tmp = " + b + "\n" +
|
|
generateStackPush(a) +
|
|
generateStackPush("tmp") +
|
|
generateStackPush(b);
|
|
break;
|
|
case 0x5b: // dup_x2
|
|
code += "\
|
|
var a = " + generateStackPop() + ";\n\
|
|
var b = " + generateStackPop() + ";\n\
|
|
var c = " + generateStackPop() + ";\n";
|
|
code += generateStackPush("a") +
|
|
generateStackPush("c") +
|
|
generateStackPush("b") +
|
|
generateStackPush("a");
|
|
break;
|
|
case 0x5c: // dup2
|
|
code += "\
|
|
var a = " + generateStackPop() + ";\n\
|
|
var b = " + generateStackPop() + ";\n";
|
|
code += generateStackPush("b") +
|
|
generateStackPush("a") +
|
|
generateStackPush("b") +
|
|
generateStackPush("a");
|
|
break;
|
|
case 0x5d: // dup2_x1
|
|
code += "\
|
|
var a = " + generateStackPop() + ";\n\
|
|
var b = " + generateStackPop() + ";\n\
|
|
var c = " + generateStackPop() + ";\n";
|
|
code += generateStackPush("b") +
|
|
generateStackPush("a") +
|
|
generateStackPush("c") +
|
|
generateStackPush("b") +
|
|
generateStackPush("a");
|
|
break;
|
|
case 0x5e: // dup2_x2
|
|
code += "\
|
|
var a = " + generateStackPop() + ";\n\
|
|
var b = " + generateStackPop() + ";\n\
|
|
var c = " + generateStackPop() + ";\n\
|
|
var d = " + generateStackPop() + ";\n";
|
|
code += generateStackPush("b") +
|
|
generateStackPush("a") +
|
|
generateStackPush("d") +
|
|
generateStackPush("c") +
|
|
generateStackPush("b") +
|
|
generateStackPush("a");
|
|
break;
|
|
case 0x5f: // swap
|
|
var a = generateStackPop();
|
|
var b = generateStackPop();
|
|
code += generateStackPush(a) +
|
|
generateStackPush(b);
|
|
break;
|
|
case 0x84: // iinc
|
|
var idx = frame.read8();
|
|
var val = frame.read8signed();
|
|
code += generateSetLocal(idx, generateGetLocal(idx) + " + " + val);
|
|
break;
|
|
case 0x60: // iadd
|
|
code += generateStackPush("(" + generateStackPop() + " + " + generateStackPop() + ") | 0");
|
|
break;
|
|
case 0x61: // ladd
|
|
code += generateStackPush2(generateStackPop2() + ".add(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x62: // fadd
|
|
code += generateStackPush("Math.fround(" + generateStackPop() + " + " + generateStackPop() + ")");
|
|
break;
|
|
case 0x63: // dadd
|
|
code += generateStackPush2(generateStackPop2() + " + " + generateStackPop2());
|
|
break;
|
|
case 0x64: // isub
|
|
code += generateStackPush("(-" + generateStackPop() + " + " + generateStackPop() + ") | 0");
|
|
break;
|
|
case 0x65: // lsub
|
|
code += generateStackPush2(generateStackPop2() + ".negate().add(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x66: // fsub
|
|
code += generateStackPush("Math.fround(-" + generateStackPop() + " + " + generateStackPop() + ")");
|
|
break;
|
|
case 0x67: // dsub
|
|
code += generateStackPush2("-" + generateStackPop2() + " + " + generateStackPop2());
|
|
break;
|
|
case 0x68: // imul
|
|
code += generateStackPush("Math.imul(" + generateStackPop() + ", " + generateStackPop() + ")");
|
|
break;
|
|
case 0x69: // lmul
|
|
code += generateStackPush2(generateStackPop2() + ".multiply(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x6a: // fmul
|
|
code += generateStackPush("Math.fround(" + generateStackPop() + " * " + generateStackPop() + ")");
|
|
break;
|
|
case 0x6b: // dmul
|
|
code += generateStackPush2(generateStackPop2() + " * " + generateStackPop2());
|
|
break;
|
|
case 0x6c: // idiv
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += "\
|
|
if (!" + b + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/ArithmeticException', '/ by zero');\n\
|
|
}\n";
|
|
code += generateStackPush("(" + a + " === util.INT_MIN && " + b + " === -1) ? " + a + " : ((" + a + " / " + b + ")|0)");
|
|
break;
|
|
case 0x6d: // ldiv
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
code += "\
|
|
if (" + b + ".isZero()) {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/ArithmeticException', '/ by zero');\n\
|
|
}\n";
|
|
code += generateStackPush2(a + ".div(" + b + ")");
|
|
break;
|
|
case 0x6e: // fdiv
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += generateStackPush("Math.fround(" + a + " / " + b + ")");
|
|
break;
|
|
case 0x6f: // ddiv
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
code += generateStackPush2(a + " / " + b);
|
|
break;
|
|
case 0x70: // irem
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += "\
|
|
if (!" + b + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/ArithmeticException', '/ by zero');\n\
|
|
}\n";
|
|
code += generateStackPush(a + " % " + b);
|
|
break;
|
|
case 0x71: // lrem
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
code += "\
|
|
if (" + b + ".isZero()) {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/ArithmeticException', '/ by zero');\n\
|
|
}\n";
|
|
code += generateStackPush2(a + ".modulo(" + b + ")");
|
|
break;
|
|
case 0x72: // frem
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += generateStackPush("Math.fround(" + a + " % " + b + ")");
|
|
break;
|
|
case 0x73: // drem
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
code += generateStackPush2(a + " % " + b);
|
|
break;
|
|
case 0x74: // ineg
|
|
code += generateStackPush("(-" + generateStackPop() + ") | 0");
|
|
break;
|
|
case 0x75: // lneg
|
|
code += generateStackPush2(generateStackPop2() + ".negate()");
|
|
break;
|
|
case 0x76: // fneg
|
|
code += generateStackPush("-" + generateStackPop());
|
|
break;
|
|
case 0x77: // dneg
|
|
code += generateStackPush2("-" + generateStackPop2());
|
|
break;
|
|
case 0x78: // ishl
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += generateStackPush(a + " << " + b);
|
|
break;
|
|
case 0x79: // lshl
|
|
var b = generateStackPop();
|
|
var a = generateStackPop2();
|
|
code += generateStackPush2(a + ".shiftLeft(" + b + ")");
|
|
break;
|
|
case 0x7a: // ishr
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += generateStackPush(a + " >> " + b);
|
|
break;
|
|
case 0x7b: // lshr
|
|
var b = generateStackPop();
|
|
var a = generateStackPop2();
|
|
code += generateStackPush2(a + ".shiftRight(" + b + ")");
|
|
break;
|
|
case 0x7c: // iushr
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
code += generateStackPush(a + " >>> " + b);
|
|
break;
|
|
case 0x7d: // lushr
|
|
var b = generateStackPop();
|
|
var a = generateStackPop2();
|
|
code += generateStackPush2(a + ".shiftRightUnsigned(" + b + ")");
|
|
break;
|
|
case 0x7e: // iand
|
|
code += generateStackPush(generateStackPop() + " & " + generateStackPop());
|
|
break;
|
|
case 0x7f: // land
|
|
code += generateStackPush2(generateStackPop2() + ".and(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x80: // ior
|
|
code += generateStackPush(generateStackPop() + " | " + generateStackPop());
|
|
break;
|
|
case 0x81: // lor
|
|
code += generateStackPush2(generateStackPop2() + ".or(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x82: // ixor
|
|
code += generateStackPush(generateStackPop() + " ^ " + generateStackPop());
|
|
break;
|
|
case 0x83: // lxor
|
|
code += generateStackPush2(generateStackPop2() + ".xor(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x94: // lcmp
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
|
|
code += "\
|
|
if (" + a + ".greaterThan(" + b + ")) {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + ".lessThan(" + b + ")) {\n\
|
|
S" + depth + " = -1;\n\
|
|
} else {\n\
|
|
S" + depth + " = 0;\n\
|
|
}\n";
|
|
|
|
depth += 1;
|
|
|
|
break;
|
|
case 0x95: // fcmpl
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
|
|
code += "\
|
|
if (isNaN(" + a + ") || isNaN(" + b + ")) {\n\
|
|
S" + depth + " = -1;\n\
|
|
} else if (" + a + " > " + b + ") {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + " < " + b + "){\n\
|
|
S" + depth + " = -1;\n\
|
|
} else {\n\
|
|
S" + depth + " = 0;\n\
|
|
}\n";
|
|
|
|
depth += 1;
|
|
|
|
break;
|
|
case 0x96: // fcmpg
|
|
var b = generateStackPop();
|
|
var a = generateStackPop();
|
|
|
|
code += "\
|
|
if (isNaN(" + a + ") || isNaN(" + b + ")) {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + " > " + b + ") {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + " < " + b + "){\n\
|
|
S" + depth + " = -1;\n\
|
|
} else {\n\
|
|
S" + depth + " = 0;\n\
|
|
}\n";
|
|
|
|
depth += 1;
|
|
|
|
break;
|
|
case 0x97: // dcmpl
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
|
|
code += "\
|
|
if (isNaN(" + a + ") || isNaN(" + b + ")) {\n\
|
|
S" + depth + " = -1;\n\
|
|
} else if (" + a + " > " + b + ") {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + " < " + b + "){\n\
|
|
S" + depth + " = -1;\n\
|
|
} else {\n\
|
|
S" + depth + " = 0;\n\
|
|
}\n";
|
|
|
|
depth += 1;
|
|
|
|
break;
|
|
case 0x98: // dcmpg
|
|
var b = generateStackPop2();
|
|
var a = generateStackPop2();
|
|
|
|
code += "\
|
|
if (isNaN(" + a + ") || isNaN(" + b + ")) {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + " > " + b + ") {\n\
|
|
S" + depth + " = 1;\n\
|
|
} else if (" + a + " < " + b + "){\n\
|
|
S" + depth + " = -1;\n\
|
|
} else {\n\
|
|
S" + depth + " = 0;\n\
|
|
}\n";
|
|
|
|
depth += 1;
|
|
|
|
break;
|
|
case 0x99: // ifeq
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " === " + 0);
|
|
break;
|
|
case 0x9a: // ifne
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " !== " + 0);
|
|
break;
|
|
case 0x9b: // iflt
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " < " + 0);
|
|
break;
|
|
case 0x9c: // ifge
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " >= " + 0);
|
|
break;
|
|
case 0x9d: // ifgt
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " > " + 0);
|
|
break;
|
|
case 0x9e: // ifle
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " <= " + 0);
|
|
break;
|
|
case 0x9f: // if_icmpeq
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " === " + generateStackPop());
|
|
break;
|
|
case 0xa0: // if_cmpne
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " !== " + generateStackPop());
|
|
break;
|
|
case 0xa1: // if_icmplt
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " > " + generateStackPop());
|
|
break;
|
|
case 0xa2: // if_icmpge
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " <= " + generateStackPop());
|
|
break;
|
|
case 0xa3: // if_icmpgt
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " < " + generateStackPop());
|
|
break;
|
|
case 0xa4: // if_icmple
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " >= " + generateStackPop());
|
|
break;
|
|
case 0xa5: // if_acmpeq
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " === " + generateStackPop());
|
|
break;
|
|
case 0xa6: // if_acmpne
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop() + " !== " + generateStackPop());
|
|
break;
|
|
case 0xc6: // ifnull
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, "!" + generateStackPop());
|
|
break;
|
|
case 0xc7: // ifnonnull
|
|
var jmp = ip + frame.read16signed();
|
|
code += generateIf(jmp, generateStackPop());
|
|
break;
|
|
case 0xa7: // goto
|
|
var target = ip + frame.read16signed();
|
|
code += " ip = " + target + ";\n continue;\n";
|
|
targetIPs.add(target);
|
|
stackLayout.set(target, depth);
|
|
break;
|
|
case 0xc8: // goto_w
|
|
var target = ip + frame.read32signed();
|
|
code += " ip = " + target + ";\n continue;\n";
|
|
targetIPs.add(target);
|
|
stackLayout.set(target, depth);
|
|
break;
|
|
case 0xa8: // jsr
|
|
var jmp = frame.read16();
|
|
targetIPs.add(frame.ip); // ret will return here
|
|
code += generateStackPush(ip) + "\n\
|
|
ip = " + jmp + ";\n\
|
|
continue;\n";
|
|
targetIPs.add(jmp);
|
|
stackLayout.set(jmp, depth);
|
|
stackLayout.set(frame.ip, depth);
|
|
break;
|
|
case 0xc9: // jsr_w
|
|
var jmp = frame.read32();
|
|
targetIPs.add(frame.ip); // ret will return here
|
|
code += generateStackPush(ip) + "\n\
|
|
ip = " + jmp + ";\n\
|
|
continue;\n";
|
|
targetIPs.add(jmp);
|
|
stackLayout.set(jmp, depth);
|
|
stackLayout.set(frame.ip, depth);
|
|
break;
|
|
case 0xa9: // ret
|
|
code += " ip = " + generateGetLocal(frame.read8()) + ";\n continue;\n";
|
|
break;
|
|
case 0x85: // i2l
|
|
code += generateStackPush2("Long.fromInt(" + generateStackPop() + ")");
|
|
break;
|
|
case 0x86: // i2f
|
|
break;
|
|
case 0x87: // i2d
|
|
code += generateStackPush2(generateStackPop());
|
|
break;
|
|
case 0x88: // l2i
|
|
code += generateStackPush(generateStackPop2() + ".toInt()");
|
|
break;
|
|
case 0x89: // l2f
|
|
code += generateStackPush("Math.fround(" + generateStackPop2() + ".toNumber())");
|
|
break;
|
|
case 0x8a: // l2d
|
|
code += generateStackPush2(generateStackPop2() + ".toNumber()");
|
|
break;
|
|
case 0x8b: // f2i
|
|
code += generateStackPush("util.double2int(" + generateStackPop() + ")")
|
|
break;
|
|
case 0x8c: // f2l
|
|
code += generateStackPush2("Long.fromNumber(" + generateStackPop() + ")")
|
|
break;
|
|
case 0x8d: // f2d
|
|
code += generateStackPush2(generateStackPop());
|
|
break;
|
|
case 0x8e: // d2i
|
|
code += generateStackPush("util.double2int(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x8f: // d2l
|
|
code += generateStackPush2("util.double2long(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x90: // d2f
|
|
code += generateStackPush("Math.fround(" + generateStackPop2() + ")");
|
|
break;
|
|
case 0x91: // i2b
|
|
code += generateStackPush("(" + generateStackPop() + " << 24) >> 24");
|
|
break;
|
|
case 0x92: // i2c
|
|
code += generateStackPush(generateStackPop() + " & 0xffff");
|
|
break;
|
|
case 0x93: // i2s
|
|
code += generateStackPush("(" + generateStackPop() + " << 16) >> 16");
|
|
break;
|
|
case 0xaa: // tableswitch TODO: ADD STACKLAYOUT STUFF
|
|
var startip = frame.ip - 1;
|
|
|
|
while ((frame.ip & 3) != 0) {
|
|
frame.ip++;
|
|
}
|
|
|
|
var def = frame.read32signed() + startip;
|
|
var low = frame.read32signed();
|
|
var high = frame.read32signed();
|
|
|
|
var numJumpOffsets = high - low + 1;
|
|
|
|
code += "\
|
|
var offsets = new Int32Array(" + numJumpOffsets + ");\n";
|
|
for (var i = 0; i < numJumpOffsets; i++) {
|
|
var jmp = frame.read32signed() + startip;
|
|
code += " offsets[" + i + "] = " + jmp + ";\n";
|
|
targetIPs.add(jmp);
|
|
}
|
|
|
|
targetIPs.add(def);
|
|
|
|
var val = generateStackPop();
|
|
|
|
code += "\
|
|
var jmp;\n\
|
|
if (" + val + " < " + low + " || " + val + " > " + high + ") {\n\
|
|
ip = " + def + ";\n\
|
|
} else {\n\
|
|
ip = offsets[" + val + " - " + low + "];\n\
|
|
}\n\
|
|
continue;\n";
|
|
break;
|
|
case 0xab: // lookupswitch TODO: ADD STACKLAYOUT STUFF
|
|
var startip = frame.ip - 1;
|
|
|
|
while ((frame.ip & 3) != 0) {
|
|
frame.ip++;
|
|
}
|
|
|
|
var def = frame.read32signed() + startip;
|
|
var size = frame.read32();
|
|
|
|
code += "\
|
|
var offsets = {};\n";
|
|
for (var i = 0; i < size; i++) {
|
|
var key = frame.read32signed();
|
|
var jmp = frame.read32signed() + startip;
|
|
code += " offsets[" + key + "] = " + jmp + ";\n";
|
|
targetIPs.add(jmp);
|
|
}
|
|
|
|
targetIPs.add(def);
|
|
|
|
var val = generateStackPop();
|
|
|
|
code += "\
|
|
var jmp = offsets[" + val + "];\n\
|
|
if (!jmp) {\n\
|
|
jmp = " + def + "\n\
|
|
}\n\
|
|
ip = jmp;\n\
|
|
continue;\n";
|
|
break;
|
|
case 0xbc: // newarray
|
|
var type = "????ZCFDBSIJ"[frame.read8()];
|
|
var size = generateStackPop();
|
|
code += "\
|
|
if (" + size + " < 0) {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NegativeArraySizeException', " + size + ");\n\
|
|
}\n" + generateStackPush("util.newPrimitiveArray('" + type + "', " + size + ")");
|
|
break;
|
|
case 0xbd: // anewarray
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the classinfo in advance.
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag) {
|
|
classInfo = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
var className = classInfo.className;
|
|
if (className[0] !== "[") {
|
|
className = "L" + className + ";";
|
|
}
|
|
className = "[" + className;
|
|
|
|
var size = generateStackPop();
|
|
|
|
code += "\
|
|
if (" + size + " < 0) {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NegativeArraySizeException', " + size + ");\n\
|
|
}\n" + generateStackPush("util.newArray('" + className + "', " + size + ")");
|
|
break;
|
|
case 0xc5: // multianewarray
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the classinfo in advance.
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag) {
|
|
classInfo = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
var dimensions = frame.read8();
|
|
|
|
code += " var lengths = new Array(" + dimensions + ");\n";
|
|
for (var i = 0; i < dimensions; i++) {
|
|
code += " lengths[" + i + "] = " + generateStackPop() + ";\n";
|
|
}
|
|
code += generateStackPush("util.newMultiArray('" + classInfo.className + "', lengths.reverse())");
|
|
break;
|
|
case 0xbe: // arraylength
|
|
var obj = generateStackPop();
|
|
|
|
code += "\
|
|
if (!" + obj + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n";
|
|
code += generateStackPush(obj + ".length");
|
|
break;
|
|
case 0xb4: // getfield
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the field in advance.
|
|
var field = cp[idx];
|
|
if (field && field.tag) {
|
|
field = resolveCompiled(cp, idx, false);
|
|
}
|
|
|
|
var obj = generateStackPop();
|
|
|
|
code += "\
|
|
if (!" + obj + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n";
|
|
|
|
if (field.signature === "J" || field.signature === "D") {
|
|
code += generateStackPush2("cp[" + idx + "].get(" + obj + ")");
|
|
} else {
|
|
code += generateStackPush("cp[" + idx + "].get(" + obj + ")");
|
|
}
|
|
break;
|
|
case 0xb5: // putfield
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the field in advance.
|
|
var field = cp[idx];
|
|
if (field && field.tag) {
|
|
field = resolveCompiled(cp, idx, false);
|
|
}
|
|
|
|
var val;
|
|
if (field.signature === "J" || field.signature === "D") {
|
|
val = generateStackPop2();
|
|
} else {
|
|
val = generateStackPop();
|
|
}
|
|
|
|
var obj = generateStackPop();
|
|
|
|
code += "\
|
|
if (!" + obj + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n\
|
|
cp[" + idx + "].set(" + obj + ", " + val + ");\n"
|
|
break;
|
|
case 0xb2: // getstatic
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the field in advance.
|
|
var field = cp[idx];
|
|
if (field && field.tag) {
|
|
field = resolveCompiled(cp, idx, true);
|
|
}
|
|
|
|
code += "\
|
|
var field = cp[" + idx + "];\n";
|
|
|
|
if (!field.classInfo.isArrayClass) {
|
|
code +="\
|
|
if (!ctx.runtime.initialized[field.classInfo.className]) {\n" +
|
|
generateStoreState(ip) + "\
|
|
ctx.pushClassInitFrame(field.classInfo);\n\
|
|
throw VM.Yield;\n\
|
|
}\n";
|
|
}
|
|
|
|
code += "\
|
|
var value = ctx.runtime.getStatic(field);\n\
|
|
if (typeof value === 'undefined') {\n\
|
|
value = util.defaultValue(field.signature);\n\
|
|
}\n";
|
|
|
|
if (field.signature === "J" || field.signature === "D") {
|
|
code += generateStackPush2("value");
|
|
} else {
|
|
code += generateStackPush("value");
|
|
}
|
|
break;
|
|
case 0xb3: // putstatic
|
|
var idx = frame.read16();
|
|
|
|
// Resolve the field in advance.
|
|
var field = cp[idx];
|
|
if (field.tag) {
|
|
field = resolveCompiled(cp, idx, true);
|
|
}
|
|
|
|
code += "\
|
|
var field = cp[" + idx + "];\n";
|
|
|
|
if (!field.classInfo.isArrayClass) {
|
|
code +="\
|
|
if (!ctx.runtime.initialized[field.classInfo.className]) {\n" +
|
|
generateStoreState(ip) + "\
|
|
ctx.pushClassInitFrame(field.classInfo);\n\
|
|
throw VM.Yield;\n\
|
|
}\n";
|
|
}
|
|
|
|
var val;
|
|
if (field.signature === "J" || field.signature === "D") {
|
|
val = generateStackPop2();
|
|
} else {
|
|
val = generateStackPop();
|
|
}
|
|
|
|
code += "\
|
|
ctx.runtime.setStatic(field, " + val + ");\n";
|
|
break;
|
|
case 0xbb: // new
|
|
var idx = frame.read16();
|
|
|
|
// Resolve class in advance.
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag) {
|
|
classInfo = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
code += "\
|
|
var classInfo = cp[" + idx + "];\n";
|
|
|
|
if (!classInfo.isArrayClass) {
|
|
code +="\
|
|
if (!ctx.runtime.initialized[classInfo.className]) {\n" +
|
|
generateStoreState(ip) + "\
|
|
ctx.pushClassInitFrame(classInfo);\n\
|
|
throw VM.Yield;\n\
|
|
}\n";
|
|
}
|
|
|
|
code += generateStackPush("util.newObject(classInfo)");
|
|
break;
|
|
case 0xc0: // checkcast
|
|
var idx = frame.read16();
|
|
|
|
// Resolve class in advance.
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag) {
|
|
classInfo = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
var obj = "S" + (depth-1);
|
|
|
|
code += "\
|
|
var classInfo = cp[" + idx + "];\n\
|
|
if (" + obj + ") {\n\
|
|
if (!" + obj + ".class.isAssignableTo(classInfo)) {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/ClassCastException',\n\
|
|
" + obj + ".class.className + ' is not assignable to ' +\n\
|
|
classInfo.className);\n\
|
|
}\n\
|
|
}\n";
|
|
break;
|
|
case 0xc1: // instanceof
|
|
var idx = frame.read16();
|
|
|
|
// Resolve class in advance.
|
|
var classInfo = cp[idx];
|
|
if (classInfo.tag) {
|
|
classInfo = resolveCompiled(cp, idx);
|
|
}
|
|
|
|
var obj = generateStackPop();
|
|
|
|
code += "\
|
|
var classInfo = cp[" + idx + "];\n\
|
|
var result = !" + obj + " ? false : " + obj + ".class.isAssignableTo(classInfo);\n" +
|
|
generateStackPush("result ? 1 : 0");
|
|
break;
|
|
case 0xbf: // athrow
|
|
var obj = generateStackPop();
|
|
|
|
code += generateStoreState(ip) + "\
|
|
if (!" + obj + ") {\n\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n" + "\
|
|
return throw_(" + obj + ", ctx);\n";
|
|
break;
|
|
case 0xc2: // monitorenter
|
|
var obj = generateStackPop();
|
|
|
|
code += "\
|
|
if (!" + obj + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n\
|
|
try {\n\
|
|
ctx.monitorEnter(" + obj + ");\n\
|
|
}\n\
|
|
catch (e) {\n\
|
|
" + generateStoreState(frame.ip) + "\
|
|
throw e;\n\
|
|
}\n";
|
|
break;
|
|
case 0xc3: // monitorexit
|
|
var obj = generateStackPop();
|
|
|
|
code += "\
|
|
if (!" + obj + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n\
|
|
ctx.monitorExit(" + obj + ");\n";
|
|
break;
|
|
case 0xc4: // wide
|
|
var op = frame.read8();
|
|
var opName = OPCODES[op];
|
|
code += "// " + opName + " [0x" + op.toString(16) + "]\n";
|
|
switch (op) {
|
|
case 0x15: // iload
|
|
case 0x17: // fload
|
|
case 0x19: // aload
|
|
code += generateStackPush(generateGetLocal(frame.read16()));
|
|
break;
|
|
case 0x16: // lload
|
|
case 0x18: // dload
|
|
code += generateStackPush2(generateGetLocal(frame.read16()));
|
|
break;
|
|
case 0x36: // istore
|
|
case 0x38: // fstore
|
|
case 0x3a: // astore
|
|
code += generateSetLocal(frame.read16(), generateStackPop());
|
|
break;
|
|
case 0x37: // lstore
|
|
case 0x39: // dstore
|
|
code += generateSetLocal(frame.read16(), generateStackPop2());
|
|
break;
|
|
case 0x84: // iinc
|
|
var idx = frame.read16();
|
|
var val = frame.read16signed();
|
|
code += generateSetLocal(idx, generateGetLocal(idx) + " + " + val);
|
|
break;
|
|
case 0xa9: // ret
|
|
code += " ip = " + generateGetLocal(frame.read16()) + ";\n continue;\n";
|
|
continue;
|
|
break;
|
|
default:
|
|
var opName = OPCODES[op];
|
|
throw new Error("Wide opcode " + opName + " [" + op + "] not supported.");
|
|
}
|
|
break;
|
|
case 0xb6: // invokevirtual
|
|
case 0xb7: // invokespecial
|
|
case 0xb8: // invokestatic
|
|
case 0xb9: // invokeinterface
|
|
var idx = frame.read16();
|
|
if (op === 0xb9) {
|
|
/*var argsNumber =*/ frame.read8();
|
|
/*var zero =*/ frame.read8();
|
|
}
|
|
var isStatic = (op === 0xb8);
|
|
var toCallMethodInfo = cp[idx];
|
|
// Resolve method in advance.
|
|
if (toCallMethodInfo.tag) {
|
|
toCallMethodInfo = resolveCompiled(cp, idx, isStatic);
|
|
}
|
|
|
|
code += " var toCallMethodInfo = cp[" + idx + "];\n";
|
|
|
|
if (isStatic && !toCallMethodInfo.classInfo.isArrayClass) {
|
|
code +="\
|
|
if (!ctx.runtime.initialized[toCallMethodInfo.classInfo.className]) {\n" +
|
|
generateStoreState(ip) + "\
|
|
ctx.pushClassInitFrame(toCallMethodInfo.classInfo);\n\
|
|
throw VM.Yield;\n\
|
|
}\n";
|
|
}
|
|
|
|
if (!isStatic) {
|
|
var obj = "S" + (depth - toCallMethodInfo.consumes);
|
|
|
|
code += "\
|
|
if (!" + obj + ") {\n\
|
|
frame.ip = " + ip + "\n" +
|
|
generateStoreLocals() + "\
|
|
ctx.raiseExceptionAndYield('java/lang/NullPointerException');\n\
|
|
}\n";
|
|
|
|
if (op === 0xb6 || op === 0xb9) {
|
|
code += "\
|
|
if (toCallMethodInfo.classInfo != " + obj + ".class) {\n\
|
|
if (" + obj + ".class.vmc['" + toCallMethodInfo.key + "']) {\n\
|
|
toCallMethodInfo = " + obj + ".class.vmc['" + toCallMethodInfo.key + "'];\n\
|
|
} else {\n\
|
|
toCallMethodInfo = CLASSES.getMethod(" + obj + ".class, '" + toCallMethodInfo.key + "');\n\
|
|
}\n\
|
|
}\n";
|
|
}
|
|
}
|
|
|
|
code += generateStoreState(frame.ip);
|
|
|
|
depth -= toCallMethodInfo.consumes;
|
|
|
|
var alternateImplCall = "\
|
|
Instrument.callPauseHooks(frame);\n\
|
|
Instrument.measure(alternateImpl, ctx, toCallMethodInfo);\n\
|
|
Instrument.callResumeHooks(frame);\n";
|
|
|
|
var normalCall = "\
|
|
Instrument.callPauseHooks(frame);\n\
|
|
if (!toCallMethodInfo.compiled && toCallMethodInfo.numCalled >= 100 && !toCallMethodInfo.dontCompile) {\n\
|
|
try {\n\
|
|
toCallMethodInfo.compiled = new Function('ctx', VM.compile(toCallMethodInfo, ctx));\n\
|
|
} catch (e) {\n\
|
|
toCallMethodInfo.dontCompile = true;\n\
|
|
console.warn('Can\\'t compile function: ' + e);\n\
|
|
}\n\
|
|
}\n\
|
|
Instrument.callResumeHooks(frame);\n\
|
|
\n\
|
|
var callee = ctx.pushFrame(toCallMethodInfo);\n";
|
|
if (toCallMethodInfo.isSynchronized) {
|
|
normalCall += "\
|
|
if (!callee.lockObject) {\n\
|
|
callee.lockObject = " + (toCallMethodInfo.isStatic
|
|
? "toCallMethodInfo.classInfo.getClassObject(ctx)\n"
|
|
: "callee.getLocal(0);\n") + "\
|
|
}\n\
|
|
ctx.monitorEnter(callee.lockObject);\n";
|
|
}
|
|
|
|
normalCall += "\n\
|
|
var newFrame;\n\
|
|
\n\
|
|
if (toCallMethodInfo.compiled) {\n\
|
|
newFrame = toCallMethodInfo.compiled(ctx);\n\
|
|
} else {\n\
|
|
newFrame = VM.execute(ctx);\n\
|
|
}\n\
|
|
\n\
|
|
if (newFrame !== frame) {\n\
|
|
return newFrame;\n\
|
|
}\n";
|
|
|
|
if (op === 0xb6 || op == 0xb9) {
|
|
code += "\
|
|
var alternateImpl = toCallMethodInfo.alternateImpl;\n\
|
|
if (alternateImpl) {\n" +
|
|
alternateImplCall + "\
|
|
} else {\n" +
|
|
normalCall + "\
|
|
}\n";
|
|
} else if (toCallMethodInfo.alternateImpl) {
|
|
code += "\
|
|
var alternateImpl = toCallMethodInfo.alternateImpl;\n" + alternateImplCall;
|
|
} else {
|
|
code += normalCall;
|
|
}
|
|
|
|
var returnType = toCallMethodInfo.signature[toCallMethodInfo.signature.length - 1];
|
|
if (returnType === 'J' || returnType === 'D') {
|
|
code += generateStackPush2("frame.stack.pop2()");
|
|
} else if (returnType !== 'V') {
|
|
code += generateStackPush("frame.stack.pop()");
|
|
}
|
|
code += " frame.stack.length = 0;\n";
|
|
|
|
break;
|
|
case 0xb1: // return
|
|
if (methodInfo.isSynchronized) {
|
|
code += " if (frame.lockObject) {\n\
|
|
ctx.monitorExit(frame.lockObject);\n\
|
|
}\n";
|
|
}
|
|
|
|
code += "\
|
|
if (ctx.frames.length == 1)\n\
|
|
return;\n\
|
|
var caller = ctx.popFrame();\n\
|
|
return caller;\n";
|
|
break;
|
|
case 0xac: // ireturn
|
|
case 0xae: // freturn
|
|
case 0xb0: // areturn
|
|
if (methodInfo.isSynchronized) {
|
|
code += " if (frame.lockObject) {\n\
|
|
ctx.monitorExit(frame.lockObject);\n\
|
|
}\n";
|
|
}
|
|
|
|
code += "\
|
|
if (ctx.frames.length == 1)\n\
|
|
return;\n\
|
|
var caller = ctx.popFrame();\n\
|
|
caller.stack.push(" + generateStackPop() + ");\n\
|
|
return caller;\n";
|
|
break;
|
|
case 0xad: // lreturn
|
|
case 0xaf: // dreturn
|
|
if (methodInfo.isSynchronized) {
|
|
code += " if (frame.lockObject) {\n\
|
|
ctx.monitorExit(frame.lockObject);\n\
|
|
}\n";
|
|
}
|
|
|
|
code += "\
|
|
if (ctx.frames.length == 1)\n\
|
|
return;\n\
|
|
var caller = ctx.popFrame();\n\
|
|
caller.stack.push2(" + generateStackPop2() + ");\n\
|
|
return caller;\n";
|
|
break;
|
|
default:
|
|
throw new Error("NOT SUPPORTED: " + op.toString(16));
|
|
}
|
|
|
|
generatedCases.push({
|
|
ip: ip,
|
|
code: code,
|
|
});
|
|
}
|
|
|
|
var localsCode = "";
|
|
for (var i = 0; i < maxLocals+1; i++) {
|
|
localsCode += " var L" + i + " = frame.getLocal(" + i + ");\n";
|
|
}
|
|
if (maxDepth > 0) {
|
|
localsCode += " var";
|
|
for (var i = 0; i < maxDepth; i++) {
|
|
localsCode += " S" + i + ",";
|
|
}
|
|
localsCode = localsCode.slice(0, -1) + ";\n";
|
|
}
|
|
|
|
var generatedCode = "\n var frame = ctx.current();\n\
|
|
var cp = frame.cp;\n" + localsCode + "\
|
|
var ip = frame.ip;\n\
|
|
\n\
|
|
if (ip !== 0) {\n";
|
|
|
|
for (var i = 0; i < maxDepth; i++) {
|
|
generatedCode += "\
|
|
if (frame.stack.length > 0) {\n\
|
|
S" + i + " = frame.stack.shift();\n\
|
|
}\n";
|
|
}
|
|
|
|
generatedCode += " }\n\n\
|
|
while (true) {\n\
|
|
switch (ip) {\n";
|
|
|
|
generatedCases.forEach(function(aCase) {
|
|
if (targetIPs.has(aCase.ip)) {
|
|
generatedCode += "case " + aCase.ip + ":\n";
|
|
}
|
|
|
|
generatedCode += aCase.code;
|
|
});
|
|
|
|
generatedCode += " default:\n\
|
|
console.log('IP: ' + ip);\n";
|
|
|
|
generatedCode += " }\n";
|
|
generatedCode += " }\n";
|
|
|
|
return generatedCode;
|
|
}
|