2014-11-26 12:21:11 +03:00
|
|
|
module J2ME {
|
|
|
|
declare var ZipFile;
|
2014-11-27 14:28:52 +03:00
|
|
|
declare var snarf;
|
2014-11-26 12:21:11 +03:00
|
|
|
|
|
|
|
export class ClassRegistry {
|
2014-11-27 14:28:52 +03:00
|
|
|
/**
|
|
|
|
* List of directories to look for source files in.
|
|
|
|
*/
|
|
|
|
sourceDirectories: string [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* All source code, only ever used for debugging.
|
|
|
|
*/
|
|
|
|
sourceFiles: Map<string, string []>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of classes whose sources files were not found. We keep track
|
|
|
|
* of them so we don't have to search for them over and over.
|
|
|
|
*/
|
|
|
|
missingSourceFiles: Map<string, string []>;
|
|
|
|
|
2014-12-21 10:39:08 +03:00
|
|
|
jarFiles: Map<string, any>;
|
2014-11-26 12:21:11 +03:00
|
|
|
classFiles: Map<string, any>;
|
|
|
|
classes: Map<string, ClassInfo>;
|
|
|
|
|
2014-12-21 10:39:08 +03:00
|
|
|
preInitializedClasses: ClassInfo [];
|
|
|
|
|
2014-11-26 12:21:11 +03:00
|
|
|
java_lang_Object: ClassInfo;
|
|
|
|
java_lang_Class: ClassInfo;
|
|
|
|
java_lang_String: ClassInfo;
|
|
|
|
java_lang_Thread: ClassInfo;
|
|
|
|
|
|
|
|
constructor() {
|
2014-11-27 14:28:52 +03:00
|
|
|
this.sourceDirectories = [];
|
|
|
|
this.sourceFiles = Object.create(null);
|
|
|
|
this.missingSourceFiles = Object.create(null);
|
|
|
|
|
2014-12-21 10:39:08 +03:00
|
|
|
this.jarFiles = Object.create(null);
|
2014-11-26 12:21:11 +03:00
|
|
|
this.classFiles = Object.create(null);
|
|
|
|
this.classes = Object.create(null);
|
2014-12-21 10:39:08 +03:00
|
|
|
this.preInitializedClasses = [];
|
2014-11-26 12:21:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
initializeBuiltinClasses() {
|
|
|
|
// These classes are guaranteed to not have a static initializer.
|
2014-12-24 08:20:07 +03:00
|
|
|
enterTimeline("initializeBuiltinClasses");
|
2015-01-15 04:13:14 +03:00
|
|
|
this.java_lang_Object = this.loadAndLinkClass("java/lang/Object");
|
|
|
|
this.java_lang_Class = this.loadAndLinkClass("java/lang/Class");
|
|
|
|
this.java_lang_String = this.loadAndLinkClass("java/lang/String");
|
|
|
|
this.java_lang_Thread = this.loadAndLinkClass("java/lang/Thread");
|
2014-11-26 12:21:11 +03:00
|
|
|
|
2014-12-21 10:39:08 +03:00
|
|
|
this.preInitializedClasses.push(this.java_lang_Object);
|
|
|
|
this.preInitializedClasses.push(this.java_lang_Class);
|
|
|
|
this.preInitializedClasses.push(this.java_lang_String);
|
|
|
|
this.preInitializedClasses.push(this.java_lang_Thread);
|
|
|
|
|
|
|
|
/**
|
2015-01-16 01:26:56 +03:00
|
|
|
* Force these frequently used classes to be initialized eagerly. We can
|
|
|
|
* skip the class initialization check for them. This is only possible
|
|
|
|
* because they don't have any static state.
|
2014-12-21 10:39:08 +03:00
|
|
|
*/
|
|
|
|
var classNames = [
|
|
|
|
"java/lang/Integer",
|
|
|
|
"java/lang/Character",
|
|
|
|
"java/lang/Math",
|
2015-01-16 01:26:56 +03:00
|
|
|
"java/util/HashtableEntry",
|
|
|
|
"java/lang/StringBuffer",
|
|
|
|
"java/util/Vector",
|
|
|
|
"java/io/IOException",
|
|
|
|
"java/lang/IllegalArgumentException"
|
2014-12-21 10:39:08 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
for (var i = 0; i < classNames.length; i++) {
|
2015-01-15 04:13:14 +03:00
|
|
|
this.preInitializedClasses.push(this.loadAndLinkClass(classNames[i]));
|
2014-12-21 10:39:08 +03:00
|
|
|
}
|
|
|
|
|
2014-12-01 06:49:39 +03:00
|
|
|
// Link primitive values and primitive arrays.
|
|
|
|
for (var i = 0; i < "ZCFDBSIJ".length; i++) {
|
|
|
|
var typeName = "ZCFDBSIJ"[i];
|
|
|
|
linkKlass(PrimitiveClassInfo[typeName]);
|
|
|
|
this.getClass("[" + typeName);
|
2014-11-26 12:21:11 +03:00
|
|
|
}
|
2014-12-24 08:20:07 +03:00
|
|
|
leaveTimeline("initializeBuiltinClasses");
|
2014-11-26 12:21:11 +03:00
|
|
|
}
|
|
|
|
|
2014-12-21 10:39:08 +03:00
|
|
|
isPreInitializedClass(classInfo: ClassInfo) {
|
|
|
|
return this.preInitializedClasses.indexOf(classInfo) >= 0;
|
|
|
|
}
|
|
|
|
|
2014-11-26 12:21:11 +03:00
|
|
|
addPath(name: string, buffer: ArrayBuffer) {
|
|
|
|
if (name.substr(-4) === ".jar") {
|
2014-12-21 10:39:08 +03:00
|
|
|
this.jarFiles[name] = new ZipFile(buffer);
|
2014-11-26 12:21:11 +03:00
|
|
|
} else {
|
|
|
|
this.classFiles[name] = buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-27 14:28:52 +03:00
|
|
|
addSourceDirectory(name: string) {
|
|
|
|
this.sourceDirectories.push(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
getSourceLine(sourceLocation: SourceLocation): string {
|
|
|
|
if (typeof snarf === "undefined") {
|
|
|
|
// Sorry, no snarf in the browser. Do async loading instead.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var source = this.sourceFiles[sourceLocation.className];
|
|
|
|
if (!source && !this.missingSourceFiles[sourceLocation.className]) {
|
|
|
|
for (var i = 0; i < this.sourceDirectories.length; i++) {
|
|
|
|
try {
|
|
|
|
var path = this.sourceDirectories[i] + "/" + sourceLocation.className + ".java";
|
|
|
|
var file = snarf(path);
|
|
|
|
if (file) {
|
|
|
|
source = this.sourceFiles[sourceLocation.className] = file.split("\n");
|
|
|
|
}
|
|
|
|
} catch (x) {
|
|
|
|
// Keep looking.
|
2014-12-01 11:15:56 +03:00
|
|
|
//stderrWriter.writeLn("" + x);
|
2014-11-27 14:28:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (source) {
|
|
|
|
return source[sourceLocation.lineNumber - 1];
|
|
|
|
}
|
|
|
|
this.missingSourceFiles[sourceLocation.className] = true;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2014-11-26 12:21:11 +03:00
|
|
|
loadFileFromJar(jarName: string, fileName: string): ArrayBuffer {
|
2014-12-21 10:39:08 +03:00
|
|
|
var zip = this.jarFiles[jarName];
|
2014-11-26 12:21:11 +03:00
|
|
|
if (!zip)
|
|
|
|
return null;
|
|
|
|
if (!(fileName in zip.directory))
|
|
|
|
return null;
|
|
|
|
var bytes = zip.read(fileName);
|
|
|
|
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
loadFile(fileName: string): ArrayBuffer {
|
|
|
|
var classFiles = this.classFiles;
|
|
|
|
var data = classFiles[fileName];
|
2014-12-21 10:39:08 +03:00
|
|
|
if (data) {
|
2014-11-26 12:21:11 +03:00
|
|
|
return data;
|
2014-12-21 10:39:08 +03:00
|
|
|
}
|
|
|
|
var jarFiles = this.jarFiles;
|
|
|
|
for (var k in jarFiles) {
|
|
|
|
var zip = jarFiles[k];
|
2014-11-26 12:21:11 +03:00
|
|
|
if (fileName in zip.directory) {
|
2014-12-18 10:29:14 +03:00
|
|
|
enterTimeline("ZIP", {file: fileName});
|
2014-11-26 12:21:11 +03:00
|
|
|
var bytes = zip.read(fileName);
|
|
|
|
data = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
2014-12-18 10:29:14 +03:00
|
|
|
leaveTimeline("ZIP");
|
2014-12-21 10:39:08 +03:00
|
|
|
break;
|
2014-11-26 12:21:11 +03:00
|
|
|
}
|
2014-12-21 10:39:08 +03:00
|
|
|
}
|
|
|
|
if (data) {
|
2014-11-26 12:21:11 +03:00
|
|
|
classFiles[fileName] = data;
|
2014-12-21 10:39:08 +03:00
|
|
|
}
|
2014-11-26 12:21:11 +03:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadClassBytes(bytes: ArrayBuffer): ClassInfo {
|
2014-12-18 11:53:16 +03:00
|
|
|
enterTimeline("loadClassBytes");
|
2014-11-26 12:21:11 +03:00
|
|
|
var classInfo = new ClassInfo(bytes);
|
2014-12-18 11:53:16 +03:00
|
|
|
leaveTimeline("loadClassBytes", {className: classInfo.className});
|
2014-11-26 12:21:11 +03:00
|
|
|
this.classes[classInfo.className] = classInfo;
|
|
|
|
return classInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadClassFile(fileName: string): ClassInfo {
|
2015-01-15 04:13:14 +03:00
|
|
|
loadWriter && loadWriter.enter("> Loading Class File: " + fileName);
|
2014-11-26 12:21:11 +03:00
|
|
|
var bytes = this.loadFile(fileName);
|
2015-01-15 04:13:14 +03:00
|
|
|
if (!bytes) {
|
|
|
|
loadWriter && loadWriter.leave("< ClassNotFoundException");
|
2014-11-26 12:21:11 +03:00
|
|
|
throw new (ClassNotFoundException)(fileName);
|
2015-01-15 04:13:14 +03:00
|
|
|
}
|
2014-11-26 12:21:11 +03:00
|
|
|
var self = this;
|
|
|
|
var classInfo = this.loadClassBytes(bytes);
|
2015-01-15 04:13:14 +03:00
|
|
|
if (classInfo.superClassName) {
|
2014-11-26 12:21:11 +03:00
|
|
|
classInfo.superClass = this.loadClass(classInfo.superClassName);
|
2015-01-19 23:28:04 +03:00
|
|
|
var superClass = classInfo.superClass;
|
|
|
|
superClass.subClasses.push(classInfo);
|
|
|
|
while (superClass) {
|
|
|
|
superClass.allSubClasses.push(classInfo);
|
|
|
|
superClass = superClass.superClass;
|
|
|
|
}
|
2015-01-15 04:13:14 +03:00
|
|
|
}
|
2014-11-26 12:21:11 +03:00
|
|
|
var classes = classInfo.classes;
|
|
|
|
classes.forEach(function (c, n) {
|
|
|
|
classes[n] = self.loadClass(c);
|
|
|
|
});
|
2014-12-13 02:00:34 +03:00
|
|
|
classInfo.complete();
|
2015-01-15 04:13:14 +03:00
|
|
|
loadWriter && loadWriter.leave("<");
|
2014-11-26 12:21:11 +03:00
|
|
|
return classInfo;
|
|
|
|
}
|
|
|
|
|
2015-01-15 04:13:14 +03:00
|
|
|
/**
|
|
|
|
* Used to test loading of all class files.
|
|
|
|
*/
|
2014-12-24 08:20:07 +03:00
|
|
|
loadAllClassFiles() {
|
|
|
|
var jarFiles = this.jarFiles;
|
2015-01-19 23:28:04 +03:00
|
|
|
Object.keys(jarFiles).every(function (path) {
|
|
|
|
if (path.substr(-4) !== ".jar") {
|
|
|
|
return true;
|
2014-12-24 08:20:07 +03:00
|
|
|
}
|
2015-01-19 23:28:04 +03:00
|
|
|
var zipFile = jarFiles[path];
|
|
|
|
Object.keys(zipFile.directory).every(function (fileName) {
|
|
|
|
if (fileName.substr(-6) !== '.class') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
var className = fileName.substring(0, fileName.length - 6);
|
|
|
|
CLASSES.getClass(className);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
});
|
2014-12-24 08:20:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
loadClass(className: string): ClassInfo {
|
2014-11-26 12:21:11 +03:00
|
|
|
var classInfo = this.classes[className];
|
2015-01-15 04:13:14 +03:00
|
|
|
if (classInfo) {
|
2014-11-26 12:21:11 +03:00
|
|
|
return classInfo;
|
2015-01-15 04:13:14 +03:00
|
|
|
}
|
2014-11-26 12:21:11 +03:00
|
|
|
return this.loadClassFile(className + ".class");
|
|
|
|
}
|
|
|
|
|
2015-01-15 04:13:14 +03:00
|
|
|
loadAndLinkClass(className: string): ClassInfo {
|
|
|
|
var classInfo = this.loadClass(className);
|
|
|
|
linkKlass(classInfo);
|
|
|
|
return classInfo;
|
|
|
|
}
|
|
|
|
|
2014-11-26 12:21:11 +03:00
|
|
|
getEntryPoint(classInfo: ClassInfo): MethodInfo {
|
|
|
|
var methods = classInfo.methods;
|
|
|
|
for (var i=0; i<methods.length; i++) {
|
|
|
|
var method = methods[i];
|
|
|
|
if (method.isPublic && method.isStatic && !method.isNative &&
|
2015-01-15 04:13:14 +03:00
|
|
|
method.name === "main" &&
|
|
|
|
method.signature === "([Ljava/lang/String;)V") {
|
2014-11-26 12:21:11 +03:00
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getClass(className: string): ClassInfo {
|
|
|
|
var classInfo = this.classes[className];
|
|
|
|
if (!classInfo) {
|
|
|
|
if (className[0] === "[") {
|
|
|
|
classInfo = this.createArrayClass(className);
|
|
|
|
} else {
|
|
|
|
classInfo = this.loadClass(className);
|
|
|
|
}
|
|
|
|
if (!classInfo)
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return classInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
createArrayClass(typeName: string): ArrayClassInfo {
|
|
|
|
var elementType = typeName.substr(1);
|
|
|
|
var constructor = getArrayConstructor(elementType);
|
|
|
|
var classInfo;
|
|
|
|
if (constructor) {
|
2014-12-01 06:49:39 +03:00
|
|
|
classInfo = PrimitiveArrayClassInfo[elementType];
|
2014-11-26 12:21:11 +03:00
|
|
|
} else {
|
|
|
|
if (elementType[0] === "L") {
|
|
|
|
elementType = elementType.substr(1).replace(";", "");
|
|
|
|
}
|
|
|
|
classInfo = new ArrayClassInfo(typeName, this.getClass(elementType));
|
|
|
|
}
|
|
|
|
if (J2ME.phase === J2ME.ExecutionPhase.Runtime) {
|
|
|
|
J2ME.linkKlass(classInfo);
|
|
|
|
}
|
|
|
|
return this.classes[typeName] = classInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getField(classInfo, fieldKey) {
|
|
|
|
if (classInfo.vfc[fieldKey]) {
|
|
|
|
return classInfo.vfc[fieldKey];
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
var fields = classInfo.fields;
|
|
|
|
for (var i=0; i<fields.length; ++i) {
|
|
|
|
var field = fields[i];
|
|
|
|
if (!field.key) {
|
2014-12-19 00:57:53 +03:00
|
|
|
field.key = (AccessFlags.isStatic(field.access_flags) ? "S" : "I") + "." + field.name + "." + field.signature;
|
2014-11-26 12:21:11 +03:00
|
|
|
}
|
|
|
|
if (field.key === fieldKey) {
|
|
|
|
return classInfo.vfc[fieldKey] = field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fieldKey[0] === 'S') {
|
|
|
|
for (var n = 0; n < classInfo.interfaces.length; ++n) {
|
|
|
|
var field = this.getField(classInfo.interfaces[n], fieldKey);
|
|
|
|
if (field) {
|
|
|
|
return classInfo.vfc[fieldKey] = field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
classInfo = classInfo.superClass;
|
|
|
|
} while (classInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
getMethod(classInfo, methodKey) {
|
|
|
|
var c = classInfo;
|
|
|
|
do {
|
|
|
|
var methods = c.methods;
|
|
|
|
for (var i=0; i<methods.length; ++i) {
|
|
|
|
var method = methods[i];
|
|
|
|
if (method.key === methodKey) {
|
|
|
|
return classInfo.vmc[methodKey] = method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c = c.superClass;
|
|
|
|
} while (c);
|
|
|
|
|
2014-12-19 00:57:53 +03:00
|
|
|
if (AccessFlags.isInterface(classInfo.access_flags)) {
|
2014-11-26 12:21:11 +03:00
|
|
|
for (var n = 0; n < classInfo.interfaces.length; ++n) {
|
|
|
|
var method = this.getMethod(classInfo.interfaces[n], methodKey);
|
|
|
|
if (method) {
|
|
|
|
return classInfo.vmc[methodKey] = method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export var ClassNotFoundException = function(message) {
|
|
|
|
this.message = message;
|
|
|
|
};
|
|
|
|
|
|
|
|
ClassNotFoundException.prototype = Object.create(Error.prototype);
|
|
|
|
ClassNotFoundException.prototype.name = "ClassNotFoundException";
|
|
|
|
ClassNotFoundException.prototype.constructor = ClassNotFoundException;
|
|
|
|
}
|
|
|
|
|