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';
|
|
|
|
|
|
|
|
var Classes = function() {
|
|
|
|
if (this instanceof Classes) {
|
|
|
|
this.classfiles = [];
|
|
|
|
this.mainclass = [];
|
|
|
|
this.classes = {};
|
|
|
|
} else {
|
|
|
|
return new Classes();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.addPath = function(name, data) {
|
|
|
|
if (name.substr(-4) === ".jar") {
|
|
|
|
data = new ZipFile(data);
|
|
|
|
}
|
|
|
|
this.classfiles[name] = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.loadFile = function(fileName) {
|
2014-07-06 13:03:36 +04:00
|
|
|
var classfiles = this.classfiles;
|
|
|
|
var data = classfiles[fileName];
|
2014-07-06 12:29:36 +04:00
|
|
|
if (data)
|
|
|
|
return data;
|
2014-07-06 13:03:36 +04:00
|
|
|
Object.keys(classfiles).every(function (name) {
|
2014-07-06 12:29:36 +04:00
|
|
|
if (name.substr(-4) !== ".jar")
|
|
|
|
return true;
|
2014-07-06 13:03:36 +04:00
|
|
|
var zip = classfiles[name];
|
2014-07-06 13:38:00 +04:00
|
|
|
if (fileName in zip.directory) {
|
|
|
|
var bytes = zip.read(fileName);
|
|
|
|
data = bytes.buffer.slice(0, bytes.length);
|
|
|
|
}
|
|
|
|
return !data;
|
2014-07-06 12:29:36 +04:00
|
|
|
});
|
2014-07-06 13:03:36 +04:00
|
|
|
classfiles[fileName] = data;
|
2014-07-06 12:29:36 +04:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.loadClassBytes = function(bytes) {
|
2014-07-13 03:42:01 +04:00
|
|
|
var classInfo = new ClassInfo(bytes);
|
2014-07-13 20:28:19 +04:00
|
|
|
this.classes[classInfo.className] = classInfo;
|
2014-07-13 03:42:01 +04:00
|
|
|
return classInfo;
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.loadClassFile = function(fileName) {
|
2014-07-15 11:30:45 +04:00
|
|
|
console.info("loading " + fileName + " ...");
|
2014-07-06 12:29:36 +04:00
|
|
|
var bytes = this.loadFile(fileName);
|
|
|
|
if (!bytes)
|
2014-07-19 22:53:27 +04:00
|
|
|
throw VM.newException("java/lang/ClassNotFoundException", "fileName");
|
2014-07-18 10:26:34 +04:00
|
|
|
var self = this;
|
2014-07-13 03:42:01 +04:00
|
|
|
var classInfo = this.loadClassBytes(bytes);
|
2014-07-18 09:34:59 +04:00
|
|
|
if (classInfo.superClassName)
|
2014-07-18 13:00:10 +04:00
|
|
|
classInfo.superClass = this.loadClass(classInfo.superClassName);
|
2014-07-18 10:26:34 +04:00
|
|
|
var interfaces = classInfo.interfaces;
|
|
|
|
interfaces.forEach(function (i, n) {
|
2014-07-18 13:00:10 +04:00
|
|
|
interfaces[n] = self.loadClass(i);
|
2014-07-18 10:26:34 +04:00
|
|
|
});
|
|
|
|
var classes = classInfo.classes;
|
|
|
|
classes.forEach(function (c, n) {
|
2014-07-19 05:00:10 +04:00
|
|
|
classes[n] = self.loadClass(util.withPath(fileName, c));
|
2014-07-18 09:34:59 +04:00
|
|
|
});
|
2014-07-13 03:42:01 +04:00
|
|
|
return classInfo;
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-18 13:00:10 +04:00
|
|
|
Classes.prototype.loadClass = function(className) {
|
|
|
|
var classInfo = this.classes[className];
|
|
|
|
if (classInfo)
|
|
|
|
return classInfo;
|
|
|
|
return this.loadClassFile(className + ".class");
|
|
|
|
}
|
|
|
|
|
2014-07-17 12:14:18 +04:00
|
|
|
Classes.prototype.getEntryPoint = function(classInfo) {
|
|
|
|
if (ACCESS_FLAGS.isPublic(classInfo.access_flags)) {
|
|
|
|
var methods = classInfo.methods;
|
|
|
|
for (var i=0; i<methods.length; i++) {
|
|
|
|
if (ACCESS_FLAGS.isPublic(methods[i].access_flags) &&
|
|
|
|
ACCESS_FLAGS.isStatic(methods[i].access_flags) &&
|
|
|
|
!ACCESS_FLAGS.isNative(methods[i].access_flags) &&
|
|
|
|
methods[i].name === "main" &&
|
|
|
|
methods[i].signature === "([Ljava/lang/String;)V") {
|
|
|
|
return methods[i];
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
}
|
2014-07-12 20:48:05 +04:00
|
|
|
}
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.initClass = function(classInfo) {
|
2014-07-17 12:37:35 +04:00
|
|
|
if (classInfo.staticFields)
|
|
|
|
return;
|
2014-07-18 09:36:32 +04:00
|
|
|
if (classInfo.superClass)
|
|
|
|
this.initClass(classInfo.superClass);
|
2014-07-17 12:37:35 +04:00
|
|
|
classInfo.staticFields = {};
|
2014-07-19 01:12:18 +04:00
|
|
|
classInfo.constructor = function () {
|
|
|
|
}
|
|
|
|
classInfo.constructor.prototype.class = classInfo;
|
2014-07-18 01:34:22 +04:00
|
|
|
var clinit = this.getMethod(classInfo, "<clinit>", "()V", true, false);
|
|
|
|
if (clinit) {
|
2014-07-18 04:39:25 +04:00
|
|
|
VM.invoke(clinit);
|
2014-07-18 01:34:22 +04:00
|
|
|
}
|
2014-07-17 09:45:10 +04:00
|
|
|
}
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.getClass = function(className, init) {
|
2014-07-13 03:42:01 +04:00
|
|
|
var classInfo = this.classes[className];
|
2014-07-17 12:37:35 +04:00
|
|
|
if (!classInfo) {
|
|
|
|
if (className[0] === "[") {
|
2014-07-20 00:18:45 +04:00
|
|
|
classInfo = this.initArrayClass(className);
|
2014-07-18 13:00:10 +04:00
|
|
|
} else {
|
|
|
|
classInfo = this.loadClass(className);
|
2014-07-17 12:37:35 +04:00
|
|
|
}
|
|
|
|
if (!classInfo)
|
|
|
|
return null;
|
2014-07-09 11:12:58 +04:00
|
|
|
}
|
2014-07-17 12:37:35 +04:00
|
|
|
if (init)
|
2014-07-18 01:34:22 +04:00
|
|
|
this.initClass(classInfo);
|
2014-07-17 12:37:35 +04:00
|
|
|
return classInfo;
|
2014-07-06 12:29:36 +04:00
|
|
|
};
|
|
|
|
|
2014-07-20 00:18:45 +04:00
|
|
|
Classes.prototype.initArrayClass = function(typeName) {
|
2014-07-14 03:31:36 +04:00
|
|
|
var elementType = typeName.substr(1);
|
2014-07-18 12:20:06 +04:00
|
|
|
var constructor = ARRAYS[elementType];
|
|
|
|
if (constructor)
|
2014-07-20 00:18:45 +04:00
|
|
|
return this.initPrimitiveArrayType(typeName, constructor);
|
2014-07-18 13:00:10 +04:00
|
|
|
if (elementType[0] === "L")
|
2014-07-20 00:18:45 +04:00
|
|
|
elementType = elementType.substr(1).replace(";", "");
|
2014-07-18 12:25:12 +04:00
|
|
|
var classInfo = new ArrayClass(typeName, this.getClass(elementType));
|
2014-07-18 13:00:10 +04:00
|
|
|
classInfo.superClass = this.loadClass("java/lang/Object");
|
2014-07-14 04:02:57 +04:00
|
|
|
classInfo.constructor = function (size) {
|
|
|
|
var array = new Array(size);
|
|
|
|
array.class = classInfo;
|
|
|
|
return array;
|
|
|
|
}
|
2014-07-18 13:00:10 +04:00
|
|
|
return this.classes[typeName] = classInfo;
|
2014-07-14 03:31:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-20 00:18:45 +04:00
|
|
|
Classes.prototype.initPrimitiveArrayType = function(typeName, constructor) {
|
|
|
|
var classInfo = new ArrayClass(typeName);
|
2014-07-18 13:00:10 +04:00
|
|
|
classInfo.superClass = this.loadClass("java/lang/Object");
|
2014-07-14 04:02:57 +04:00
|
|
|
constructor.prototype.class = classInfo;
|
|
|
|
classInfo.constructor = constructor;
|
|
|
|
return classInfo;
|
2014-07-14 03:31:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.getStaticField = function(className, fieldName) {
|
|
|
|
return this.getClass(className, true).staticFields[fieldName];
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.setStaticField = function(className, fieldName, value) {
|
|
|
|
this.getClass(className, true).staticFields[fieldName] = value;
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-20 02:53:58 +04:00
|
|
|
Classes.prototype.getField = function(className, fieldName, signature, staticFlag) {
|
|
|
|
var classInfo = this.getClass(className, false);
|
|
|
|
do {
|
|
|
|
var fields = classInfo.fields;
|
|
|
|
for (var i=0; i<fields.length; ++i) {
|
|
|
|
var field = fields[i];
|
|
|
|
if (ACCESS_FLAGS.isStatic(field.access_flags) === !!staticFlag) {
|
|
|
|
if (field.name === fieldName && field.signature === signature) {
|
|
|
|
if (!field.id)
|
|
|
|
field.id = classInfo.className + "$" + fieldName;
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
classInfo = classInfo.superClass;
|
|
|
|
} while (classInfo);
|
|
|
|
};
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.getMethod = function(classInfo, methodName, signature, staticFlag, inheritFlag) {
|
2014-07-18 09:36:32 +04:00
|
|
|
do {
|
2014-07-15 08:18:56 +04:00
|
|
|
var methods = classInfo.methods;
|
2014-07-20 02:53:58 +04:00
|
|
|
for (var i=0; i<methods.length; ++i) {
|
|
|
|
var method = methods[i];
|
|
|
|
if (ACCESS_FLAGS.isStatic(method.access_flags) === !!staticFlag) {
|
|
|
|
if (method.name === methodName && method.signature === signature)
|
|
|
|
return method;
|
2014-07-10 09:23:17 +04:00
|
|
|
}
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
2014-07-18 09:36:32 +04:00
|
|
|
classInfo = classInfo.superClass;
|
|
|
|
} while (classInfo);
|
2014-07-06 12:29:36 +04:00
|
|
|
};
|
2014-07-07 05:16:15 +04:00
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.newObject = function(className) {
|
|
|
|
return new (this.getClass(className, true).constructor)();
|
2014-07-09 12:28:03 +04:00
|
|
|
}
|
2014-07-07 05:16:15 +04:00
|
|
|
|
2014-07-18 12:20:06 +04:00
|
|
|
Classes.prototype.newPrimitiveArray = function(type, size) {
|
|
|
|
var constructor = ARRAYS[type];
|
|
|
|
if (!constructor.prototype.class)
|
|
|
|
this.initPrimitiveArrayType(type, constructor);
|
2014-07-14 04:36:47 +04:00
|
|
|
return constructor.call(null, size);
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
2014-07-09 12:28:03 +04:00
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.newArray = function(typeName, size) {
|
2014-07-20 00:18:45 +04:00
|
|
|
return this.getClass(typeName).constructor.call(null, size);
|
2014-07-16 18:59:37 +04:00
|
|
|
}
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.newMultiArray = function(typeName, lengths) {
|
2014-07-16 19:25:57 +04:00
|
|
|
var length = lengths[0];
|
2014-07-18 10:51:11 +04:00
|
|
|
var array = this.newArray(typeName, length);
|
2014-07-18 13:00:10 +04:00
|
|
|
if (lengths.length > 1) {
|
2014-07-16 19:25:57 +04:00
|
|
|
lengths = lengths.slice(1);
|
|
|
|
for (var i=0; i<length; i++)
|
2014-07-18 01:34:22 +04:00
|
|
|
array[i] = this.newMultiArray(typeName.substr(1), lengths);
|
2014-07-16 19:25:57 +04:00
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
2014-07-18 01:34:22 +04:00
|
|
|
Classes.prototype.newString = function(s) {
|
|
|
|
var obj = this.newObject("java/lang/String");
|
2014-07-09 12:28:03 +04:00
|
|
|
var length = s.length;
|
2014-07-18 12:20:06 +04:00
|
|
|
var chars = this.newPrimitiveArray("C", length);
|
2014-07-09 12:28:03 +04:00
|
|
|
for (var n = 0; n < length; ++n)
|
|
|
|
chars[n] = s.charCodeAt(n);
|
2014-07-20 02:53:58 +04:00
|
|
|
obj["java/lang/String$value"] = chars;
|
|
|
|
obj["java/lang/String$offset"] = 0;
|
|
|
|
obj["java/lang/String$count"] = length;
|
2014-07-09 12:28:03 +04:00
|
|
|
return obj;
|
2014-07-12 20:48:05 +04:00
|
|
|
}
|