primary support for arrays and bytecode interpret is at letter D
This commit is contained in:
parent
edb0b77a61
commit
2d6305c1bc
86
class.js
86
class.js
@ -52,7 +52,58 @@ var log = function (msg){
|
||||
}
|
||||
}
|
||||
|
||||
var ClassDefinition = function (file,jvm){
|
||||
var ClassDefinition = function(jvm) {
|
||||
this.jvm = jvm;
|
||||
};
|
||||
|
||||
// bad bad code! This shouldn't be done this way!
|
||||
ClassDefinition.prototype.makeForArray = function(arrayDef){
|
||||
this.magic = 0xCAFEBABE;
|
||||
this.minorVersion = 0;
|
||||
this.majorVersion = 50;
|
||||
this.constantPool = [];
|
||||
this.access_flags = 0;
|
||||
this.this_class = {id:CONSTANT_Class, name_ref:{str:arrayDef}};
|
||||
this.super_class = null;
|
||||
this.interface_count = 0;
|
||||
this.interfaces = [];
|
||||
this.fields_count = 0;
|
||||
this.fields = [];
|
||||
this.methods_count = 1;
|
||||
this.methods = [
|
||||
{
|
||||
access_flags:0,
|
||||
name_ref: {str:"clone"},
|
||||
descriptor_ref: {str: "()Ljava.lang.Object;"},
|
||||
attributes_count:0,
|
||||
attributes: []
|
||||
}
|
||||
];
|
||||
ClassDefinition.prototype.isArrayClass = function (){
|
||||
return true;
|
||||
}
|
||||
|
||||
// extra info:
|
||||
this.dimensions = 0;
|
||||
var i=0;
|
||||
for (i=0; i<arrayDef.length; i++){
|
||||
if (arrayDef.charAt(i) == "["){
|
||||
this.dimensions += 1;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.arrayType = arrayDef.substr(1,arrayDef.length);
|
||||
if (this.arrayType.charAt(0) == "L"){
|
||||
this.arrayTypeClass = arrayDef.substr(i+1,arrayDef.length-1);
|
||||
this.jvm.classForName(this.arrayTypeDef);
|
||||
}else{
|
||||
this.arrayTypeClass = "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ClassDefinition.prototype.loadFromFile = function (file){
|
||||
var dataStream = new DataStream(slurpFile(file)[1]);
|
||||
this.magic = dataStream.getU4();
|
||||
if (this.magic != 0xCAFEBABE){
|
||||
@ -63,7 +114,8 @@ var ClassDefinition = function (file,jvm){
|
||||
if (this.majorVersion > 50 || this.majorVersion < 45){
|
||||
throw "Unsuported java class file format version";
|
||||
}
|
||||
this.constantPool = new ConstantPool(dataStream);
|
||||
this.constantPool = new ConstantPool();
|
||||
this.constantPool.loadFromStream(dataStream);
|
||||
this.access_flags = dataStream.getU2();
|
||||
|
||||
this.this_class = ConstantPoolRef(dataStream.getU2(), this.constantPool, CONSTANT_Class);
|
||||
@ -93,9 +145,14 @@ var ClassDefinition = function (file,jvm){
|
||||
this.methods[i] = new MethodInfo(dataStream, this.constantPool);
|
||||
}
|
||||
|
||||
this.attributes_count = dataStream.getU2();
|
||||
this.attributes = [];
|
||||
for (var i=0; i<this.attributes_count; i++){
|
||||
this.attributes[i] = Attribute(dataStream,this.constantPool);
|
||||
}
|
||||
|
||||
// Post added info;
|
||||
this.jvm = jvm;
|
||||
this className = this.this_class.name_ref.str.replace(/\//g,".");
|
||||
this.className = this.this_class.name_ref.str.replace(/\//g,".");
|
||||
}
|
||||
|
||||
ClassDefinition.prototype.isInterface = function () {
|
||||
@ -118,7 +175,16 @@ ClassDefinition.prototype.isAssignable = function (T) {
|
||||
if (T.isInterface()){
|
||||
return (T == this.jvm.java_lang_cloneable || T == this.jvm.java_io_serializable);
|
||||
}else if (T.isArrayClass()){
|
||||
|
||||
if (T.arrayType.charAt(i) == this.arrayType.charAt(i) && (this.arrayType.charAt(i) == "L" || this.arrayType.charAt(i) == "[")){
|
||||
return this.jvm.classForName(this.arrayType).isAssignable(T.arrayType);
|
||||
}else{
|
||||
// VER: check if elements are primitive.
|
||||
if (T.arrayType.charAt(i) == this.arrayType.charAt(i)){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return T == this.jvm.java_lang_object;
|
||||
}
|
||||
@ -157,7 +223,15 @@ ClassDefinition.prototype.isInterfaceOrSuperInterface = function(I){
|
||||
}
|
||||
|
||||
function LoadClassFile (x,jvm){
|
||||
return new ClassDefinition(x,jvm);
|
||||
var def = new ClassDefinition(jvm);
|
||||
if (x.charAt(0) != "[" ){
|
||||
def.loadFromFile(x);
|
||||
}else{
|
||||
def.makeForArray(x);
|
||||
}
|
||||
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,34 +40,7 @@ var constUtf8 = function(){
|
||||
}else{
|
||||
result = byte_x
|
||||
}
|
||||
strBuf += String.fromCharCode(byte_x);
|
||||
|
||||
|
||||
/*
|
||||
|
||||
if ((one_byte >> 7) == 1) {
|
||||
var tmp;
|
||||
|
||||
// its a multi-byte character
|
||||
tmp = (one_byte & 0x3f); // Bits 5..0 (six bits)
|
||||
// read the next byte
|
||||
one_byte = dStream.getU1();
|
||||
charCnt++;
|
||||
tmp = (tmp | ((one_byte & 0x3f) << 6));
|
||||
if ((one_byte >> 6) == 0x2) {
|
||||
// We have 12 bits so far, get bits 15..12
|
||||
one_byte = dStream .getU1();
|
||||
charCnt++;
|
||||
one_byte = (one_byte & 0xf);
|
||||
tmp = (tmp | (one_byte << 12));
|
||||
}
|
||||
one_char = tmp;
|
||||
}
|
||||
else {
|
||||
one_char = one_byte;
|
||||
}
|
||||
strBuf += String.fromCharCode(one_char);
|
||||
*/
|
||||
strBuf += String.fromCharCode(byte_x);
|
||||
} // while
|
||||
this.str = strBuf.toString();
|
||||
} // read
|
||||
@ -300,7 +273,10 @@ var ConstantPoolRef = function(index, constantPool, expected){
|
||||
return result;
|
||||
}
|
||||
|
||||
var ConstantPool = function(dStream){
|
||||
var ConstantPool = function(){
|
||||
}
|
||||
|
||||
ConstantPool.prototype.loadFromStream = function(dStream){
|
||||
this.constantPoolCount = dStream.getU2();
|
||||
this.constantPool = [];
|
||||
for(var i = 1; i < this.constantPoolCount; i++){
|
||||
@ -328,4 +304,8 @@ var ConstantPool = function(dStream){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConstantPool.prototype.get = function (i){
|
||||
return this.constantPoll[i];
|
||||
}
|
117
cpu.js
117
cpu.js
@ -12,27 +12,26 @@ var JVM = function(params,args){
|
||||
|
||||
this.classForName = function (name){
|
||||
var superClass, loaded_class = this.method_area[name];
|
||||
|
||||
|
||||
if (!loaded_class){
|
||||
loaded_class = LoadClassFile(name, this);
|
||||
this.method_area[className] = loaded_class;
|
||||
this.method_area[name] = loaded_class;
|
||||
this.verifyAndLoadClass(loaded_class);
|
||||
|
||||
log("[Loaded " + className + " from runtime/" + classFile + "]");
|
||||
log("[Loaded " + name + "]");
|
||||
}
|
||||
|
||||
return loaded_class;
|
||||
|
||||
}
|
||||
|
||||
this.verifyAndLoadClass = function(classFile){
|
||||
this.verifyAndLoadClass = function(loaded_class){
|
||||
var superClass;
|
||||
if(loaded_class.super_class){
|
||||
// if super_class not java.lang.Object
|
||||
superClass = canonicalName(loaded_class.super_class.name_ref);
|
||||
}else{
|
||||
superClass = "java.lang.Object";
|
||||
this.classForName(superClass);
|
||||
}
|
||||
this.classForName(superClass);
|
||||
|
||||
// this doesn't seem right. doing this will cause the entire JRE to be loaded
|
||||
// as soon as you start JVM.
|
||||
@ -52,7 +51,6 @@ var JVM = function(params,args){
|
||||
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");
|
||||
|
||||
var mainClass = this.args[0];
|
||||
this.classForName(mainClass);
|
||||
};
|
||||
@ -73,8 +71,10 @@ function JVM_THROWS_NEW(exception){
|
||||
throw "VER: throws new " + exception;
|
||||
}
|
||||
|
||||
function (frame){
|
||||
function interpret(frame){
|
||||
var operand_stack = frame.operand_stack;
|
||||
var local_variables = frame.local_variables;
|
||||
var code = 0; //resolve code from method;
|
||||
|
||||
switch(opcode){
|
||||
case 0x32: // aaload
|
||||
@ -101,12 +101,105 @@ function (frame){
|
||||
if (index >= arrayref.length){
|
||||
JVM_THROWS_NEW("java.lang.ArrayIndexOutOfBoundsException");
|
||||
}
|
||||
|
||||
if (value.ref_of == REF_TYPE_class){
|
||||
if (isSubClass(value.ref_of,arrayref.of));
|
||||
if (!arrayref.classRef.isAssignable(value.cl)){
|
||||
JVM_THROWS_NEW("java.lang.ArrayStoreException");
|
||||
}
|
||||
break;
|
||||
|
||||
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();
|
||||
|
||||
|
||||
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();
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user