primary support for arrays and bytecode interpret is at letter D

This commit is contained in:
Artur Ventura 2011-07-08 17:29:57 +01:00
parent edb0b77a61
commit 2d6305c1bc
4 changed files with 197 additions and 49 deletions

View File

@ -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;
}

View File

@ -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
View File

@ -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");
}
}
}

View File

@ -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();
}