module J2ME { declare var ZipFile; declare var snarf; export class ClassRegistry { /** * List of directories to look for source files in. */ sourceDirectories: string []; /** * All source code, only ever used for debugging. */ sourceFiles: Map; /** * 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; jarFiles: Map; classFiles: Map; classes: Map; preInitializedClasses: ClassInfo []; java_lang_Object: ClassInfo; java_lang_Class: ClassInfo; java_lang_String: ClassInfo; java_lang_Thread: ClassInfo; constructor() { this.sourceDirectories = []; this.sourceFiles = Object.create(null); this.missingSourceFiles = Object.create(null); this.jarFiles = Object.create(null); this.classFiles = Object.create(null); this.classes = Object.create(null); this.preInitializedClasses = []; } initializeBuiltinClasses() { // These classes are guaranteed to not have a static initializer. enterTimeline("initializeBuiltinClasses"); this.java_lang_Object = this.loadClass("java/lang/Object"); this.java_lang_Class = this.loadClass("java/lang/Class"); this.java_lang_String = this.loadClass("java/lang/String"); this.java_lang_Thread = this.loadClass("java/lang/Thread"); 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); /** * We force these additinoal frequently used classes to be initialized so that * we can generate more optimal AOT code that skips the class initialization * check. */ var classNames = [ "java/lang/Integer", "java/lang/Character", "java/lang/Math", "java/util/HashtableEntry" ]; for (var i = 0; i < classNames.length; i++) { this.preInitializedClasses.push(this.loadClass(classNames[i])); } // Link primitive values and primitive arrays. for (var i = 0; i < "ZCFDBSIJ".length; i++) { var typeName = "ZCFDBSIJ"[i]; linkKlass(PrimitiveClassInfo[typeName]); this.getClass("[" + typeName); } leaveTimeline("initializeBuiltinClasses"); } isPreInitializedClass(classInfo: ClassInfo) { return this.preInitializedClasses.indexOf(classInfo) >= 0; } addPath(name: string, buffer: ArrayBuffer) { if (name.substr(-4) === ".jar") { this.jarFiles[name] = new ZipFile(buffer); } else { this.classFiles[name] = buffer; } } 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. //stderrWriter.writeLn("" + x); } } } if (source) { return source[sourceLocation.lineNumber - 1]; } this.missingSourceFiles[sourceLocation.className] = true; return null; } loadFileFromJar(jarName: string, fileName: string): ArrayBuffer { var zip = this.jarFiles[jarName]; 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]; if (data) { return data; } var jarFiles = this.jarFiles; for (var k in jarFiles) { var zip = jarFiles[k]; if (fileName in zip.directory) { enterTimeline("ZIP", {file: fileName}); var bytes = zip.read(fileName); data = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); leaveTimeline("ZIP"); break; } } if (data) { classFiles[fileName] = data; } return data; } loadClassBytes(bytes: ArrayBuffer): ClassInfo { enterTimeline("loadClassBytes"); var classInfo = new ClassInfo(bytes); leaveTimeline("loadClassBytes", {className: classInfo.className}); this.classes[classInfo.className] = classInfo; return classInfo; } loadClassFile(fileName: string): ClassInfo { var bytes = this.loadFile(fileName); if (!bytes) throw new (ClassNotFoundException)(fileName); var self = this; var classInfo = this.loadClassBytes(bytes); if (classInfo.superClassName) classInfo.superClass = this.loadClass(classInfo.superClassName); var classes = classInfo.classes; classes.forEach(function (c, n) { classes[n] = self.loadClass(c); }); classInfo.complete(); if (J2ME.phase === J2ME.ExecutionPhase.Runtime) { J2ME.linkKlass(classInfo); } return classInfo; } loadAllClassFiles() { var jarFiles = this.jarFiles; for (var k in jarFiles) { var zip = jarFiles[k]; for (var fileName in zip.directory) { if (fileName.substr(-6) === ".class") { this.loadClassFile(fileName); } } } } loadClass(className: string): ClassInfo { var classInfo = this.classes[className]; if (classInfo) return classInfo; return this.loadClassFile(className + ".class"); } getEntryPoint(classInfo: ClassInfo): MethodInfo { var methods = classInfo.methods; for (var i=0; i