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) {
|
|
|
|
var classArea = new ClassArea(bytes);
|
|
|
|
this.classes[classArea.getClassName()] = classArea;
|
|
|
|
return classArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.loadClassFile = function(fileName) {
|
|
|
|
LOG.debug("loading " + fileName + " ...");
|
|
|
|
var bytes = this.loadFile(fileName);
|
|
|
|
if (!bytes)
|
|
|
|
return null;
|
|
|
|
var ca = this.loadClassBytes(bytes);
|
|
|
|
var classes = ca.getClasses();
|
|
|
|
for (var i=0; i<classes.length; i++) {
|
|
|
|
if (!this.classes[classes[i]]) {
|
|
|
|
this.loadClassFile(path.dirname(fileName) + path.sep + classes[i] + ".class");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ca;
|
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.getEntryPoint = function(className, methodName) {
|
|
|
|
for(var name in this.classes) {
|
|
|
|
var ca = this.classes[name];
|
|
|
|
if (ca instanceof ClassArea) {
|
|
|
|
if (!className || (className === ca.getClassName())) {
|
|
|
|
if (ACCESS_FLAGS.isPublic(ca.getAccessFlags())) {
|
|
|
|
var methods = ca.getMethods();
|
|
|
|
var cp = ca.getConstantPool();
|
|
|
|
for(var i=0; i<methods.length; i++) {
|
|
|
|
if
|
|
|
|
(
|
|
|
|
ACCESS_FLAGS.isPublic(methods[i].access_flags) &&
|
|
|
|
ACCESS_FLAGS.isStatic(methods[i].access_flags) &&
|
|
|
|
cp[methods[i].name_index].bytes === methodName
|
|
|
|
)
|
|
|
|
{ return new Frame(ca, methods[i]); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-09 11:12:58 +04:00
|
|
|
Classes.prototype.initClass = function(className) {
|
|
|
|
var clinit = this.getStaticMethod(className, "<clinit>", "()V");
|
2014-07-09 12:07:14 +04:00
|
|
|
if (!clinit)
|
|
|
|
return;
|
|
|
|
SCHEDULER.sync(function() {
|
|
|
|
LOG.debug("call " + className + ".<clinit> ...");
|
|
|
|
clinit.run([], function() {
|
|
|
|
LOG.debug("call " + className + ".<clinit> ... done");
|
2014-07-09 11:12:58 +04:00
|
|
|
});
|
2014-07-09 12:07:14 +04:00
|
|
|
});
|
2014-07-09 11:12:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.getClass = function(className, initialize) {
|
2014-07-06 12:29:36 +04:00
|
|
|
var ca = this.classes[className];
|
2014-07-06 13:03:36 +04:00
|
|
|
if (ca)
|
|
|
|
return ca;
|
2014-07-09 11:12:58 +04:00
|
|
|
if (!!(ca = this.loadClassFile(className + ".class"))) {
|
|
|
|
if (initialize) {
|
|
|
|
ca.staticFields = {};
|
|
|
|
this.initClass(className);
|
|
|
|
}
|
2014-07-06 12:29:36 +04:00
|
|
|
return ca;
|
2014-07-09 11:12:58 +04:00
|
|
|
}
|
2014-07-06 12:29:36 +04:00
|
|
|
throw new Error(util.format("Implementation of the %s class is not found.", className));
|
|
|
|
};
|
|
|
|
|
|
|
|
Classes.prototype.getStaticField = function(className, fieldName) {
|
2014-07-09 11:12:58 +04:00
|
|
|
return this.getClass(className, true).staticFields[fieldName];
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.setStaticField = function(className, fieldName, value) {
|
2014-07-09 11:12:58 +04:00
|
|
|
this.getClass(className, true).staticFields[fieldName] = value;
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
|
|
|
|
2014-07-09 12:07:14 +04:00
|
|
|
Classes.prototype.getMethod = function(className, methodName, signature, staticFlag) {
|
|
|
|
// Only force initialization when accessing a static method.
|
|
|
|
var ca = this.getClass(className, staticFlag);
|
2014-07-06 12:29:36 +04:00
|
|
|
if (ca instanceof ClassArea) {
|
|
|
|
var methods = ca.getMethods();
|
|
|
|
var cp = ca.getConstantPool();
|
2014-07-09 12:07:14 +04:00
|
|
|
for(var i=0; i<methods.length; i++) {
|
|
|
|
if (ACCESS_FLAGS.isStatic(methods[i].access_flags) === !!staticFlag)
|
2014-07-06 12:29:36 +04:00
|
|
|
if (cp[methods[i].name_index].bytes === methodName)
|
|
|
|
if (signature.toString() === cp[methods[i].signature_index].bytes)
|
|
|
|
return new Frame(ca, methods[i]);
|
2014-07-09 12:07:14 +04:00
|
|
|
}
|
2014-07-06 12:29:36 +04:00
|
|
|
} else {
|
|
|
|
if (methodName in ca) {
|
|
|
|
return ca[methodName];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
2014-07-07 05:16:15 +04:00
|
|
|
|
2014-07-09 12:07:14 +04:00
|
|
|
Classes.prototype.getStaticMethod = function(className, methodName, signature) {
|
|
|
|
return this.getMethod(className, methodName, signature, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
Classes.prototype.newObject = function(className) {
|
|
|
|
// Force initialization of the class (if not already done).
|
2014-07-09 12:28:03 +04:00
|
|
|
return { class: this.getClass(className, true) };
|
|
|
|
}
|
2014-07-07 05:16:15 +04:00
|
|
|
|
2014-07-09 12:28:03 +04:00
|
|
|
Classes.prototype.newArray = function(type, size) {
|
|
|
|
switch (type) {
|
|
|
|
case ARRAY_TYPE.T_BOOLEAN: return new Uint8Array(size);
|
|
|
|
case ARRAY_TYPE.T_CHAR: return new Uint16Array(size);
|
|
|
|
case ARRAY_TYPE.T_FLOAT: return new Float32Array(size);
|
|
|
|
case ARRAY_TYPE.T_DOUBLE: return new Float64Array(size);
|
|
|
|
case ARRAY_TYPE.T_BYTE: return new Int8Array(size);
|
|
|
|
case ARRAY_TYPE.T_SHORT: return new Int16Array(size);
|
|
|
|
case ARRAY_TYPE.T_INT: return new Int32Array(size);
|
|
|
|
case ARRAY_TYPE.T_LONG: return new Int64Array(size);
|
|
|
|
}
|
2014-07-06 12:29:36 +04:00
|
|
|
}
|
2014-07-09 12:28:03 +04:00
|
|
|
|
|
|
|
Classes.prototype.newString = function(s) {
|
|
|
|
var obj = this.newObject("java/lang/String");
|
|
|
|
var length = s.length;
|
|
|
|
var chars = this.newArray(ARRAY_TYPE.T_CHAR, length);
|
|
|
|
for (var n = 0; n < length; ++n)
|
|
|
|
chars[n] = s.charCodeAt(n);
|
|
|
|
obj.value = chars;
|
|
|
|
obj.offset = 0;
|
|
|
|
obj.count = length;
|
|
|
|
return obj;
|
|
|
|
}
|