pluotsorbet/interpreter.ts

1238 строки
40 KiB
TypeScript
Исходник Обычный вид История

2014-11-27 12:15:46 +03:00
module J2ME {
declare var util;
2014-12-13 00:30:22 +03:00
declare var Instrument;
2014-12-19 00:03:59 +03:00
declare var Promise;
2014-11-27 12:15:46 +03:00
import BytecodeStream = Bytecode.BytecodeStream;
2014-12-25 20:39:01 +03:00
import checkArrayBounds = J2ME.checkArrayBounds;
import checkDivideByZero = J2ME.checkDivideByZero;
import checkDivideByZeroLong = J2ME.checkDivideByZeroLong;
2014-11-27 12:15:46 +03:00
import Bytecodes = Bytecode.Bytecodes;
import assert = Debug.assert;
2014-12-22 05:21:30 +03:00
import popManyInto = ArrayUtilities.popManyInto;
2014-11-27 12:15:46 +03:00
2015-01-15 07:40:05 +03:00
export var interpreterCounter = null; // new Metrics.Counter(true);
export var interpreterMethodCounter = null; // new Metrics.Counter(true);
var traceArrayAccess = false;
function traceArrayStore(index: number, array: any [], value: any) {
traceWriter.writeLn(toDebugString(array) + "[" + index + "] = " + toDebugString(value));
2014-11-27 12:15:46 +03:00
}
function traceArrayLoad(index: number, array: any []) {
assert(array[index] !== undefined);
traceWriter.writeLn(toDebugString(array) + "[" + index + "] (" + toDebugString(array[index]) + ")");
2014-11-27 12:15:46 +03:00
}
/**
* Optimize method bytecode.
*/
function optimizeMethodBytecode(methodInfo: MethodInfo) {
2015-01-15 07:40:05 +03:00
interpreterCounter && interpreterCounter.count("optimize: " + methodInfo.implKey);
var stream = new BytecodeStream(methodInfo.code);
while (stream.currentBC() !== Bytecodes.END) {
if (stream.rawCurrentBC() === Bytecodes.WIDE) {
stream.next();
continue;
}
switch (stream.currentBC()) {
case Bytecodes.ALOAD:
if (stream.nextBC() === Bytecodes.ILOAD) {
stream.writeCurrentBC(Bytecodes.ALOAD_ILOAD);
}
break;
case Bytecodes.IINC:
if (stream.nextBC() === Bytecodes.GOTO) {
stream.writeCurrentBC(Bytecodes.IINC_GOTO);
}
break;
case Bytecodes.ARRAYLENGTH:
if (stream.nextBC() === Bytecodes.IF_ICMPGE) {
stream.writeCurrentBC(Bytecodes.ARRAYLENGTH_IF_ICMPGE);
}
break;
}
stream.next();
}
methodInfo.isOptimized = true;
}
function resolve(index: number, classInfo: ClassInfo, isStatic: boolean = false): any {
return classInfo.resolve(index, isStatic);
}
function resolveField(index: number, classInfo: ClassInfo, isStatic: boolean): FieldInfo {
return <FieldInfo><any>classInfo.resolve(index, isStatic);
}
function resolveClass(index: number, classInfo: ClassInfo, isStatic: boolean): ClassInfo {
var classInfo: ClassInfo = <any>classInfo.resolve(index, isStatic);
linkKlass(classInfo);
return classInfo;
}
function resolveMethod(index: number, classInfo: ClassInfo, isStatic: boolean): MethodInfo {
return <MethodInfo><any>classInfo.resolve(index, isStatic);
}
2014-12-26 08:03:45 +03:00
/**
* Debugging helper to make sure native methods were implemented correctly.
*/
function checkReturnValue(methodInfo: MethodInfo, returnValue: any) {
if (returnValue instanceof Promise) {
console.error("You forgot to call asyncImpl():", methodInfo.implKey);
} else if (methodInfo.getReturnKind() === Kind.Void && returnValue) {
console.error("You returned something in a void method:", methodInfo.implKey);
} else if (methodInfo.getReturnKind() !== Kind.Void && (returnValue === undefined) &&
!U) {
2014-12-26 08:03:45 +03:00
console.error("You returned undefined in a non-void method:", methodInfo.implKey);
} else if (typeof returnValue === "string") {
console.error("You returned a non-wrapped string:", methodInfo.implKey);
} else if (returnValue === true || returnValue === false) {
console.error("You returned a JS boolean:", methodInfo.implKey);
}
}
2014-12-18 11:53:16 +03:00
/**
* The number of opcodes executed thus far.
*/
2015-01-16 11:46:34 +03:00
export var bytecodeCount = 0;
2014-12-18 11:53:16 +03:00
/**
* Temporarily used for fn.apply.
*/
var argArray = [];
function buildExceptionLog(ex, stackTrace) {
var className = ex.klass.classInfo.className;
var detailMessage = util.fromJavaString(CLASSES.getField(ex.klass.classInfo, "I.detailMessage.Ljava/lang/String;").get(ex));
return className + ": " + (detailMessage || "") + "\n" + stackTrace.map(function(entry) {
return " - " + entry.className + "." + entry.methodName + "(), pc=" + entry.offset;
}).join("\n") + "\n\n";
}
2014-11-27 12:15:46 +03:00
function tryCatch(e) {
2014-12-30 02:43:27 +03:00
var ctx = $.ctx;
var frame = ctx.current();
var frames = ctx.frames;
var stack = frame.stack;
2014-11-27 12:15:46 +03:00
var exClass = e.class;
if (!e.stackTrace) {
e.stackTrace = [];
}
2014-11-27 12:15:46 +03:00
var stackTrace = e.stackTrace;
2014-11-27 12:15:46 +03:00
do {
var exception_table = frame.methodInfo.exception_table;
var handler_pc = null;
for (var i = 0; exception_table && i < exception_table.length; i++) {
2015-01-08 02:08:56 +03:00
if (frame.opPc >= exception_table[i].start_pc && frame.opPc < exception_table[i].end_pc) {
if (exception_table[i].catch_type === 0) {
handler_pc = exception_table[i].handler_pc;
break;
} else {
classInfo = resolveClass(exception_table[i].catch_type, frame.methodInfo.classInfo, false);
if (isAssignableTo(e.klass, classInfo.klass)) {
2014-11-27 12:15:46 +03:00
handler_pc = exception_table[i].handler_pc;
break;
2014-11-27 12:15:46 +03:00
}
}
}
}
2014-11-27 12:15:46 +03:00
var classInfo = frame.methodInfo.classInfo;
if (classInfo && classInfo.className) {
stackTrace.push({
className: classInfo.className,
methodName: frame.methodInfo.name,
offset: frame.pc
});
}
2014-11-27 12:15:46 +03:00
if (handler_pc != null) {
stack.length = 0;
stack.push(e);
frame.pc = handler_pc;
2014-11-27 12:15:46 +03:00
if (VM.DEBUG_PRINT_ALL_EXCEPTIONS) {
console.error(buildExceptionLog(e, stackTrace));
2014-11-27 12:15:46 +03:00
}
return;
}
frames.pop();
frame = frames[frames.length - 1];
if (Frame.isMarker(frame)) {
break;
}
stack = frame.stack;
} while (true);
2014-11-27 12:15:46 +03:00
if (ctx.current() === Frame.Start) {
ctx.kill();
if (ctx.thread && ctx.thread.waiting && ctx.thread.waiting.length > 0) {
console.error(buildExceptionLog(e, stackTrace));
ctx.thread.waiting.forEach(function(waitingCtx, n) {
ctx.thread.waiting[n] = null;
waitingCtx.wakeup(ctx.thread);
});
2014-11-27 12:15:46 +03:00
}
throw new Error(buildExceptionLog(e, stackTrace));
} else {
throw e;
2014-11-27 12:15:46 +03:00
}
}
2014-11-27 12:15:46 +03:00
2014-12-30 02:43:27 +03:00
export function interpret() {
var ctx = $.ctx;
// These must always be kept up to date with the current frame.
var frame = ctx.current();
release || assert (!Frame.isMarker(frame));
var frames = ctx.frames;
var mi = frame.methodInfo;
var ci = mi.classInfo;
var cp = ci.constant_pool;
var stack = frame.stack;
var returnValue = null;
2014-11-27 12:15:46 +03:00
var traceBytecodes = false;
var traceSourceLocation = true;
var index: any, value: any, constant: any;
2014-12-25 20:39:53 +03:00
var a: any, b: any, c: any;
var pc: number, startPc: number;
2014-12-25 20:39:53 +03:00
var type;
var size;
2014-12-25 21:11:18 +03:00
var array: any;
var object: java.lang.Object;
var fieldInfo: FieldInfo;
var classInfo: ClassInfo;
2015-01-08 02:46:53 +03:00
if (!frame.methodInfo.isOptimized && frame.methodInfo.bytecodeCount > 100) {
optimizeMethodBytecode(frame.methodInfo);
}
2015-01-08 02:46:53 +03:00
frame.methodInfo.interpreterCallCount ++;
2014-11-27 12:15:46 +03:00
while (true) {
2015-01-16 11:46:34 +03:00
profile && bytecodeCount ++;
// frame.methodInfo.bytecodeCount ++;
2015-01-08 02:08:56 +03:00
frame.opPc = frame.pc;
2014-11-27 12:15:46 +03:00
var op: Bytecodes = frame.read8();
// interpreterMethodCounter && interpreterMethodCounter.count(Bytecodes[op]);
2014-12-16 05:54:47 +03:00
try {
switch (op) {
case Bytecodes.NOP:
break;
case Bytecodes.ACONST_NULL:
stack.push(null);
break;
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:
stack.push(op - Bytecodes.ICONST_0);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.FCONST_0:
case Bytecodes.FCONST_1:
case Bytecodes.FCONST_2:
stack.push(op - Bytecodes.FCONST_0);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.DCONST_0:
case Bytecodes.DCONST_1:
stack.push2(op - Bytecodes.DCONST_0);
break;
case Bytecodes.LCONST_0:
2014-12-16 05:54:47 +03:00
case Bytecodes.LCONST_1:
stack.push2(Long.fromInt(op - Bytecodes.LCONST_0));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.BIPUSH:
stack.push(frame.read8Signed());
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.SIPUSH:
stack.push(frame.read16Signed());
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LDC:
case Bytecodes.LDC_W:
2014-12-25 20:39:53 +03:00
index = (op === Bytecodes.LDC) ? frame.read8() : frame.read16();
constant = resolve(index, ci, false);
2014-12-16 05:54:47 +03:00
stack.push(constant);
break;
case Bytecodes.LDC2_W:
2014-12-25 20:39:53 +03:00
index = frame.read16();
constant = resolve(index, ci, false);
2014-12-16 05:54:47 +03:00
stack.push2(constant);
break;
case Bytecodes.ILOAD:
stack.push(frame.getLocal(frame.read8()));
break;
2014-12-16 05:54:47 +03:00
case Bytecodes.FLOAD:
stack.push(frame.getLocal(frame.read8()));
break;
2014-12-16 05:54:47 +03:00
case Bytecodes.ALOAD:
stack.push(frame.getLocal(frame.read8()));
break;
case Bytecodes.ALOAD_ILOAD:
stack.push(frame.getLocal(frame.read8()));
frame.pc ++;
stack.push(frame.getLocal(frame.read8()));
break;
2014-12-16 05:54:47 +03:00
case Bytecodes.LLOAD:
case Bytecodes.DLOAD:
stack.push2(frame.getLocal(frame.read8()));
break;
case Bytecodes.ILOAD_0:
case Bytecodes.ILOAD_1:
case Bytecodes.ILOAD_2:
case Bytecodes.ILOAD_3:
stack.push(frame.getLocal(op - Bytecodes.ILOAD_0));
break;
case Bytecodes.FLOAD_0:
case Bytecodes.FLOAD_1:
case Bytecodes.FLOAD_2:
2014-12-16 05:54:47 +03:00
case Bytecodes.FLOAD_3:
stack.push(frame.getLocal(op - Bytecodes.FLOAD_0));
break;
case Bytecodes.ALOAD_0:
case Bytecodes.ALOAD_1:
case Bytecodes.ALOAD_2:
2014-12-16 05:54:47 +03:00
case Bytecodes.ALOAD_3:
stack.push(frame.getLocal(op - Bytecodes.ALOAD_0));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LLOAD_0:
case Bytecodes.LLOAD_1:
case Bytecodes.LLOAD_2:
case Bytecodes.LLOAD_3:
stack.push2(frame.getLocal(op - Bytecodes.LLOAD_0));
break;
case Bytecodes.DLOAD_0:
case Bytecodes.DLOAD_1:
case Bytecodes.DLOAD_2:
2014-12-16 05:54:47 +03:00
case Bytecodes.DLOAD_3:
stack.push2(frame.getLocal(op - Bytecodes.DLOAD_0));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IALOAD:
case Bytecodes.FALOAD:
case Bytecodes.AALOAD:
case Bytecodes.BALOAD:
case Bytecodes.CALOAD:
case Bytecodes.SALOAD:
2014-12-25 20:39:53 +03:00
index = stack.pop();
array = stack.pop();
2014-12-25 20:39:01 +03:00
checkArrayBounds(array, index);
stack.push(array[index]);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LALOAD:
case Bytecodes.DALOAD:
2014-12-25 20:39:53 +03:00
index = stack.pop();
array = stack.pop();
2014-12-25 20:39:01 +03:00
checkArrayBounds(array, index);
stack.push2(array[index]);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.ISTORE:
case Bytecodes.FSTORE:
case Bytecodes.ASTORE:
frame.setLocal(frame.read8(), stack.pop());
break;
case Bytecodes.LSTORE:
case Bytecodes.DSTORE:
frame.setLocal(frame.read8(), stack.pop2());
break;
case Bytecodes.ISTORE_0:
case Bytecodes.FSTORE_0:
case Bytecodes.ASTORE_0:
frame.setLocal(0, stack.pop());
break;
case Bytecodes.ISTORE_1:
case Bytecodes.FSTORE_1:
case Bytecodes.ASTORE_1:
frame.setLocal(1, stack.pop());
break;
case Bytecodes.ISTORE_2:
case Bytecodes.FSTORE_2:
case Bytecodes.ASTORE_2:
frame.setLocal(2, stack.pop());
break;
case Bytecodes.ISTORE_3:
case Bytecodes.FSTORE_3:
case Bytecodes.ASTORE_3:
frame.setLocal(3, stack.pop());
break;
case Bytecodes.LSTORE_0:
case Bytecodes.DSTORE_0:
frame.setLocal(0, stack.pop2());
break;
case Bytecodes.LSTORE_1:
case Bytecodes.DSTORE_1:
frame.setLocal(1, stack.pop2());
break;
case Bytecodes.LSTORE_2:
case Bytecodes.DSTORE_2:
frame.setLocal(2, stack.pop2());
break;
case Bytecodes.LSTORE_3:
case Bytecodes.DSTORE_3:
frame.setLocal(3, stack.pop2());
break;
case Bytecodes.IASTORE:
case Bytecodes.FASTORE:
case Bytecodes.BASTORE:
case Bytecodes.CASTORE:
case Bytecodes.SASTORE:
2014-12-25 20:39:53 +03:00
value = stack.pop();
index = stack.pop();
array = stack.pop();
2014-12-25 20:39:01 +03:00
checkArrayBounds(array, index);
array[index] = value;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LASTORE:
case Bytecodes.DASTORE:
2014-12-25 20:39:53 +03:00
value = stack.pop2();
index = stack.pop();
array = stack.pop();
2014-12-25 20:39:01 +03:00
checkArrayBounds(array, index);
array[index] = value;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.AASTORE:
2014-12-25 20:39:53 +03:00
value = stack.pop();
index = stack.pop();
array = stack.pop();
2014-12-25 20:39:01 +03:00
checkArrayBounds(array, index);
checkArrayStore(array, value);
array[index] = value;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.POP:
stack.pop();
break;
case Bytecodes.POP2:
stack.pop2();
break;
case Bytecodes.DUP:
stack.push(stack[stack.length - 1]);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.DUP_X1:
2014-12-25 20:39:53 +03:00
a = stack.pop();
b = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(a);
stack.push(b);
stack.push(a);
break;
case Bytecodes.DUP_X2:
2014-12-25 20:39:53 +03:00
a = stack.pop();
b = stack.pop();
c = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(a);
stack.push(c);
stack.push(b);
stack.push(a);
break;
case Bytecodes.DUP2:
2014-12-25 20:39:53 +03:00
a = stack.pop();
b = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(b);
stack.push(a);
stack.push(b);
stack.push(a);
break;
case Bytecodes.DUP2_X1:
2014-12-25 20:39:53 +03:00
a = stack.pop();
b = stack.pop();
c = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(b);
stack.push(a);
stack.push(c);
stack.push(b);
stack.push(a);
break;
case Bytecodes.DUP2_X2:
2014-12-25 20:39:53 +03:00
a = stack.pop();
b = stack.pop();
c = stack.pop();
2014-12-16 05:54:47 +03:00
var d = stack.pop();
stack.push(b);
stack.push(a);
stack.push(d);
stack.push(c);
stack.push(b);
stack.push(a);
break;
case Bytecodes.SWAP:
2014-12-25 20:39:53 +03:00
a = stack.pop();
b = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(a);
stack.push(b);
break;
case Bytecodes.IINC:
2014-12-25 20:39:53 +03:00
index = frame.read8();
value = frame.read8Signed();
frame.incLocal(index, value);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IINC_GOTO:
index = frame.read8();
value = frame.read8Signed();
frame.setLocal(index, frame.getLocal(index) + value);
frame.pc ++;
frame.pc = frame.readTargetPC();
break;
2014-12-16 05:54:47 +03:00
case Bytecodes.IADD:
stack.push((stack.pop() + stack.pop()) | 0);
break;
case Bytecodes.LADD:
stack.push2(stack.pop2().add(stack.pop2()));
break;
case Bytecodes.FADD:
stack.push(Math.fround(stack.pop() + stack.pop()));
break;
case Bytecodes.DADD:
stack.push2(stack.pop2() + stack.pop2());
break;
case Bytecodes.ISUB:
stack.push((-stack.pop() + stack.pop()) | 0);
break;
case Bytecodes.LSUB:
stack.push2(stack.pop2().negate().add(stack.pop2()));
break;
case Bytecodes.FSUB:
stack.push(Math.fround(-stack.pop() + stack.pop()));
break;
case Bytecodes.DSUB:
stack.push2(-stack.pop2() + stack.pop2());
break;
case Bytecodes.IMUL:
stack.push(Math.imul(stack.pop(), stack.pop()));
break;
case Bytecodes.LMUL:
stack.push2(stack.pop2().multiply(stack.pop2()));
break;
case Bytecodes.FMUL:
stack.push(Math.fround(stack.pop() * stack.pop()));
break;
case Bytecodes.DMUL:
stack.push2(stack.pop2() * stack.pop2());
break;
case Bytecodes.IDIV:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-25 20:39:01 +03:00
checkDivideByZero(b);
stack.push((a === Constants.INT_MIN && b === -1) ? a : ((a / b) | 0));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LDIV:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-25 20:39:01 +03:00
checkDivideByZeroLong(b);
2014-12-16 05:54:47 +03:00
stack.push2(a.div(b));
break;
case Bytecodes.FDIV:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(Math.fround(a / b));
break;
case Bytecodes.DDIV:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
stack.push2(a / b);
break;
case Bytecodes.IREM:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-25 20:39:01 +03:00
checkDivideByZero(b);
2014-12-16 05:54:47 +03:00
stack.push(a % b);
break;
case Bytecodes.LREM:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-25 20:39:01 +03:00
checkDivideByZeroLong(b);
2014-12-16 05:54:47 +03:00
stack.push2(a.modulo(b));
break;
case Bytecodes.FREM:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(Math.fround(a % b));
break;
case Bytecodes.DREM:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
stack.push2(a % b);
break;
case Bytecodes.INEG:
stack.push((-stack.pop()) | 0);
break;
case Bytecodes.LNEG:
stack.push2(stack.pop2().negate());
break;
case Bytecodes.FNEG:
stack.push(-stack.pop());
break;
case Bytecodes.DNEG:
stack.push2(-stack.pop2());
break;
case Bytecodes.ISHL:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(a << b);
break;
case Bytecodes.LSHL:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
stack.push2(a.shiftLeft(b));
break;
case Bytecodes.ISHR:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(a >> b);
break;
case Bytecodes.LSHR:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
stack.push2(a.shiftRight(b));
break;
case Bytecodes.IUSHR:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
stack.push(a >>> b);
break;
case Bytecodes.LUSHR:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
stack.push2(a.shiftRightUnsigned(b));
break;
case Bytecodes.IAND:
stack.push(stack.pop() & stack.pop());
break;
case Bytecodes.LAND:
stack.push2(stack.pop2().and(stack.pop2()));
break;
case Bytecodes.IOR:
stack.push(stack.pop() | stack.pop());
break;
case Bytecodes.LOR:
stack.push2(stack.pop2().or(stack.pop2()));
break;
case Bytecodes.IXOR:
stack.push(stack.pop() ^ stack.pop());
break;
case Bytecodes.LXOR:
stack.push2(stack.pop2().xor(stack.pop2()));
break;
case Bytecodes.LCMP:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
if (a.greaterThan(b)) {
stack.push(1);
} else if (a.lessThan(b)) {
stack.push(-1);
} else {
stack.push(0);
}
break;
case Bytecodes.FCMPL:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
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 Bytecodes.FCMPG:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
2014-12-16 05:54:47 +03:00
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 Bytecodes.DCMPL:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
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 Bytecodes.DCMPG:
2014-12-25 20:39:53 +03:00
b = stack.pop2();
a = stack.pop2();
2014-12-16 05:54:47 +03:00
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 Bytecodes.IFEQ:
pc = frame.readTargetPC();
if (stack.pop() === 0) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFNE:
pc = frame.readTargetPC();
if (stack.pop() !== 0) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFLT:
pc = frame.readTargetPC();
if (stack.pop() < 0) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFGE:
pc = frame.readTargetPC();
if (stack.pop() >= 0) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFGT:
pc = frame.readTargetPC();
if (stack.pop() > 0) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFLE:
pc = frame.readTargetPC();
if (stack.pop() <= 0) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ICMPEQ:
pc = frame.readTargetPC();
if (stack.pop() === stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ICMPNE:
pc = frame.readTargetPC();
if (stack.pop() !== stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ICMPLT:
pc = frame.readTargetPC();
if (stack.pop() > stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ICMPGE:
pc = frame.readTargetPC();
if (stack.pop() <= stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ICMPGT:
pc = frame.readTargetPC();
if (stack.pop() < stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ICMPLE:
pc = frame.readTargetPC();
if (stack.pop() >= stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ACMPEQ:
pc = frame.readTargetPC();
if (stack.pop() === stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IF_ACMPNE:
pc = frame.readTargetPC();
if (stack.pop() !== stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFNULL:
pc = frame.readTargetPC();
if (!stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.IFNONNULL:
pc = frame.readTargetPC();
if (stack.pop()) {
frame.pc = pc;
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.GOTO:
frame.pc = frame.readTargetPC();
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.GOTO_W:
frame.pc = frame.read32Signed() - 1;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.JSR:
pc = frame.read16();
stack.push(frame.pc);
frame.pc = pc;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.JSR_W:
pc = frame.read32();
stack.push(frame.pc);
frame.pc = pc;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.RET:
frame.pc = frame.getLocal(frame.read8());
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.I2L:
stack.push2(Long.fromInt(stack.pop()));
break;
case Bytecodes.I2F:
break;
case Bytecodes.I2D:
stack.push2(stack.pop());
break;
case Bytecodes.L2I:
stack.push(stack.pop2().toInt());
break;
case Bytecodes.L2F:
stack.push(Math.fround(stack.pop2().toNumber()));
break;
case Bytecodes.L2D:
stack.push2(stack.pop2().toNumber());
break;
case Bytecodes.F2I:
stack.push(util.double2int(stack.pop()));
break;
case Bytecodes.F2L:
stack.push2(Long.fromNumber(stack.pop()));
break;
case Bytecodes.F2D:
stack.push2(stack.pop());
break;
case Bytecodes.D2I:
stack.push(util.double2int(stack.pop2()));
break;
case Bytecodes.D2L:
stack.push2(util.double2long(stack.pop2()));
break;
case Bytecodes.D2F:
stack.push(Math.fround(stack.pop2()));
break;
case Bytecodes.I2B:
stack.push((stack.pop() << 24) >> 24);
break;
case Bytecodes.I2C:
stack.push(stack.pop() & 0xffff);
break;
case Bytecodes.I2S:
stack.push((stack.pop() << 16) >> 16);
break;
case Bytecodes.TABLESWITCH:
frame.pc = frame.tableSwitch();
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LOOKUPSWITCH:
frame.pc = frame.lookupSwitch();
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.NEWARRAY:
2014-12-25 20:39:53 +03:00
type = frame.read8();
size = stack.pop();
2014-12-16 05:54:47 +03:00
if (size < 0) {
throw $.newNegativeArraySizeException();
2014-11-27 12:15:46 +03:00
}
2014-12-16 05:54:47 +03:00
stack.push(util.newPrimitiveArray("????ZCFDBSIJ"[type], size));
break;
case Bytecodes.ANEWARRAY:
2014-12-25 20:39:53 +03:00
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
classInitCheck(classInfo, frame.pc - 3);
2014-12-25 20:39:53 +03:00
size = stack.pop();
2014-12-16 05:54:47 +03:00
if (size < 0) {
throw $.newNegativeArraySizeException();
2014-12-16 05:54:47 +03:00
}
stack.push(util.newArray(classInfo, size));
break;
case Bytecodes.MULTIANEWARRAY:
2014-12-25 20:39:53 +03:00
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
2014-12-16 05:54:47 +03:00
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, lengths.reverse()));
break;
case Bytecodes.ARRAYLENGTH:
2014-12-25 21:11:18 +03:00
array = stack.pop();
stack.push(array.length);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.ARRAYLENGTH_IF_ICMPGE:
array = stack.pop();
stack.push(array.length);
frame.pc ++;
pc = frame.readTargetPC();
if (stack.pop() <= stack.pop()) {
frame.pc = pc;
}
break;
2014-12-16 05:54:47 +03:00
case Bytecodes.GETFIELD:
2014-12-25 20:39:53 +03:00
index = frame.read16();
fieldInfo = resolveField(index, mi.classInfo, false);
2014-12-25 21:11:18 +03:00
object = stack.pop();
stack.pushKind(fieldInfo.kind, fieldInfo.get(object));
frame.patch(3, Bytecodes.GETFIELD, Bytecodes.RESOLVED_GETFIELD);
break;
case Bytecodes.RESOLVED_GETFIELD:
fieldInfo = <FieldInfo><any>cp[frame.read16()];
object = stack.pop();
stack.pushKind(fieldInfo.kind, fieldInfo.get(object));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.PUTFIELD:
2014-12-25 20:39:53 +03:00
index = frame.read16();
fieldInfo = resolveField(index, mi.classInfo, false);
value = stack.popKind(fieldInfo.kind);
2014-12-25 21:11:18 +03:00
object = stack.pop();
fieldInfo.set(object, value);
frame.patch(3, Bytecodes.PUTFIELD, Bytecodes.RESOLVED_PUTFIELD);
break;
case Bytecodes.RESOLVED_PUTFIELD:
fieldInfo = <FieldInfo><any>cp[frame.read16()];
value = stack.popKind(fieldInfo.kind);
object = stack.pop();
fieldInfo.set(object, value);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.GETSTATIC:
2014-12-25 20:39:53 +03:00
index = frame.read16();
fieldInfo = resolveField(index, mi.classInfo, true);
2014-12-30 02:43:27 +03:00
classInitCheck(fieldInfo.classInfo, frame.pc - 3);
if (U) {
return;
}
value = fieldInfo.getStatic();
stack.pushKind(fieldInfo.kind, value);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.PUTSTATIC:
2014-12-25 20:39:53 +03:00
index = frame.read16();
fieldInfo = resolveField(index, mi.classInfo, true);
if (fieldInfo.classInfo.className === "gnu/testlet/vm/InterfaceTest$A") {
debugger;
}
2014-12-30 02:43:27 +03:00
classInitCheck(fieldInfo.classInfo, frame.pc - 3);
if (U) {
return;
}
fieldInfo.setStatic(stack.popKind(fieldInfo.kind));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.NEW:
2014-12-25 20:39:53 +03:00
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
2014-12-30 02:43:27 +03:00
classInitCheck(classInfo, frame.pc - 3);
if (U) {
return;
}
2014-12-25 21:17:00 +03:00
stack.push(util.newObject(classInfo));
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.CHECKCAST:
2014-12-25 20:39:53 +03:00
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
2014-12-25 21:11:18 +03:00
object = stack[stack.length - 1];
if (object && !isAssignableTo(object.klass, classInfo.klass)) {
throw $.newClassCastException(
2014-12-25 21:11:18 +03:00
object.klass.classInfo.className + " is not assignable to " +
2014-12-16 05:54:47 +03:00
classInfo.className);
}
break;
case Bytecodes.INSTANCEOF:
2014-12-25 20:39:53 +03:00
index = frame.read16();
classInfo = resolveClass(index, mi.classInfo, false);
2014-12-25 21:11:18 +03:00
object = stack.pop();
var result = !object ? false : isAssignableTo(object.klass, classInfo.klass);
2014-12-16 05:54:47 +03:00
stack.push(result ? 1 : 0);
break;
case Bytecodes.ATHROW:
2014-12-25 21:11:18 +03:00
object = stack.pop();
if (!object) {
throw $.newNullPointerException();
2014-12-16 05:54:47 +03:00
}
2014-12-25 21:11:18 +03:00
throw object;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.MONITORENTER:
2014-12-25 21:11:18 +03:00
object = stack.pop();
ctx.monitorEnter(object);
if (U === VMState.Pausing) {
2014-12-16 05:54:47 +03:00
return;
}
break;
case Bytecodes.MONITOREXIT:
2014-12-25 21:11:18 +03:00
object = stack.pop();
ctx.monitorExit(object);
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.WIDE:
2014-12-25 21:17:00 +03:00
frame.wide();
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.RESOLVED_INVOKEVIRTUAL:
var startPC = frame.pc - 1;
index = frame.read16();
var calleeMethodInfo = <MethodInfo><any>cp[index];
var object = frame.peekInvokeObject(calleeMethodInfo);
calleeMethod = object[calleeMethodInfo.mangledName];
var calleeTargetMethodInfo: MethodInfo = calleeMethod.methodInfo;
if (calleeTargetMethodInfo && !calleeTargetMethodInfo.isNative && !calleeTargetMethodInfo.isCompiled) {
var calleeFrame = Frame.create(calleeTargetMethodInfo, [], 0);
ArrayUtilities.popManyInto(stack, calleeTargetMethodInfo.consumeArgumentSlots, calleeFrame.local);
frames.push(calleeFrame);
frame = calleeFrame;
mi = frame.methodInfo;
ci = mi.classInfo;
cp = ci.constant_pool;
stack = frame.stack;
continue;
}
// Call directy.
var returnValue;
var argumentSlots = calleeMethodInfo.argumentSlots;
switch (argumentSlots) {
case 0:
returnValue = calleeMethod.call(object);
break;
case 1:
a = stack.pop();
returnValue = calleeMethod.call(object, a);
break;
case 2:
b = stack.pop();
a = stack.pop();
returnValue = calleeMethod.call(object, a, b);
break;
case 3:
c = stack.pop();
b = stack.pop();
a = stack.pop();
returnValue = calleeMethod.call(object, a, b, c);
break;
default:
debugger;
}
stack.pop();
if (!release) {
checkReturnValue(calleeMethodInfo, returnValue);
}
if (U) {
return;
}
if (calleeMethodInfo.getReturnKind() !== Kind.Void) {
if (isTwoSlot(calleeMethodInfo.getReturnKind())) {
stack.push2(returnValue);
} else {
stack.push(returnValue);
}
}
break;
2014-12-16 05:54:47 +03:00
case Bytecodes.INVOKEVIRTUAL:
case Bytecodes.INVOKESPECIAL:
case Bytecodes.INVOKESTATIC:
case Bytecodes.INVOKEINTERFACE:
var startPC = frame.pc - 1;
2014-12-25 20:39:53 +03:00
index = frame.read16();
2014-12-26 08:03:45 +03:00
if (op === Bytecodes.INVOKEINTERFACE) {
frame.read16(); // Args Number & Zero
2014-12-16 05:54:47 +03:00
}
2014-12-26 08:03:45 +03:00
var isStatic = (op === Bytecodes.INVOKESTATIC);
// Resolve method and do the class init check if necessary.
var calleeMethodInfo = resolveMethod(index, mi.classInfo, isStatic);
if (isStatic) {
classInitCheck(calleeMethodInfo.classInfo, startPC);
if (U) {
return;
}
2014-12-16 05:54:47 +03:00
}
// Figure out the target method.
var calleeTargetMethodInfo: MethodInfo = calleeMethodInfo;
2014-12-25 21:11:18 +03:00
object = null;
var calleeMethod: any;
2014-12-16 05:54:47 +03:00
if (!isStatic) {
object = frame.peekInvokeObject(calleeMethodInfo);
2014-12-16 05:54:47 +03:00
switch (op) {
case Bytecodes.INVOKEVIRTUAL:
if (!calleeTargetMethodInfo.hasTwoSlotArguments &&
!calleeTargetMethodInfo.isSynchronized &&
calleeTargetMethodInfo.argumentSlots < 4) {
frame.patch(3, Bytecodes.INVOKEVIRTUAL, Bytecodes.RESOLVED_INVOKEVIRTUAL);
}
2014-12-16 05:54:47 +03:00
case Bytecodes.INVOKEINTERFACE:
calleeMethod = object[calleeMethodInfo.mangledName];
calleeTargetMethodInfo = calleeMethod.methodInfo;
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.INVOKESPECIAL:
checkNull(object);
calleeMethod = calleeMethodInfo.fn;
2014-12-16 05:54:47 +03:00
break;
}
} else {
calleeMethod = calleeMethodInfo.fn;
2014-11-27 12:15:46 +03:00
}
// Call method directly in the interpreter if we can.
if (calleeTargetMethodInfo && !calleeTargetMethodInfo.isNative && !calleeTargetMethodInfo.isCompiled) {
var calleeFrame = Frame.create(calleeTargetMethodInfo, [], 0);
ArrayUtilities.popManyInto(stack, calleeTargetMethodInfo.consumeArgumentSlots, calleeFrame.local);
frames.push(calleeFrame);
frame = calleeFrame;
mi = frame.methodInfo;
ci = mi.classInfo;
cp = ci.constant_pool;
stack = frame.stack;
if (calleeTargetMethodInfo.isSynchronized) {
if (!calleeFrame.lockObject) {
frame.lockObject = calleeTargetMethodInfo.isStatic
? calleeTargetMethodInfo.classInfo.getClassObject()
: frame.getLocal(0);
}
ctx.monitorEnter(calleeFrame.lockObject);
if (U === VMState.Pausing) {
return;
}
}
continue;
}
// Call directy.
var returnValue;
var argumentSlots = calleeMethodInfo.hasTwoSlotArguments ? -1 : calleeMethodInfo.argumentSlots;
2014-12-31 04:48:53 +03:00
switch (argumentSlots) {
case 0:
returnValue = calleeMethod.call(object);
break;
case 1:
2014-12-25 20:39:53 +03:00
a = stack.pop();
returnValue = calleeMethod.call(object, a);
break;
case 2:
2014-12-25 20:39:53 +03:00
b = stack.pop();
a = stack.pop();
returnValue = calleeMethod.call(object, a, b);
break;
case 3:
2014-12-25 20:39:53 +03:00
c = stack.pop();
b = stack.pop();
a = stack.pop();
returnValue = calleeMethod.call(object, a, b, c);
break;
default:
if (calleeMethodInfo.hasTwoSlotArguments) {
frame.popArgumentsInto(calleeMethodInfo.signatureDescriptor, argArray);
} else {
popManyInto(stack, calleeMethodInfo.argumentSlots, argArray);
}
var returnValue = calleeMethod.apply(object, argArray);
}
if (!isStatic) {
stack.pop();
2014-12-16 05:54:47 +03:00
}
2014-12-21 11:08:46 +03:00
if (!release) {
checkReturnValue(calleeMethodInfo, returnValue);
2014-12-19 00:03:59 +03:00
}
2014-12-21 11:08:46 +03:00
if (U) {
2014-12-16 04:38:27 +03:00
return;
}
2014-12-16 04:06:53 +03:00
if (calleeMethodInfo.getReturnKind() !== Kind.Void) {
if (isTwoSlot(calleeMethodInfo.getReturnKind())) {
2014-12-16 05:54:47 +03:00
stack.push2(returnValue);
} else {
stack.push(returnValue);
}
}
2014-12-16 05:54:47 +03:00
break;
case Bytecodes.LRETURN:
case Bytecodes.DRETURN:
returnValue = stack.pop();
2014-12-16 05:54:47 +03:00
case Bytecodes.IRETURN:
case Bytecodes.FRETURN:
case Bytecodes.ARETURN:
returnValue = stack.pop();
case Bytecodes.RETURN:
var callee = frames.pop();
if (callee.lockObject) {
ctx.monitorExit(callee.lockObject);
2014-12-16 05:54:47 +03:00
}
callee.free();
frame = frames[frames.length - 1];
if (Frame.isMarker(frame)) { // Marker or Start Frame
if (op === Bytecodes.RETURN) {
return undefined;
}
2014-12-16 05:54:47 +03:00
return returnValue;
}
stack = frame.stack;
mi = frame.methodInfo;
ci = mi.classInfo;
cp = ci.constant_pool;
if (op === Bytecodes.RETURN) {
// Nop.
} else if (op === Bytecodes.LRETURN || op === Bytecodes.DRETURN) {
stack.push2(returnValue);
} else {
stack.push(returnValue);
2014-12-16 05:54:47 +03:00
}
break;
default:
var opName = Bytecodes[op];
throw new Error("Opcode " + opName + " [" + op + "] not supported.");
}
} catch (e) {
e = translateException(e);
if (!e.klass) {
// A non-java exception was thrown. Rethrow so it is not handled by tryCatch.
2015-01-03 02:43:51 +03:00
throw e;
}
tryCatch(e);
frame = ctx.current();
assert (!Frame.isMarker(frame));
mi = frame.methodInfo;
ci = mi.classInfo;
cp = ci.constant_pool;
stack = frame.stack;
2014-12-16 05:54:47 +03:00
continue;
2014-11-27 12:15:46 +03:00
}
}
}
export class VM {
static execute = interpret;
static Yield = {toString: function () { return "YIELD" }};
static Pause = {toString: function () { return "PAUSE" }};
2014-11-27 12:15:46 +03:00
static DEBUG = false;
static DEBUG_PRINT_ALL_EXCEPTIONS = false;
}
}
2015-01-14 03:47:42 +03:00
var VM = J2ME.VM;