2011-07-04 18:20:27 +01:00
|
|
|
function canonicalName(ref){
|
|
|
|
return ref.str.replace(/\//g,".")
|
|
|
|
}
|
|
|
|
|
|
|
|
var JVM = function(params,args){
|
|
|
|
this.heap = {};
|
|
|
|
this.params = params;
|
|
|
|
this.args = args;
|
|
|
|
this.method_area = {};
|
|
|
|
this.level = 0;
|
|
|
|
this.classpath = params.classes_to_load;
|
|
|
|
|
2011-07-06 16:30:18 +01:00
|
|
|
this.classForName = function (name){
|
|
|
|
var superClass, loaded_class = this.method_area[name];
|
2011-07-08 17:29:57 +01:00
|
|
|
|
2011-07-06 16:30:18 +01:00
|
|
|
if (!loaded_class){
|
|
|
|
loaded_class = LoadClassFile(name, this);
|
2011-07-08 17:29:57 +01:00
|
|
|
this.method_area[name] = loaded_class;
|
2011-07-06 16:30:18 +01:00
|
|
|
this.verifyAndLoadClass(loaded_class);
|
2011-07-04 18:20:27 +01:00
|
|
|
|
2011-07-08 17:29:57 +01:00
|
|
|
log("[Loaded " + name + "]");
|
2011-07-06 16:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return loaded_class;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-07-08 17:29:57 +01:00
|
|
|
this.verifyAndLoadClass = function(loaded_class){
|
2011-07-06 16:30:18 +01:00
|
|
|
var superClass;
|
|
|
|
if(loaded_class.super_class){
|
2011-07-08 17:29:57 +01:00
|
|
|
// if super_class not java.lang.Object
|
2011-07-06 16:30:18 +01:00
|
|
|
superClass = canonicalName(loaded_class.super_class.name_ref);
|
2011-07-08 17:29:57 +01:00
|
|
|
this.classForName(superClass);
|
2011-07-06 16:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// this doesn't seem right. doing this will cause the entire JRE to be loaded
|
|
|
|
// as soon as you start JVM.
|
2011-07-04 18:20:27 +01:00
|
|
|
|
2011-07-06 16:30:18 +01:00
|
|
|
/*
|
|
|
|
var that = this;
|
|
|
|
|
2011-07-04 18:20:27 +01:00
|
|
|
loaded_class.constantPool.each(function(constant){
|
|
|
|
if (constant.name_ref.str.charAt(0) == "[") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
that.verifyAndLoadClass(canonicalName(constant.name_ref));
|
2011-07-06 16:30:18 +01:00
|
|
|
}, CONSTANT_Class);*/
|
2011-07-04 18:20:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
this.run = function (){
|
2011-07-06 16:30:18 +01:00
|
|
|
this.java_lang_object = this.classForName("java.lang.Object");
|
|
|
|
this.java_lang_cloneable = this.classForName("java.lang.Cloneable");
|
|
|
|
this.java_io_serializable = this.classForName("java.io.Serializable");
|
2011-07-04 18:20:27 +01:00
|
|
|
var mainClass = this.args[0];
|
2011-07-06 16:30:18 +01:00
|
|
|
this.classForName(mainClass);
|
2011-07-04 18:20:27 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
var JVMThread = function(){
|
|
|
|
this.pc = null;
|
|
|
|
this.stack = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
var JVMFrame = function(){
|
|
|
|
this.local_variables = [];
|
|
|
|
this.operand_stack = [];
|
|
|
|
|
2011-07-06 16:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function JVM_THROWS_NEW(exception){
|
|
|
|
throw "VER: throws new " + exception;
|
|
|
|
}
|
|
|
|
|
2011-07-08 17:29:57 +01:00
|
|
|
function interpret(frame){
|
2011-07-06 16:30:18 +01:00
|
|
|
var operand_stack = frame.operand_stack;
|
2011-07-08 17:29:57 +01:00
|
|
|
var local_variables = frame.local_variables;
|
|
|
|
var code = 0; //resolve code from method;
|
2011-07-06 16:30:18 +01:00
|
|
|
|
|
|
|
switch(opcode){
|
|
|
|
case 0x32: // aaload
|
|
|
|
var index = operand_stack.pop();
|
|
|
|
var arrayref = operand_stack.pop();
|
|
|
|
|
|
|
|
if (arrayref == NULL){
|
|
|
|
JVM_THROWS_NEW("java.lang.NullPointerException");
|
|
|
|
}
|
|
|
|
if (index >= arrayref.length){
|
|
|
|
JVM_THROWS_NEW("java.lang.ArrayIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
operand_stack.push(arrayref.value[index.value]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x53: // aastore
|
|
|
|
var value = operand_stack.pop();
|
|
|
|
var index = operand_stack.pop();
|
|
|
|
var arrayref = operand_stack.pop();
|
|
|
|
|
|
|
|
if (arrayref == NULL){
|
|
|
|
JVM_THROWS_NEW("java.lang.NullPointerException");
|
|
|
|
}
|
|
|
|
if (index >= arrayref.length){
|
|
|
|
JVM_THROWS_NEW("java.lang.ArrayIndexOutOfBoundsException");
|
|
|
|
}
|
2011-07-08 17:29:57 +01:00
|
|
|
if (!arrayref.classRef.isAssignable(value.cl)){
|
|
|
|
JVM_THROWS_NEW("java.lang.ArrayStoreException");
|
2011-07-06 16:30:18 +01:00
|
|
|
}
|
2011-07-08 17:29:57 +01:00
|
|
|
break;
|
2011-07-06 16:30:18 +01:00
|
|
|
|
2011-07-08 17:29:57 +01:00
|
|
|
case 0x1: //aconst_null
|
|
|
|
operand_stack.push(null);
|
|
|
|
break;
|
|
|
|
case 0x19: //aload
|
|
|
|
var i = operand_stack.pop();
|
|
|
|
operand_stack.push(local_variables[i])
|
|
|
|
break;
|
|
|
|
case 0x2a:
|
|
|
|
case 0x2b:
|
|
|
|
case 0x2c:
|
|
|
|
case 0x2d: // aload_<n>
|
|
|
|
operand_stack.push(local_variables[opcode - 0x2a]);
|
|
|
|
break;
|
|
|
|
case 0xbd: // anewarray
|
|
|
|
var indexbyte1 = code.pop();
|
|
|
|
var indexbyte2 = code.pop();
|
|
|
|
var count = operand_stack.pop();
|
|
|
|
if (count < 0){
|
|
|
|
JVM_THROWS_NEW("java.lang.NegativeArraySizeException");
|
|
|
|
}
|
|
|
|
var clRef = frame.classRef.constantPool.get((indexbyte1 << 8) | indexbyte2);
|
|
|
|
var instance = {length:count, value:[], classRef:this.jvm.classForName(clRef)};
|
|
|
|
operand_stack.push(instance);
|
|
|
|
break;
|
|
|
|
case 0xb0: // areturn
|
|
|
|
var objectref = operand_stack.pop();
|
|
|
|
return {return_object: objectref}
|
|
|
|
case 0xbe: // arraylength
|
|
|
|
var arrayref = operand_stack.pop();
|
|
|
|
operand_stack.push(arrayref.length);
|
|
|
|
break;
|
|
|
|
case 0x3a: // astore
|
|
|
|
var objectref = operand_stack.pop();
|
|
|
|
local_variables[code.pop()] = objectref;
|
|
|
|
break;
|
|
|
|
case 0x4b:
|
|
|
|
case 0x4c:
|
|
|
|
case 0x4d:
|
|
|
|
case 0x4e: // astore_<n>
|
|
|
|
var objectref = operand_stack.pop();
|
|
|
|
local_variables[opcode-0x4b] = objectref;
|
|
|
|
break;
|
|
|
|
case 0xbf: // athrow
|
|
|
|
var objectref = operand_stack.pop();
|
|
|
|
if (objectref == null){
|
|
|
|
JVM_THROWS_NEW("java.lang.NullPointerException");
|
|
|
|
}
|
|
|
|
throw [objectref.classRef.name_ref.str,objectref];
|
|
|
|
case 0x33: // baload
|
|
|
|
var value = operand_stack.pop();
|
|
|
|
var index = operand_stack.pop();
|
|
|
|
var arrayref = operand_stack.pop();
|
|
|
|
|
2011-07-06 16:30:18 +01:00
|
|
|
|
2011-07-08 17:29:57 +01:00
|
|
|
if (arrayref == NULL){
|
|
|
|
JVM_THROWS_NEW("java.lang.NullPointerException");
|
|
|
|
}
|
|
|
|
if (index >= arrayref.length){
|
|
|
|
JVM_THROWS_NEW("java.lang.ArrayIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
arrayref.value[index] = value;
|
|
|
|
break;
|
|
|
|
case 0x10: // bipush
|
|
|
|
operand_stack.push(code.pop());
|
|
|
|
break;
|
|
|
|
case 0x34: // caload
|
|
|
|
var index = operand_stack.pop();
|
|
|
|
var arrayref = operand_stack.pop();
|
|
|
|
|
|
|
|
operand_stack.push(arrayref.value[index]);
|
|
|
|
break;
|
|
|
|
case 0x55: // castore
|
|
|
|
var value = operand_stack.pop();
|
|
|
|
var index = operand_stack.pop();
|
|
|
|
var arrayref = operand_stack.pop();
|
|
|
|
|
2011-07-06 16:30:18 +01:00
|
|
|
|
2011-07-08 17:29:57 +01:00
|
|
|
if (arrayref == NULL){
|
|
|
|
JVM_THROWS_NEW("java.lang.NullPointerException");
|
|
|
|
}
|
|
|
|
if (index >= arrayref.length){
|
|
|
|
JVM_THROWS_NEW("java.lang.ArrayIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
arrayref.value[index] = value;
|
|
|
|
break;
|
|
|
|
case 0xc0: // checkcast
|
|
|
|
var objectref = operand_stack.pop();
|
|
|
|
var indexbyte1 = code.pop();
|
|
|
|
var indexbyte2 = code.pop();
|
|
|
|
var clRef = frame.classRef.constantPool.get((indexbyte1 << 8) | indexbyte2);
|
|
|
|
if (objectref.classRef.isAssignable(clRef)){
|
|
|
|
operand_stack.push(objectref);
|
|
|
|
}else{
|
|
|
|
JVM_THROWS_NEW("java.lang.ClassCastException");
|
|
|
|
}
|
2011-07-06 16:30:18 +01:00
|
|
|
}
|
2011-07-04 18:20:27 +01:00
|
|
|
}
|