2014-07-13 20:12:57 +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';
|
|
|
|
|
2014-08-02 09:47:26 +04:00
|
|
|
var FieldInfo = (function() {
|
|
|
|
var idgen = 0;
|
|
|
|
return function(classInfo, access_flags, name, signature) {
|
|
|
|
this.classInfo = classInfo;
|
|
|
|
this.access_flags = access_flags;
|
|
|
|
this.name = name;
|
|
|
|
this.signature = signature;
|
|
|
|
this.id = idgen++;
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
FieldInfo.prototype.get = function(obj) {
|
|
|
|
var value = obj[this.id];
|
|
|
|
if (typeof value === "undefined") {
|
|
|
|
value = util.defaultValue(this.signature);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
FieldInfo.prototype.set = function(obj, value) {
|
|
|
|
obj[this.id] = value;
|
|
|
|
}
|
|
|
|
|
2014-09-12 11:58:44 +04:00
|
|
|
FieldInfo.prototype.toString = function() {
|
|
|
|
return "[field " + this.name + "]";
|
|
|
|
}
|
|
|
|
|
2014-10-07 22:14:05 +04:00
|
|
|
function missingNativeImpl(key, ctx, stack) {
|
|
|
|
console.error("Attempted to invoke missing native:", key);
|
|
|
|
}
|
|
|
|
|
2014-10-10 03:20:18 +04:00
|
|
|
/**
|
|
|
|
* Required params:
|
|
|
|
* - name
|
|
|
|
* - signature
|
|
|
|
* - classInfo
|
|
|
|
*
|
|
|
|
* Optional params:
|
|
|
|
* - attributes (defaults to [])
|
|
|
|
* - code (if not provided, pulls from attributes)
|
|
|
|
* - isNative, isPublic, isStatic, isSynchronized
|
|
|
|
*/
|
|
|
|
function MethodInfo(opts) {
|
|
|
|
this.name = opts.name;
|
|
|
|
this.signature = opts.signature;
|
|
|
|
this.classInfo = opts.classInfo;
|
|
|
|
this.attributes = opts.attributes || [];
|
|
|
|
|
|
|
|
// Use code if provided, otherwise search for the code within attributes.
|
|
|
|
if (opts.code) {
|
|
|
|
this.code = opts.code;
|
|
|
|
this.exception_table = [];
|
|
|
|
this.max_locals = undefined; // Unused for now.
|
|
|
|
} else {
|
|
|
|
for (var i = 0; i < this.attributes.length; i++) {
|
|
|
|
var a = this.attributes[i];
|
|
|
|
if (a.info.type === ATTRIBUTE_TYPES.Code) {
|
|
|
|
this.code = new Uint8Array(a.info.code);
|
|
|
|
this.exception_table = a.info.exception_table;
|
|
|
|
this.max_locals = a.info.max_locals;
|
|
|
|
break;
|
|
|
|
}
|
2014-10-07 02:32:40 +04:00
|
|
|
}
|
2014-10-07 17:29:53 +04:00
|
|
|
}
|
2014-10-07 02:32:40 +04:00
|
|
|
|
2014-10-10 03:20:18 +04:00
|
|
|
this.isNative = opts.isNative;
|
|
|
|
this.isPublic = opts.isPublic;
|
|
|
|
this.isStatic = opts.isStatic;
|
|
|
|
this.isSynchronized = opts.isSynchronized;
|
2014-10-07 02:32:40 +04:00
|
|
|
|
2014-10-07 22:14:05 +04:00
|
|
|
this.key = (this.isStatic ? "S." : "I.") + this.name + "." + this.signature;
|
|
|
|
this.implKey = this.classInfo.className + "." + this.name + "." + this.signature;
|
2014-10-07 02:32:40 +04:00
|
|
|
|
2014-10-07 22:14:05 +04:00
|
|
|
if (this.isNative) {
|
|
|
|
if (this.implKey in Native) {
|
|
|
|
this.alternateImpl = Native[this.implKey];
|
|
|
|
} else {
|
|
|
|
// Some Native MethodInfos are constructed but never called;
|
|
|
|
// that's fine, unless we actually try to call them.
|
|
|
|
this.alternateImpl = missingNativeImpl.bind(null, this.implKey);
|
|
|
|
}
|
|
|
|
} else if (this.implKey in Override) {
|
|
|
|
this.alternateImpl = Override[this.implKey];
|
|
|
|
} else {
|
|
|
|
this.alternateImpl = null;
|
|
|
|
}
|
2014-10-10 00:57:18 +04:00
|
|
|
|
2014-10-10 02:10:29 +04:00
|
|
|
this.consumes = Signature.getINSlots(this.signature);
|
2014-10-10 12:30:30 +04:00
|
|
|
if (!this.isStatic) {
|
2014-10-10 02:10:29 +04:00
|
|
|
this.consumes++;
|
|
|
|
}
|
2014-10-13 20:34:23 +04:00
|
|
|
|
2014-10-21 21:40:25 +04:00
|
|
|
this.numCalled = urlParams.numCalled || 0;
|
2014-10-10 00:46:12 +04:00
|
|
|
this.compiled = null;
|
|
|
|
this.dontCompile = false;
|
2014-10-07 02:32:40 +04:00
|
|
|
}
|
|
|
|
|
2014-07-13 20:12:57 +04:00
|
|
|
var ClassInfo = function(classBytes) {
|
2014-07-14 02:32:58 +04:00
|
|
|
var classImage = getClassImage(classBytes, this);
|
|
|
|
var cp = classImage.constant_pool;
|
|
|
|
this.className = cp[cp[classImage.this_class].name_index].bytes;
|
|
|
|
this.superClassName = classImage.super_class ? cp[cp[classImage.super_class].name_index].bytes : null;
|
|
|
|
this.access_flags = classImage.access_flags;
|
|
|
|
this.constant_pool = cp;
|
2014-07-26 07:14:19 +04:00
|
|
|
this.constructor = function () {
|
|
|
|
}
|
|
|
|
this.constructor.prototype.class = this;
|
2014-09-12 11:58:44 +04:00
|
|
|
this.constructor.prototype.toString = function() {
|
|
|
|
return "[instance " + this.class.className + "]";
|
|
|
|
}
|
2014-09-25 23:52:38 +04:00
|
|
|
// Cache for virtual methods and fields
|
2014-09-25 07:20:49 +04:00
|
|
|
this.vmc = {};
|
2014-09-25 23:52:38 +04:00
|
|
|
this.vfc = {};
|
2014-07-13 21:37:28 +04:00
|
|
|
|
2014-07-14 02:32:58 +04:00
|
|
|
var self = this;
|
2014-07-13 21:37:28 +04:00
|
|
|
|
2014-07-18 10:26:34 +04:00
|
|
|
this.interfaces = [];
|
|
|
|
classImage.interfaces.forEach(function(i) {
|
2014-08-22 00:09:58 +04:00
|
|
|
var int = CLASSES.loadClass(cp[cp[i].name_index].bytes);
|
|
|
|
self.interfaces.push(int);
|
|
|
|
self.interfaces = self.interfaces.concat(int.interfaces);
|
2014-07-18 10:26:34 +04:00
|
|
|
});
|
|
|
|
|
2014-07-14 02:32:58 +04:00
|
|
|
this.fields = [];
|
|
|
|
classImage.fields.forEach(function(f) {
|
2014-08-02 09:47:26 +04:00
|
|
|
var field = new FieldInfo(self, f.access_flags, cp[f.name_index].bytes, cp[f.descriptor_index].bytes);
|
2014-08-01 05:29:17 +04:00
|
|
|
f.attributes.forEach(function(attribute) {
|
|
|
|
if (cp[attribute.attribute_name_index].bytes === "ConstantValue")
|
|
|
|
field.constantValue = new DataView(attribute.info).getUint16(0, false);
|
2014-07-13 20:52:01 +04:00
|
|
|
});
|
2014-08-01 05:29:17 +04:00
|
|
|
self.fields.push(field);
|
2014-07-14 02:32:58 +04:00
|
|
|
});
|
2014-07-13 21:37:28 +04:00
|
|
|
|
2014-07-14 02:32:58 +04:00
|
|
|
this.methods = [];
|
|
|
|
classImage.methods.forEach(function(m) {
|
2014-10-10 03:20:18 +04:00
|
|
|
self.methods.push(new MethodInfo({
|
|
|
|
name: cp[m.name_index].bytes,
|
|
|
|
signature: cp[m.signature_index].bytes,
|
|
|
|
classInfo: self,
|
|
|
|
attributes: m.attributes,
|
|
|
|
isNative: ACCESS_FLAGS.isNative(m.access_flags),
|
|
|
|
isPublic: ACCESS_FLAGS.isPublic(m.access_flags),
|
|
|
|
isStatic: ACCESS_FLAGS.isStatic(m.access_flags),
|
|
|
|
isSynchronized: ACCESS_FLAGS.isSynchronized(m.access_flags)
|
|
|
|
}));
|
2014-07-14 02:32:58 +04:00
|
|
|
});
|
|
|
|
|
2014-07-16 03:49:00 +04:00
|
|
|
var classes = this.classes = [];
|
2014-07-14 02:32:58 +04:00
|
|
|
classImage.attributes.forEach(function(a) {
|
|
|
|
if (a.info.type === ATTRIBUTE_TYPES.InnerClasses) {
|
|
|
|
a.info.classes.forEach(function(c) {
|
|
|
|
classes.push(cp[cp[c.inner_class_info_index].name_index].bytes);
|
2014-07-31 04:40:39 +04:00
|
|
|
if (c.outer_class_info_index)
|
|
|
|
classes.push(cp[cp[c.outer_class_info_index].name_index].bytes);
|
2014-07-14 02:32:58 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2014-07-13 20:12:57 +04:00
|
|
|
}
|
2014-07-14 04:02:57 +04:00
|
|
|
|
2014-07-18 10:44:06 +04:00
|
|
|
ClassInfo.prototype.implementsInterface = function(iface) {
|
2014-07-18 10:02:28 +04:00
|
|
|
var classInfo = this;
|
|
|
|
do {
|
2014-07-18 10:44:06 +04:00
|
|
|
var interfaces = classInfo.interfaces;
|
|
|
|
for (var n = 0; n < interfaces.length; ++n) {
|
|
|
|
if (interfaces[n] === iface)
|
|
|
|
return true;
|
|
|
|
}
|
2014-07-18 10:02:28 +04:00
|
|
|
classInfo = classInfo.superClass;
|
|
|
|
} while (classInfo);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-18 22:31:05 +04:00
|
|
|
ClassInfo.prototype.isAssignableTo = function(toClass) {
|
2014-07-21 04:10:12 +04:00
|
|
|
if (this === toClass || toClass === ClassInfo.java_lang_Object)
|
2014-07-18 10:44:06 +04:00
|
|
|
return true;
|
2014-07-27 01:18:44 +04:00
|
|
|
if (ACCESS_FLAGS.isInterface(toClass.access_flags) && this.implementsInterface(toClass))
|
|
|
|
return true;
|
2014-07-20 00:18:45 +04:00
|
|
|
if (this.elementClass && toClass.elementClass)
|
|
|
|
return this.elementClass.isAssignableTo(toClass.elementClass);
|
2014-07-18 22:31:05 +04:00
|
|
|
return this.superClass ? this.superClass.isAssignableTo(toClass) : false;
|
2014-07-18 10:44:06 +04:00
|
|
|
}
|
|
|
|
|
2014-08-07 03:48:17 +04:00
|
|
|
ClassInfo.prototype.getClassObject = function(ctx) {
|
2014-08-07 21:10:54 +04:00
|
|
|
var className = this.className;
|
|
|
|
var classObjects = ctx.runtime.classObjects;
|
|
|
|
var classObject = classObjects[className];
|
|
|
|
if (!classObject) {
|
2014-10-27 15:13:54 +03:00
|
|
|
classObject = util.newObject(CLASSES.java_lang_Class);
|
2014-08-07 21:10:54 +04:00
|
|
|
classObject.vmClass = this;
|
|
|
|
classObjects[className] = classObject;
|
|
|
|
}
|
|
|
|
return classObject;
|
2014-07-18 10:02:28 +04:00
|
|
|
}
|
|
|
|
|
2014-09-26 08:08:51 +04:00
|
|
|
ClassInfo.prototype.getField = function(fieldKey) {
|
|
|
|
return CLASSES.getField(this, fieldKey);
|
2014-08-02 09:47:26 +04:00
|
|
|
}
|
|
|
|
|
2014-09-12 11:58:44 +04:00
|
|
|
ClassInfo.prototype.toString = function() {
|
|
|
|
return "[class " + this.className + "]";
|
|
|
|
}
|
|
|
|
|
2014-07-20 00:18:45 +04:00
|
|
|
var ArrayClass = function(className, elementClass) {
|
2014-07-18 12:25:12 +04:00
|
|
|
this.className = className;
|
2014-07-14 04:02:57 +04:00
|
|
|
this.superClassName = "java/lang/Object";
|
|
|
|
this.access_flags = 0;
|
2014-07-20 00:18:45 +04:00
|
|
|
this.elementClass = elementClass;
|
2014-09-26 06:20:41 +04:00
|
|
|
this.vmc = {};
|
|
|
|
this.vfc = {};
|
2014-07-14 04:02:57 +04:00
|
|
|
}
|
2014-07-18 10:11:45 +04:00
|
|
|
|
2014-07-27 05:20:54 +04:00
|
|
|
ArrayClass.prototype.methods = [];
|
|
|
|
|
2014-07-18 10:11:45 +04:00
|
|
|
ArrayClass.prototype.isArrayClass = true;
|
2014-07-20 00:18:45 +04:00
|
|
|
|
|
|
|
ArrayClass.prototype.implementsInterface = function(iface) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayClass.prototype.isAssignableTo = ClassInfo.prototype.isAssignableTo;
|
|
|
|
|
|
|
|
ArrayClass.prototype.getClassObject = ClassInfo.prototype.getClassObject;
|