2014-07-06 12:29:36 +04:00
|
|
|
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/* vim: set shiftwidth=4 tabstop=4 autoindent cindent expandtab: */
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2014-07-13 10:05:35 +04:00
|
|
|
Array.prototype.push2 = function (value) {
|
|
|
|
this.push(value);
|
|
|
|
this.push(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
Array.prototype.pop2 = function () {
|
|
|
|
this.pop();
|
|
|
|
return this.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
Array.prototype.top = function () {
|
|
|
|
return this[this.length - 1];
|
|
|
|
}
|
|
|
|
|
2014-07-13 09:19:03 +04:00
|
|
|
var Frame = function(methodInfo) {
|
2014-07-13 11:00:11 +04:00
|
|
|
if (methodInfo) {
|
|
|
|
this.methodInfo = methodInfo;
|
2014-07-13 20:28:19 +04:00
|
|
|
this.cp = methodInfo.classInfo.constant_pool;
|
2014-07-13 11:00:11 +04:00
|
|
|
this.code = methodInfo.code;
|
|
|
|
this.ip = 0;
|
|
|
|
}
|
2014-07-13 10:13:53 +04:00
|
|
|
this.stack = [];
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-16 05:11:59 +04:00
|
|
|
Frame.prototype.pushFrame = function(methodInfo, consumes) {
|
2014-07-16 04:18:34 +04:00
|
|
|
var callee = new Frame(methodInfo);
|
|
|
|
callee.locals = this.stack;
|
|
|
|
callee.localsBase = this.stack.length - consumes;
|
|
|
|
callee.caller = this;
|
|
|
|
THREADS.current.frame = callee;
|
|
|
|
return callee;
|
|
|
|
}
|
|
|
|
|
|
|
|
Frame.prototype.popFrame = function() {
|
|
|
|
var caller = this.caller;
|
|
|
|
caller.stack.length = this.localsBase;
|
|
|
|
THREADS.current.frame = caller;
|
|
|
|
return caller;
|
|
|
|
}
|
|
|
|
|
2014-07-13 10:05:35 +04:00
|
|
|
Frame.prototype.getLocal = function(idx) {
|
|
|
|
return this.locals[this.localsBase + idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
Frame.prototype.setLocal = function(idx, value) {
|
|
|
|
this.locals[this.localsBase + idx] = value;
|
|
|
|
}
|
|
|
|
|
2014-07-12 22:54:04 +04:00
|
|
|
Frame.prototype.isWide = function() {
|
2014-07-14 05:36:06 +04:00
|
|
|
return this.code[this.ip - 2] === OPCODES.wide;
|
2014-07-12 22:54:04 +04:00
|
|
|
}
|
|
|
|
|
2014-07-14 05:40:05 +04:00
|
|
|
Frame.prototype.getOp = function() {
|
|
|
|
return this.code[this.ip - 1];
|
|
|
|
}
|
|
|
|
|
2014-07-12 10:20:45 +04:00
|
|
|
Frame.prototype.read8 = function() {
|
|
|
|
return this.code[this.ip++];
|
2014-07-06 12:29:36 +04:00
|
|
|
};
|
|
|
|
|
2014-07-12 10:20:45 +04:00
|
|
|
Frame.prototype.read16 = function() {
|
|
|
|
return this.read8()<<8 | this.read8();
|
2014-07-06 12:29:36 +04:00
|
|
|
};
|
|
|
|
|
2014-07-12 10:20:45 +04:00
|
|
|
Frame.prototype.read32 = function() {
|
|
|
|
return this.read16()<<16 | this.read16();
|
2014-07-06 12:29:36 +04:00
|
|
|
};
|
|
|
|
|
2014-07-15 09:30:30 +04:00
|
|
|
Frame.prototype.read8signed = function() {
|
|
|
|
var x = this.read8();
|
|
|
|
return (x > 0x7f) ? (x - 0x100) : x;
|
|
|
|
}
|
|
|
|
|
2014-07-12 10:20:45 +04:00
|
|
|
Frame.prototype.read16signed = function() {
|
2014-07-15 09:30:30 +04:00
|
|
|
var x = this.read16();
|
|
|
|
return (x > 0x7fff) ? (x - 0x10000) : x;
|
2014-07-06 13:03:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-12 10:20:45 +04:00
|
|
|
Frame.prototype.read32signed = function() {
|
2014-07-15 09:30:30 +04:00
|
|
|
var x = this.read32();
|
|
|
|
return (x > 0x7fffffff) ? (x - 0x100000000) : x;
|
2014-07-06 13:03:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-12 20:48:05 +04:00
|
|
|
Frame.prototype.throw = function(ex) {
|
2014-07-06 12:29:36 +04:00
|
|
|
var handler_pc = null;
|
2014-07-11 10:21:12 +04:00
|
|
|
|
2014-07-13 04:51:35 +04:00
|
|
|
for (var i=0; i<this.exception_table.length; i++) {
|
2014-07-12 10:20:45 +04:00
|
|
|
if (this.ip >= this.exception_table[i].start_pc && this.ip <= this.exception_table[i].end_pc) {
|
|
|
|
if (this.exception_table[i].catch_type === 0) {
|
2014-07-12 20:48:05 +04:00
|
|
|
handler_pc = this.exception_table[i].handler_pc;
|
2014-07-06 12:29:36 +04:00
|
|
|
} else {
|
2014-07-12 10:20:45 +04:00
|
|
|
var name = this.cp[this.cp[this.exception_table[i].catch_type].name_index].bytes;
|
2014-07-13 20:28:19 +04:00
|
|
|
if (name === ex.className) {
|
2014-07-12 10:20:45 +04:00
|
|
|
handler_pc = this.exception_table[i].handler_pc;
|
2014-07-06 12:29:36 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-12 20:48:05 +04:00
|
|
|
|
2014-07-06 12:29:36 +04:00
|
|
|
if (handler_pc != null) {
|
2014-07-13 01:07:11 +04:00
|
|
|
stack.push(ex);
|
2014-07-12 20:48:05 +04:00
|
|
|
this.ip = handler_pc;
|
2014-07-06 12:29:36 +04:00
|
|
|
} else {
|
|
|
|
throw ex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-13 11:15:00 +04:00
|
|
|
Frame.prototype.raiseException = function(className, message) {
|
2014-07-13 10:22:17 +04:00
|
|
|
var ex = CLASSES.newObject(this, className);
|
2014-07-15 08:18:56 +04:00
|
|
|
var ctor = CLASSES.getMethod(this, ex.class, "<init>", "(Ljava/lang/String;)V", false, false);
|
2014-07-13 10:24:10 +04:00
|
|
|
this.stack.push(ex);
|
|
|
|
this.stack.push(message);
|
2014-07-14 17:45:31 +04:00
|
|
|
this.invoke(OPCODES.invokespecial, ctor);
|
2014-07-12 10:20:45 +04:00
|
|
|
this.throw(ex);
|
2014-07-07 05:16:15 +04:00
|
|
|
}
|
|
|
|
|
2014-07-16 17:23:23 +04:00
|
|
|
Frame.prototype.checkArrayAccess = function(refArray, idx) {
|
|
|
|
if (!refArray) {
|
|
|
|
this.raiseException("java/lang/NullPointerException");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (idx < 0 || idx >= refArray.length) {
|
|
|
|
this.raiseException("java/lang/ArrayIndexOutOfBoundsException", idx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-14 17:45:31 +04:00
|
|
|
Frame.prototype.invoke = function(op, methodInfo) {
|
2014-07-14 06:53:24 +04:00
|
|
|
var consumes = Signature.parse(methodInfo.signature).IN.slots;
|
2014-07-14 17:48:02 +04:00
|
|
|
|
|
|
|
if (op !== OPCODES.invokestatic) {
|
2014-07-14 06:53:24 +04:00
|
|
|
++consumes;
|
2014-07-14 17:48:02 +04:00
|
|
|
var obj = this.stack[this.stack.length - consumes];
|
|
|
|
if (!obj) {
|
|
|
|
this.raiseException("java/lang/NullPointerException");
|
|
|
|
return;
|
|
|
|
}
|
2014-07-14 22:21:25 +04:00
|
|
|
switch (op) {
|
|
|
|
case OPCODES.invokevirtual:
|
2014-07-15 09:13:01 +04:00
|
|
|
// console.log("virtual dispatch", methodInfo.classInfo.className, obj.class.className, methodInfo.name, methodInfo.signature);
|
2014-07-14 22:21:25 +04:00
|
|
|
if (methodInfo.classInfo != obj.class)
|
2014-07-15 08:18:56 +04:00
|
|
|
methodInfo = CLASSES.getMethod(this, obj.class, methodInfo.name, methodInfo.signature, op === OPCODES.invokestatic);
|
2014-07-14 22:21:25 +04:00
|
|
|
break;
|
|
|
|
}
|
2014-07-14 17:48:02 +04:00
|
|
|
}
|
2014-07-13 02:03:46 +04:00
|
|
|
|
2014-07-15 11:01:11 +04:00
|
|
|
if (ACCESS_FLAGS.isNative(methodInfo.access_flags)) {
|
|
|
|
NATIVE.invokeNative(this, methodInfo);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-16 05:11:59 +04:00
|
|
|
var callee = this.pushFrame(methodInfo, consumes);
|
2014-07-13 10:05:35 +04:00
|
|
|
|
2014-07-16 23:15:48 +04:00
|
|
|
VM.execute(callee);
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|