diff --git a/class.js b/class.js index d2788b6..e6cd811 100644 --- a/class.js +++ b/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 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> 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]; } \ No newline at end of file diff --git a/cpu.js b/cpu.js index efc8e0b..337715d 100644 --- a/cpu.js +++ b/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_ + 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_ + 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"); + } } } \ No newline at end of file diff --git a/main.js b/main.js index 9d089d1..8a04b12 100644 --- a/main.js +++ b/main.js @@ -5,7 +5,8 @@ include("attributes.js"); include("infos.js"); include("class.js"); include("cpu.js"); - +var test_jvm; function main (args){ - new JVM({},["java.text.DecimalFormat"]).run(); + test_jvm = new JVM({},["[I"]) + test_jvm.run(); } \ No newline at end of file