diff --git a/actors.ts b/actors.ts index 63bbfbae..87c451ea 100644 --- a/actors.ts +++ b/actors.ts @@ -1,622 +1,622 @@ -module J2ME { - declare var Native, Override; - declare var missingNativeImpl; - declare var CC; - declare var Signature; - declare var classObjects; - declare var util; - - import Counter = Metrics.Counter; - declare var byteSize; - - export interface ConstantPoolEntry { - tag: TAGS; - name_index: number; - bytes: string; - class_index: number; - name_and_type_index: number; - signature_index: number; - string_index: number; - integer: number; - float: number; - double: number; - highBits: number; - lowBits: number; - } - - export interface ExceptionHandler { - start_pc: number; - end_pc: number; - handler_pc: number; - catch_type: number; - } - - export class SourceLocation { - constructor(public className: string, public sourceFile: string, public lineNumber: number) { - // ... - } - toString() { - return this.sourceFile + ":" + this.lineNumber; - } - equals(other: SourceLocation): boolean { - if (!other) { - return false; - } - return this.sourceFile === other.sourceFile && - this.lineNumber === other.lineNumber; - } - } - - export class FieldInfo { - private static _nextiId = 0; - id: number; - isStatic: boolean ; - constantValue: any; - mangledName: string; - key: string; - kind: Kind; - - constructor(public classInfo: ClassInfo, public access_flags: number, public name: string, public signature: string) { - this.id = FieldInfo._nextiId++; - this.isStatic = AccessFlags.isStatic(access_flags); - this.constantValue = undefined; - this.mangledName = undefined; - this.key = undefined; - this.kind = getSignatureKind(signature); - } - - get(object: java.lang.Object) { - return object[this.mangledName]; - } - - set(object: java.lang.Object, value: any) { - object[this.mangledName] = value - } - - getStatic() { - return this.get(this.classInfo.getStaticObject($.ctx)); - } - - setStatic(value: any) { - return this.set(this.classInfo.getStaticObject($.ctx), value); - } - - toString() { - return "[field " + this.name + "]"; - } - } - - /** - * Required params: - * - name - * - signature - * - classInfo - * - * Optional params: - * - attributes (defaults to []) - * - code (if not provided, pulls from attributes) - * - isNative, isPublic, isStatic, isSynchronized - */ - export class MethodInfo { - name: string; - classInfo: ClassInfo; - code: Uint8Array; - isNative: boolean; - isPublic: boolean; - isStatic: boolean; - isSynchronized: boolean; - isAbstract: boolean; - isFinal: boolean; - - /** - * There is a compiled version of this method.? - */ - state: MethodState; - - exception_table: ExceptionHandler []; - max_locals: number; - max_stack: number; - - argumentSlots: number; - - /** - * The number of arguments to pop of the stack when calling this function. - */ - consumeArgumentSlots: number; - - hasTwoSlotArguments: boolean; - signatureDescriptor: SignatureDescriptor; - signature: string; - implKey: string; - key: string; - alternateImpl: {()}; - fn: {()}; - attributes: any []; - mangledName: string; - mangledClassAndMethodName: string; - - onStackReplacementEntryPoints: number []; - - line_number_table: {start_pc: number; line_number: number} []; - - /** - * Approximate number of bytecodes executed in this method. - */ - bytecodeCount: number; - - /** - * Approximate number of times this method was called. - */ - callCount: number; - - /** - * Approximate number of times this method was called. - */ - interpreterCallCount: number; - - /** - * Approximate number of times a backward branch was taken. - */ - backwardsBranchCount: number; - - /** - * Number of times this method's counters were reset. - */ - resetCount: number; - - /** - * Whether this method's bytecode has been optimized for quicker interpretation. - */ - isOptimized: boolean; - - constructor(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; - this.max_stack = a.info.max_stack; - - var codeAttributes = a.info.attributes; - for (var j = 0; j < codeAttributes.length; j++) { - var b = codeAttributes[j]; - if (b.info.type === ATTRIBUTE_TYPES.LineNumberTable) { - this.line_number_table = b.info.line_number_table; - } - } - break; - } - } - } - - this.isNative = opts.isNative; - this.isPublic = opts.isPublic; - this.isStatic = opts.isStatic; - this.isSynchronized = opts.isSynchronized; - this.isAbstract = opts.isAbstract; - this.isFinal = opts.isAbstract; - this.state = MethodState.Cold; - this.key = (this.isStatic ? "S." : "I.") + this.name + "." + this.signature; - this.implKey = this.classInfo.className + "." + this.name + "." + this.signature; - - - this.mangledName = mangleMethod(this); - this.mangledClassAndMethodName = mangleClassAndMethod(this); - - this.signatureDescriptor = SignatureDescriptor.makeSignatureDescriptor(this.signature); - this.hasTwoSlotArguments = this.signatureDescriptor.hasTwoSlotArguments(); - this.argumentSlots = this.signatureDescriptor.getArgumentSlotCount(); - this.consumeArgumentSlots = this.argumentSlots; - if (!this.isStatic) { - this.consumeArgumentSlots ++; - } - - this.callCount = 0; - this.resetCount = 0; - this.interpreterCallCount = 0; - this.backwardsBranchCount = 0; - this.bytecodeCount = 0; - - this.isOptimized = false; - this.onStackReplacementEntryPoints = null; - } - - public getReturnKind(): Kind { - return this.signatureDescriptor.typeDescriptors[0].kind; - } - - getSourceLocationForPC(pc: number): SourceLocation { - var sourceFile = this.classInfo.sourceFile || null; - if (!sourceFile) { - return null; - } - var lineNumber = -1; - if (this.line_number_table && this.line_number_table.length) { - var table = this.line_number_table; - for (var i = 0; i < table.length; i++) { - if (pc >= table[i].start_pc) { - lineNumber = table[i].line_number; - } else if (pc < table[i].start_pc) { - break; - } - } - } - return new SourceLocation(this.classInfo.className, sourceFile, lineNumber) - } - } - - var classID = 0; - - export class ClassInfo { - className: string; - c: string; - superClass: ClassInfo; - superClassName: string; - interfaces: ClassInfo []; - fields: FieldInfo []; - methods: MethodInfo []; - staticInitializer: MethodInfo; - classes: any []; - subClasses: ClassInfo []; - allSubClasses: ClassInfo []; - constant_pool: ConstantPoolEntry []; - resolved_constant_pool: any []; - isArrayClass: boolean; - elementClass: ClassInfo; - klass: Klass; - access_flags: number; - vmc: any; - vfc: any; - mangledName: string; - thread: any; - id: number; - - sourceFile: string; - - static createFromObject(object) { - var classInfo = Object.create(ClassInfo.prototype, object); - classInfo.resolved_constant_pool = new Array(classInfo.constant_pool.length); - classInfo.mangledName = mangleClass(classInfo); - return classInfo; - } - - constructor(classBytes) { - this.id = classID ++; - enterTimeline("getClassImage"); - var classImage = getClassImage(classBytes); - leaveTimeline("getClassImage"); - 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; - this.resolved_constant_pool = new Array(cp.length); - this.subClasses = []; - this.allSubClasses = []; - // Cache for virtual methods and fields - this.vmc = {}; - this.vfc = {}; - - this.mangledName = mangleClass(this); - - var self = this; - - this.interfaces = []; - for (var i = 0; i < classImage.interfaces.length; i++) { - var j = classImage.interfaces[i]; - var int = CLASSES.loadClass(cp[cp[j].name_index].bytes); - self.interfaces.push(int); - self.interfaces = self.interfaces.concat(int.interfaces); - } - - this.fields = []; - for (var i = 0; i < classImage.fields.length; i++) { - var f = classImage.fields[i]; - var field = new FieldInfo(self, f.access_flags, cp[f.name_index].bytes, cp[f.descriptor_index].bytes); - f.attributes.forEach(function (attribute) { - if (cp[attribute.attribute_name_index].bytes === "ConstantValue") - field.constantValue = new DataView(attribute.info).getUint16(0, false); - }); - self.fields.push(field); - } - - enterTimeline("methods"); - this.methods = []; - - for (var i = 0; i < classImage.methods.length; i++) { - var m = classImage.methods[i]; - var methodInfo = new MethodInfo({ - name: cp[m.name_index].bytes, - signature: cp[m.signature_index].bytes, - classInfo: self, - attributes: m.attributes, - isNative: AccessFlags.isNative(m.access_flags), - isPublic: AccessFlags.isPublic(m.access_flags), - isStatic: AccessFlags.isStatic(m.access_flags), - isSynchronized: AccessFlags.isSynchronized(m.access_flags), - isAbstract: AccessFlags.isAbstract(m.access_flags), - isFinal: AccessFlags.isFinal(m.access_flags) - }); - this.methods.push(methodInfo); - if (methodInfo.name === "") { - this.staticInitializer = methodInfo; - } - } - leaveTimeline("methods"); - - var classes = this.classes = []; - for (var i = 0; i < classImage.attributes.length; i++) { - var a = classImage.attributes[i]; - 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); - if (c.outer_class_info_index) - classes.push(cp[cp[c.outer_class_info_index].name_index].bytes); - }); - } else if (a.info.type === ATTRIBUTE_TYPES.SourceFile) { - self.sourceFile = cp[a.info.sourcefile_index].bytes; - } - } - } - - public complete() { - enterTimeline("mangleFields"); - this._mangleFields(); - leaveTimeline("mangleFields"); - } - - /** - * Gets the class hierarchy in derived -> base order. - */ - private _getClassHierarchy(): ClassInfo [] { - var classHierarchy = []; - var classInfo = this; - do { - classHierarchy.push(classInfo); - classInfo = classInfo.superClass; - } while (classInfo); - return classHierarchy; - } - - private _mangleFields() { - // Keep track of how many times a field name was used and resolve conflicts by - // prefixing filed names with numbers. - var classInfo: ClassInfo; - var classHierarchy = this._getClassHierarchy(); - var count = Object.create(null); - for (var i = classHierarchy.length - 1; i >= 0; i--) { - classInfo = classHierarchy[i]; - var fields = classInfo.fields; - for (var j = 0; j < fields.length; j++) { - var field = fields[j]; - var fieldName = field.name; - if (count[field.name] === undefined) { - count[fieldName] = 0; - } - var fieldCount = count[fieldName]; - // Only mangle this classInfo's fields. - if (i === 0) { - field.mangledName = "$" + (fieldCount ? "$" + fieldCount : "") + field.name; - } - count[fieldName] ++; - } - } - } - - get isInterface() : boolean { - return AccessFlags.isInterface(this.access_flags); - } - - get isFinal() : boolean { - return AccessFlags.isFinal(this.access_flags); - } - - implementsInterface(iface) : boolean { - var classInfo = this; - do { - var interfaces = classInfo.interfaces; - for (var n = 0; n < interfaces.length; ++n) { - if (interfaces[n] === iface) - return true; - } - classInfo = classInfo.superClass; - } while (classInfo); - return false; - } - - isAssignableTo(toClass: ClassInfo) : boolean { - if (this === toClass || toClass === CLASSES.java_lang_Object) - return true; - if (AccessFlags.isInterface(toClass.access_flags) && this.implementsInterface(toClass)) - return true; - if (this.elementClass && toClass.elementClass) - return this.elementClass.isAssignableTo(toClass.elementClass); - return this.superClass ? this.superClass.isAssignableTo(toClass) : false; - } - - /** - * java.lang.Class object for this class info. This is a not where static properties - * are stored for this class. - */ - getClassObject(): java.lang.Class { - return getRuntimeKlass($, this.klass).classObject; - } - - /** - * Object that holds static properties for this class. - */ - getStaticObject(ctx: Context): java.lang.Object { - return getRuntimeKlass(ctx.runtime, this.klass); - } - - getField(fieldKey: string) : FieldInfo { - return CLASSES.getField(this, fieldKey); - } - - getClassInitLockObject(ctx: Context) { - if (!(this.className in ctx.runtime.classInitLockObjects)) { - ctx.runtime.classInitLockObjects[this.className] = { - classInfo: this - }; - } - return ctx.runtime.classInitLockObjects[this.className]; - } - - toString() { - return "[class " + this.className + "]"; - } - - count(counter: Counter) { - counter.count("Classes", 1); - counter.count("Methods", this.methods.length); - counter.count("Fields", this.fields.length); - - for (var i = 0; i < this.methods.length; i++) { - var m = this.methods[i]; - if (m.code) { - counter.count("Code", m.code.length); - } - } - } - - /** - * Resolves a constant pool reference. - */ - resolve(index: number, isStatic: boolean) { - var rp = this.resolved_constant_pool; - var constant: any = rp[index]; - if (constant !== undefined) { - return constant; - } - var cp = this.constant_pool; - var entry = this.constant_pool[index]; - switch (entry.tag) { - case TAGS.CONSTANT_Integer: - constant = entry.integer; - break; - case TAGS.CONSTANT_Float: - constant = entry.float; - break; - case TAGS.CONSTANT_String: - constant = $.newStringConstant(cp[entry.string_index].bytes); - break; - case TAGS.CONSTANT_Long: - constant = Long.fromBits(entry.lowBits, entry.highBits); - break; - case TAGS.CONSTANT_Double: - constant = entry.double; - break; - case TAGS.CONSTANT_Class: - constant = CLASSES.getClass(cp[entry.name_index].bytes); - break; - case TAGS.CONSTANT_Fieldref: - var classInfo = this.resolve(entry.class_index, isStatic); - var fieldName = cp[cp[entry.name_and_type_index].name_index].bytes; - var signature = cp[cp[entry.name_and_type_index].signature_index].bytes; - constant = CLASSES.getField(classInfo, (isStatic ? "S" : "I") + "." + fieldName + "." + signature); - if (!constant) { - throw $.newRuntimeException( - classInfo.className + "." + fieldName + "." + signature + " not found"); - } - break; - case TAGS.CONSTANT_Methodref: - case TAGS.CONSTANT_InterfaceMethodref: - var classInfo = this.resolve(entry.class_index, isStatic); - var methodName = cp[cp[entry.name_and_type_index].name_index].bytes; - var signature = cp[cp[entry.name_and_type_index].signature_index].bytes; - constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature); - if (!constant) { - constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature); - throw $.newRuntimeException( - classInfo.className + "." + methodName + "." + signature + " not found"); - } - break; - default: - throw new Error("not support constant type"); - } - return rp[index] = constant; - } - } - - export class ArrayClassInfo extends ClassInfo { - constructor(className: string, elementClass: ClassInfo, mangledName?: string) { - false && super(null); - this.className = className; - // TODO this may need to change for compiled code. - this.mangledName = mangledName || mangleClassName(className); - this.superClass = CLASSES.java_lang_Object; - this.superClassName = "java/lang/Object"; - this.access_flags = 0; - this.elementClass = elementClass; - this.vmc = {}; - this.vfc = {}; - } - implementsInterface(iface) { - return false; - } - } - - ArrayClassInfo.prototype.fields = []; - ArrayClassInfo.prototype.methods = []; - ArrayClassInfo.prototype.interfaces = []; - ArrayClassInfo.prototype.isArrayClass = true; - - export class PrimitiveClassInfo extends ClassInfo { - constructor(className: string, mangledName: string) { - false && super(null); - this.className = className; - this.mangledName = mangledName; - } - static Z = new PrimitiveClassInfo("Z", "boolean"); - static C = new PrimitiveClassInfo("C", "char"); - static F = new PrimitiveClassInfo("F", "float"); - static D = new PrimitiveClassInfo("D", "double"); - static B = new PrimitiveClassInfo("B", "byte"); - static S = new PrimitiveClassInfo("S", "short"); - static I = new PrimitiveClassInfo("I", "int"); - static J = new PrimitiveClassInfo("J", "long"); - } - - PrimitiveClassInfo.prototype.fields = []; - PrimitiveClassInfo.prototype.methods = []; - PrimitiveClassInfo.prototype.interfaces = []; - - export class PrimitiveArrayClassInfo extends ArrayClassInfo { - constructor(className: string, elementClass: ClassInfo, mangledName: string) { - super(className, elementClass, mangledName); - } - - get superClass() { - return CLASSES.java_lang_Object; - } - - static Z = new PrimitiveArrayClassInfo("[Z", PrimitiveClassInfo.Z, "Uint8Array"); - static C = new PrimitiveArrayClassInfo("[C", PrimitiveClassInfo.C, "Uint16Array"); - static F = new PrimitiveArrayClassInfo("[F", PrimitiveClassInfo.F, "Float32Array"); - static D = new PrimitiveArrayClassInfo("[D", PrimitiveClassInfo.D, "Float64Array"); - static B = new PrimitiveArrayClassInfo("[B", PrimitiveClassInfo.B, "Int8Array"); - static S = new PrimitiveArrayClassInfo("[S", PrimitiveClassInfo.S, "Int16Array"); - static I = new PrimitiveArrayClassInfo("[I", PrimitiveClassInfo.I, "Int32Array"); - static J = new PrimitiveArrayClassInfo("[J", PrimitiveClassInfo.J, "Int64Array"); - } - - PrimitiveClassInfo.prototype.fields = []; - PrimitiveClassInfo.prototype.methods = []; - PrimitiveClassInfo.prototype.interfaces = []; -} - -var FieldInfo = J2ME.FieldInfo; -var MethodInfo = J2ME.MethodInfo; -var ClassInfo = J2ME.ClassInfo; +//module J2ME { +// declare var Native, Override; +// declare var missingNativeImpl; +// declare var CC; +// declare var Signature; +// declare var classObjects; +// declare var util; +// +// import Counter = Metrics.Counter; +// declare var byteSize; +// +// export interface ConstantPoolEntry { +// tag: TAGS; +// name_index: number; +// bytes: string; +// class_index: number; +// name_and_type_index: number; +// signature_index: number; +// string_index: number; +// integer: number; +// float: number; +// double: number; +// highBits: number; +// lowBits: number; +// } +// +// export interface ExceptionHandler { +// start_pc: number; +// end_pc: number; +// handler_pc: number; +// catch_type: number; +// } +// +// export class SourceLocation { +// constructor(public className: string, public sourceFile: string, public lineNumber: number) { +// // ... +// } +// toString() { +// return this.sourceFile + ":" + this.lineNumber; +// } +// equals(other: SourceLocation): boolean { +// if (!other) { +// return false; +// } +// return this.sourceFile === other.sourceFile && +// this.lineNumber === other.lineNumber; +// } +// } +// +// export class FieldInfo { +// private static _nextiId = 0; +// id: number; +// isStatic: boolean ; +// constantValue: any; +// mangledName: string; +// key: string; +// kind: Kind; +// +// constructor(public classInfo: ClassInfo, public access_flags: number, public name: string, public signature: string) { +// this.id = FieldInfo._nextiId++; +// this.isStatic = AccessFlags.isStatic(access_flags); +// this.constantValue = undefined; +// this.mangledName = undefined; +// this.key = undefined; +// this.kind = getSignatureKind(signature); +// } +// +// get(object: java.lang.Object) { +// return object[this.mangledName]; +// } +// +// set(object: java.lang.Object, value: any) { +// object[this.mangledName] = value +// } +// +// getStatic() { +// return this.get(this.classInfo.getStaticObject($.ctx)); +// } +// +// setStatic(value: any) { +// return this.set(this.classInfo.getStaticObject($.ctx), value); +// } +// +// toString() { +// return "[field " + this.name + "]"; +// } +// } +// +// /** +// * Required params: +// * - name +// * - signature +// * - classInfo +// * +// * Optional params: +// * - attributes (defaults to []) +// * - code (if not provided, pulls from attributes) +// * - isNative, isPublic, isStatic, isSynchronized +// */ +// export class MethodInfo { +// name: string; +// classInfo: ClassInfo; +// code: Uint8Array; +// isNative: boolean; +// isPublic: boolean; +// isStatic: boolean; +// isSynchronized: boolean; +// isAbstract: boolean; +// isFinal: boolean; +// +// /** +// * There is a compiled version of this method.? +// */ +// state: MethodState; +// +// exception_table: ExceptionHandler []; +// max_locals: number; +// max_stack: number; +// +// argumentSlots: number; +// +// /** +// * The number of arguments to pop of the stack when calling this function. +// */ +// consumeArgumentSlots: number; +// +// hasTwoSlotArguments: boolean; +// signatureDescriptor: SignatureDescriptor; +// signature: string; +// implKey: string; +// key: string; +// alternateImpl: {()}; +// fn: {()}; +// attributes: any []; +// mangledName: string; +// mangledClassAndMethodName: string; +// +// onStackReplacementEntryPoints: number []; +// +// line_number_table: {start_pc: number; line_number: number} []; +// +// /** +// * Approximate number of bytecodes executed in this method. +// */ +// bytecodeCount: number; +// +// /** +// * Approximate number of times this method was called. +// */ +// callCount: number; +// +// /** +// * Approximate number of times this method was called. +// */ +// interpreterCallCount: number; +// +// /** +// * Approximate number of times a backward branch was taken. +// */ +// backwardsBranchCount: number; +// +// /** +// * Number of times this method's counters were reset. +// */ +// resetCount: number; +// +// /** +// * Whether this method's bytecode has been optimized for quicker interpretation. +// */ +// isOptimized: boolean; +// +// constructor(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; +// this.max_stack = a.info.max_stack; +// +// var codeAttributes = a.info.attributes; +// for (var j = 0; j < codeAttributes.length; j++) { +// var b = codeAttributes[j]; +// if (b.info.type === ATTRIBUTE_TYPES.LineNumberTable) { +// this.line_number_table = b.info.line_number_table; +// } +// } +// break; +// } +// } +// } +// +// this.isNative = opts.isNative; +// this.isPublic = opts.isPublic; +// this.isStatic = opts.isStatic; +// this.isSynchronized = opts.isSynchronized; +// this.isAbstract = opts.isAbstract; +// this.isFinal = opts.isAbstract; +// this.state = MethodState.Cold; +// this.key = (this.isStatic ? "S." : "I.") + this.name + "." + this.signature; +// this.implKey = this.classInfo.className + "." + this.name + "." + this.signature; +// +// +// this.mangledName = mangleMethod(this); +// this.mangledClassAndMethodName = mangleClassAndMethod(this); +// +// this.signatureDescriptor = SignatureDescriptor.makeSignatureDescriptor(this.signature); +// this.hasTwoSlotArguments = this.signatureDescriptor.hasTwoSlotArguments(); +// this.argumentSlots = this.signatureDescriptor.getArgumentSlotCount(); +// this.consumeArgumentSlots = this.argumentSlots; +// if (!this.isStatic) { +// this.consumeArgumentSlots ++; +// } +// +// this.callCount = 0; +// this.resetCount = 0; +// this.interpreterCallCount = 0; +// this.backwardsBranchCount = 0; +// this.bytecodeCount = 0; +// +// this.isOptimized = false; +// this.onStackReplacementEntryPoints = null; +// } +// +// public getReturnKind(): Kind { +// return this.signatureDescriptor.typeDescriptors[0].kind; +// } +// +// getSourceLocationForPC(pc: number): SourceLocation { +// var sourceFile = this.classInfo.sourceFile || null; +// if (!sourceFile) { +// return null; +// } +// var lineNumber = -1; +// if (this.line_number_table && this.line_number_table.length) { +// var table = this.line_number_table; +// for (var i = 0; i < table.length; i++) { +// if (pc >= table[i].start_pc) { +// lineNumber = table[i].line_number; +// } else if (pc < table[i].start_pc) { +// break; +// } +// } +// } +// return new SourceLocation(this.classInfo.className, sourceFile, lineNumber) +// } +// } +// +// var classID = 0; +// +// export class ClassInfo { +// className: string; +// c: string; +// superClass: ClassInfo; +// superClassName: string; +// interfaces: ClassInfo []; +// fields: FieldInfo []; +// methods: MethodInfo []; +// staticInitializer: MethodInfo; +// classes: any []; +// subClasses: ClassInfo []; +// allSubClasses: ClassInfo []; +// constant_pool: ConstantPoolEntry []; +// resolved_constant_pool: any []; +// isArrayClass: boolean; +// elementClass: ClassInfo; +// klass: Klass; +// access_flags: number; +// vmc: any; +// vfc: any; +// mangledName: string; +// thread: any; +// id: number; +// +// sourceFile: string; +// +// static createFromObject(object) { +// var classInfo = Object.create(ClassInfo.prototype, object); +// classInfo.resolved_constant_pool = new Array(classInfo.constant_pool.length); +// classInfo.mangledName = mangleClass(classInfo); +// return classInfo; +// } +// +// constructor(classBytes) { +// this.id = classID ++; +// enterTimeline("getClassImage"); +// var classImage = getClassImage(classBytes); +// leaveTimeline("getClassImage"); +// 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; +// this.resolved_constant_pool = new Array(cp.length); +// this.subClasses = []; +// this.allSubClasses = []; +// // Cache for virtual methods and fields +// this.vmc = {}; +// this.vfc = {}; +// +// this.mangledName = mangleClass(this); +// +// var self = this; +// +// this.interfaces = []; +// for (var i = 0; i < classImage.interfaces.length; i++) { +// var j = classImage.interfaces[i]; +// var int = CLASSES.loadClass(cp[cp[j].name_index].bytes); +// self.interfaces.push(int); +// self.interfaces = self.interfaces.concat(int.interfaces); +// } +// +// this.fields = []; +// for (var i = 0; i < classImage.fields.length; i++) { +// var f = classImage.fields[i]; +// var field = new FieldInfo(self, f.access_flags, cp[f.name_index].bytes, cp[f.descriptor_index].bytes); +// f.attributes.forEach(function (attribute) { +// if (cp[attribute.attribute_name_index].bytes === "ConstantValue") +// field.constantValue = new DataView(attribute.info).getUint16(0, false); +// }); +// self.fields.push(field); +// } +// +// enterTimeline("methods"); +// this.methods = []; +// +// for (var i = 0; i < classImage.methods.length; i++) { +// var m = classImage.methods[i]; +// var methodInfo = new MethodInfo({ +// name: cp[m.name_index].bytes, +// signature: cp[m.signature_index].bytes, +// classInfo: self, +// attributes: m.attributes, +// isNative: AccessFlags.isNative(m.access_flags), +// isPublic: AccessFlags.isPublic(m.access_flags), +// isStatic: AccessFlags.isStatic(m.access_flags), +// isSynchronized: AccessFlags.isSynchronized(m.access_flags), +// isAbstract: AccessFlags.isAbstract(m.access_flags), +// isFinal: AccessFlags.isFinal(m.access_flags) +// }); +// this.methods.push(methodInfo); +// if (methodInfo.name === "") { +// this.staticInitializer = methodInfo; +// } +// } +// leaveTimeline("methods"); +// +// var classes = this.classes = []; +// for (var i = 0; i < classImage.attributes.length; i++) { +// var a = classImage.attributes[i]; +// 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); +// if (c.outer_class_info_index) +// classes.push(cp[cp[c.outer_class_info_index].name_index].bytes); +// }); +// } else if (a.info.type === ATTRIBUTE_TYPES.SourceFile) { +// self.sourceFile = cp[a.info.sourcefile_index].bytes; +// } +// } +// } +// +// public complete() { +// enterTimeline("mangleFields"); +// this._mangleFields(); +// leaveTimeline("mangleFields"); +// } +// +// /** +// * Gets the class hierarchy in derived -> base order. +// */ +// private _getClassHierarchy(): ClassInfo [] { +// var classHierarchy = []; +// var classInfo = this; +// do { +// classHierarchy.push(classInfo); +// classInfo = classInfo.superClass; +// } while (classInfo); +// return classHierarchy; +// } +// +// private _mangleFields() { +// // Keep track of how many times a field name was used and resolve conflicts by +// // prefixing filed names with numbers. +// var classInfo: ClassInfo; +// var classHierarchy = this._getClassHierarchy(); +// var count = Object.create(null); +// for (var i = classHierarchy.length - 1; i >= 0; i--) { +// classInfo = classHierarchy[i]; +// var fields = classInfo.fields; +// for (var j = 0; j < fields.length; j++) { +// var field = fields[j]; +// var fieldName = field.name; +// if (count[field.name] === undefined) { +// count[fieldName] = 0; +// } +// var fieldCount = count[fieldName]; +// // Only mangle this classInfo's fields. +// if (i === 0) { +// field.mangledName = "$" + (fieldCount ? "$" + fieldCount : "") + field.name; +// } +// count[fieldName] ++; +// } +// } +// } +// +// get isInterface() : boolean { +// return AccessFlags.isInterface(this.access_flags); +// } +// +// get isFinal() : boolean { +// return AccessFlags.isFinal(this.access_flags); +// } +// +// implementsInterface(iface) : boolean { +// var classInfo = this; +// do { +// var interfaces = classInfo.interfaces; +// for (var n = 0; n < interfaces.length; ++n) { +// if (interfaces[n] === iface) +// return true; +// } +// classInfo = classInfo.superClass; +// } while (classInfo); +// return false; +// } +// +// isAssignableTo(toClass: ClassInfo) : boolean { +// if (this === toClass || toClass === CLASSES.java_lang_Object) +// return true; +// if (AccessFlags.isInterface(toClass.access_flags) && this.implementsInterface(toClass)) +// return true; +// if (this.elementClass && toClass.elementClass) +// return this.elementClass.isAssignableTo(toClass.elementClass); +// return this.superClass ? this.superClass.isAssignableTo(toClass) : false; +// } +// +// /** +// * java.lang.Class object for this class info. This is a not where static properties +// * are stored for this class. +// */ +// getClassObject(): java.lang.Class { +// return getRuntimeKlass($, this.klass).classObject; +// } +// +// /** +// * Object that holds static properties for this class. +// */ +// getStaticObject(ctx: Context): java.lang.Object { +// return getRuntimeKlass(ctx.runtime, this.klass); +// } +// +// getField(fieldKey: string) : FieldInfo { +// return CLASSES.getField(this, fieldKey); +// } +// +// getClassInitLockObject(ctx: Context) { +// if (!(this.className in ctx.runtime.classInitLockObjects)) { +// ctx.runtime.classInitLockObjects[this.className] = { +// classInfo: this +// }; +// } +// return ctx.runtime.classInitLockObjects[this.className]; +// } +// +// toString() { +// return "[class " + this.className + "]"; +// } +// +// count(counter: Counter) { +// counter.count("Classes", 1); +// counter.count("Methods", this.methods.length); +// counter.count("Fields", this.fields.length); +// +// for (var i = 0; i < this.methods.length; i++) { +// var m = this.methods[i]; +// if (m.code) { +// counter.count("Code", m.code.length); +// } +// } +// } +// +// /** +// * Resolves a constant pool reference. +// */ +// resolve(index: number, isStatic: boolean) { +// var rp = this.resolved_constant_pool; +// var constant: any = rp[index]; +// if (constant !== undefined) { +// return constant; +// } +// var cp = this.constant_pool; +// var entry = this.constant_pool[index]; +// switch (entry.tag) { +// case TAGS.CONSTANT_Integer: +// constant = entry.integer; +// break; +// case TAGS.CONSTANT_Float: +// constant = entry.float; +// break; +// case TAGS.CONSTANT_String: +// constant = $.newStringConstant(cp[entry.string_index].bytes); +// break; +// case TAGS.CONSTANT_Long: +// constant = Long.fromBits(entry.lowBits, entry.highBits); +// break; +// case TAGS.CONSTANT_Double: +// constant = entry.double; +// break; +// case TAGS.CONSTANT_Class: +// constant = CLASSES.getClass(cp[entry.name_index].bytes); +// break; +// case TAGS.CONSTANT_Fieldref: +// var classInfo = this.resolve(entry.class_index, isStatic); +// var fieldName = cp[cp[entry.name_and_type_index].name_index].bytes; +// var signature = cp[cp[entry.name_and_type_index].signature_index].bytes; +// constant = CLASSES.getField(classInfo, (isStatic ? "S" : "I") + "." + fieldName + "." + signature); +// if (!constant) { +// throw $.newRuntimeException( +// classInfo.className + "." + fieldName + "." + signature + " not found"); +// } +// break; +// case TAGS.CONSTANT_Methodref: +// case TAGS.CONSTANT_InterfaceMethodref: +// var classInfo = this.resolve(entry.class_index, isStatic); +// var methodName = cp[cp[entry.name_and_type_index].name_index].bytes; +// var signature = cp[cp[entry.name_and_type_index].signature_index].bytes; +// constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature); +// if (!constant) { +// constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature); +// throw $.newRuntimeException( +// classInfo.className + "." + methodName + "." + signature + " not found"); +// } +// break; +// default: +// throw new Error("not support constant type"); +// } +// return rp[index] = constant; +// } +// } +// +// export class ArrayClassInfo extends ClassInfo { +// constructor(className: string, elementClass: ClassInfo, mangledName?: string) { +// false && super(null); +// this.className = className; +// // TODO this may need to change for compiled code. +// this.mangledName = mangledName || mangleClassName(className); +// this.superClass = CLASSES.java_lang_Object; +// this.superClassName = "java/lang/Object"; +// this.access_flags = 0; +// this.elementClass = elementClass; +// this.vmc = {}; +// this.vfc = {}; +// } +// implementsInterface(iface) { +// return false; +// } +// } +// +// ArrayClassInfo.prototype.fields = []; +// ArrayClassInfo.prototype.methods = []; +// ArrayClassInfo.prototype.interfaces = []; +// ArrayClassInfo.prototype.isArrayClass = true; +// +// export class PrimitiveClassInfo extends ClassInfo { +// constructor(className: string, mangledName: string) { +// false && super(null); +// this.className = className; +// this.mangledName = mangledName; +// } +// static Z = new PrimitiveClassInfo("Z", "boolean"); +// static C = new PrimitiveClassInfo("C", "char"); +// static F = new PrimitiveClassInfo("F", "float"); +// static D = new PrimitiveClassInfo("D", "double"); +// static B = new PrimitiveClassInfo("B", "byte"); +// static S = new PrimitiveClassInfo("S", "short"); +// static I = new PrimitiveClassInfo("I", "int"); +// static J = new PrimitiveClassInfo("J", "long"); +// } +// +// PrimitiveClassInfo.prototype.fields = []; +// PrimitiveClassInfo.prototype.methods = []; +// PrimitiveClassInfo.prototype.interfaces = []; +// +// export class PrimitiveArrayClassInfo extends ArrayClassInfo { +// constructor(className: string, elementClass: ClassInfo, mangledName: string) { +// super(className, elementClass, mangledName); +// } +// +// get superClass() { +// return CLASSES.java_lang_Object; +// } +// +// static Z = new PrimitiveArrayClassInfo("[Z", PrimitiveClassInfo.Z, "Uint8Array"); +// static C = new PrimitiveArrayClassInfo("[C", PrimitiveClassInfo.C, "Uint16Array"); +// static F = new PrimitiveArrayClassInfo("[F", PrimitiveClassInfo.F, "Float32Array"); +// static D = new PrimitiveArrayClassInfo("[D", PrimitiveClassInfo.D, "Float64Array"); +// static B = new PrimitiveArrayClassInfo("[B", PrimitiveClassInfo.B, "Int8Array"); +// static S = new PrimitiveArrayClassInfo("[S", PrimitiveClassInfo.S, "Int16Array"); +// static I = new PrimitiveArrayClassInfo("[I", PrimitiveClassInfo.I, "Int32Array"); +// static J = new PrimitiveArrayClassInfo("[J", PrimitiveClassInfo.J, "Int64Array"); +// } +// +// PrimitiveClassInfo.prototype.fields = []; +// PrimitiveClassInfo.prototype.methods = []; +// PrimitiveClassInfo.prototype.interfaces = []; +//} +// +//var FieldInfo = J2ME.FieldInfo; +//var MethodInfo = J2ME.MethodInfo; +//var ClassInfo = J2ME.ClassInfo; diff --git a/interpreter.ts b/interpreter.ts index 5a29803b..7a172481 100644 --- a/interpreter.ts +++ b/interpreter.ts @@ -201,7 +201,7 @@ module J2ME { var frames = ctx.frames; var mi = frame.methodInfo; var ci = mi.classInfo; - var rp = ci.resolved_constant_pool; + var rp = ci.constantPool.resolved; var stack = frame.stack; @@ -287,7 +287,7 @@ module J2ME { } mi = frame.methodInfo; ci = mi.classInfo; - rp = ci.resolved_constant_pool; + rp = ci.constantPool.resolved; stack = frame.stack; lastPC = -1; @@ -1074,7 +1074,7 @@ module J2ME { mi = frame.methodInfo; mi.interpreterCallCount ++; ci = mi.classInfo; - rp = ci.resolved_constant_pool; + rp = ci.constantPool.resolved; stack = frame.stack; lastPC = -1; continue; @@ -1179,7 +1179,7 @@ module J2ME { mi = frame.methodInfo; mi.interpreterCallCount ++; ci = mi.classInfo; - rp = ci.resolved_constant_pool; + rp = ci.constantPool.resolved; stack = frame.stack; lastPC = -1; if (calleeTargetMethodInfo.isSynchronized) { @@ -1270,7 +1270,7 @@ module J2ME { } mi = frame.methodInfo; ci = mi.classInfo; - rp = ci.resolved_constant_pool; + rp = ci.constantPool.resolved; stack = frame.stack; lastPC = -1; if (op === Bytecodes.RETURN) { @@ -1302,7 +1302,7 @@ module J2ME { assert (!Frame.isMarker(frame)); mi = frame.methodInfo; ci = mi.classInfo; - rp = ci.resolved_constant_pool; + rp = ci.constantPool.resolved; stack = frame.stack; lastPC = -1; continue; diff --git a/jit/analyze.ts b/jit/analyze.ts index e1fae799..e5aa5aaf 100644 --- a/jit/analyze.ts +++ b/jit/analyze.ts @@ -74,7 +74,7 @@ module J2ME { // These can technically yield but are worth the risk. // XXX Determine the current status of this item. // "java/lang/Object.equals.(Ljava/lang/Object;)Z": YieldReason.None - } + }; export function isFinalClass(classInfo: ClassInfo): boolean { var result = classInfo.isFinal; @@ -97,7 +97,7 @@ module J2ME { var allSubClasses = classInfo.allSubClasses; result = true; for (var i = 0; i < allSubClasses.length; i++) { - var subClassMethods = allSubClasses[i].methods; + var subClassMethods = allSubClasses[i].getMethods(); for (var j = 0; j < subClassMethods.length; j++) { var subClassMethodInfo = subClassMethods[j]; if (methodInfo.name === subClassMethodInfo.name && @@ -112,7 +112,7 @@ module J2ME { } export function gatherCallees(callees: MethodInfo [], classInfo: ClassInfo, methodInfo: MethodInfo) { - var methods = classInfo.methods; + var methods = classInfo.getMethods(); for (var i = 0; i < methods.length; i++) { var method = methods[i]; @@ -237,7 +237,7 @@ module J2ME { case Bytecodes.INVOKESPECIAL: case Bytecodes.INVOKESTATIC: var cpi = stream.readCPI(); - var callee = methodInfo.classInfo.resolve(cpi, op === Bytecodes.INVOKESTATIC); + var callee = methodInfo.classInfo.resolveMethod(cpi, op === Bytecodes.INVOKESTATIC); if (op !== Bytecodes.INVOKESTATIC) { if (yieldVirtualMap[methodInfo.implKey] === YieldReason.None) { diff --git a/jit/baseline.ts b/jit/baseline.ts index 7f3dcc1d..25b8b612 100644 --- a/jit/baseline.ts +++ b/jit/baseline.ts @@ -243,7 +243,7 @@ module J2ME { if (classInfo instanceof PrimitiveArrayClassInfo) { return classInfo.mangledName; } - if (classInfo.isArrayClass) { + if (classInfo instanceof ArrayClassInfo) { return "AK(" + classConstant(classInfo.elementClass) + ")"; } if (classInfo.mangledName) { @@ -570,19 +570,19 @@ module J2ME { } lookupClass(cpi: number): ClassInfo { - var classInfo = this.methodInfo.classInfo.resolve(cpi, false); + var classInfo = this.methodInfo.classInfo.resolveClass(cpi); ArrayUtilities.pushUnique(this.referencedClasses, classInfo); return classInfo; } lookupMethod(cpi: number, opcode: Bytecodes, isStatic: boolean): MethodInfo { - var methodInfo = this.methodInfo.classInfo.resolve(cpi, isStatic); + var methodInfo = this.methodInfo.classInfo.resolveMethod(cpi, isStatic); ArrayUtilities.pushUnique(this.referencedClasses, methodInfo.classInfo); return methodInfo; } lookupField(cpi: number, opcode: Bytecodes, isStatic: boolean): FieldInfo { - var fieldInfo = this.methodInfo.classInfo.resolve(cpi, isStatic); + var fieldInfo = this.methodInfo.classInfo.resolveField(cpi); ArrayUtilities.pushUnique(this.referencedClasses, fieldInfo.classInfo); return fieldInfo; } @@ -725,8 +725,8 @@ module J2ME { } emitClassInitializationCheck(classInfo: ClassInfo) { - while (classInfo.isArrayClass) { - classInfo = classInfo.elementClass; + while (classInfo instanceof ArrayClassInfo) { + classInfo = (classInfo).elementClass; } if (!CLASSES.isPreInitializedClass(classInfo)) { if (this.target === CompilationTarget.Runtime && $.initialized[classInfo.className]) { diff --git a/jit/builder.ts b/jit/builder.ts deleted file mode 100644 index 5c826a5f..00000000 --- a/jit/builder.ts +++ /dev/null @@ -1,1405 +0,0 @@ -module J2ME { - - var debug = false; - var writer = null; // new IndentingWriter(true); - var consoleWriter = new IndentingWriter(); - - export var counter = new J2ME.Metrics.Counter(true); - - export function printResults() { - counter.traceSorted(stderrWriter); - for (var k in yieldMap) { - stderrWriter.writeLn(YieldReason[yieldMap[k]].padRight(" ", 20) + " " + k); - } - } - - import Block = Bytecode.Block; - import BlockMap = Bytecode.BlockMap; - - import assert = Debug.assert; - import unique = ArrayUtilities.unique; - - import IR = C4.IR; - import Node = IR.Node; - import Value = IR.Value; - import Phi = IR.Phi; - import Control = IR.Control; - import Constant = IR.Constant; - import Start = IR.Start; - import Region = IR.Region; - import ProjectionType = IR.ProjectionType; - import Null = IR.Null; - import Undefined = IR.Undefined; - import True = IR.True; - import False = IR.False; - import Operator = IR.Operator; - import PeepholeOptimizer = IR.PeepholeOptimizer; - - import Bytecodes = Bytecode.Bytecodes; - import BytecodeStream = Bytecode.BytecodeStream; - import Condition = Bytecode.Condition; - - function kindsFromSignature(signature: string) { - - } - - var staticCallGraph = Object.create(null); - - declare var Long: any; - declare var VM: any; - - function conditionToOperator(condition: Condition): Operator { - switch (condition) { - case Condition.EQ: return Operator.EQ; - case Condition.NE: return Operator.NE; - case Condition.LT: return Operator.LT; - case Condition.LE: return Operator.LE; - case Condition.GT: return Operator.GT; - case Condition.GE: return Operator.GE; - default: throw "TODO" - } - } - - export function assertHigh(x: Value) { - assert (x === null); - } - - - function assertKind(kind: Kind, x: Node): Node { - assert(stackKind(x.kind) === stackKind(kind), "Got " + kindCharacter(stackKind(x.kind)) + " expected " + kindCharacter(stackKind(kind))); - return x; - } - - export class State { - private static _nextID = 0; - id: number; - bci: number; - nextBCI: number; - local: Value []; - stack: Value []; - store: Value; - loads: Value []; - constructor(bci: number = 0) { - this.id = State._nextID += 1; - this.bci = bci; - this.nextBCI = -1; - this.local = []; - this.stack = []; - this.store = Undefined; - this.loads = []; - } - - clone(bci: number, nextBCI: number = -1) { - var s = new State(); - s.bci = bci !== undefined ? bci : this.bci; - s.nextBCI = nextBCI; - s.local = this.local.slice(0); - s.stack = this.stack.slice(0); - s.loads = this.loads.slice(0); - s.store = this.store; - return s; - } - - matches(other: State) { - return this.stack.length === other.stack.length && - this.local.length === other.local.length; - } - - makeLoopPhis(control: Control, dirtyLocals: boolean []) { - var s = new State(); - release || assert (control); - function makePhi(x) { - var phi = new Phi(control, x); - phi.kind = x.kind; - phi.isLoop = true; - return phi; - } - s.bci = this.bci; - s.local = this.local.map(function (v, i) { - if (v === null) { - return null; - } - if (true || dirtyLocals[i]) { - return makePhi(v); - } - return v; - }); - s.stack = this.stack.map(makePhi); - s.loads = this.loads.slice(0); - s.store = makePhi(this.store); - return s; - } - - static tryOptimizePhi(x: Value) { - if (x instanceof Phi) { - var phi: Phi = x; - if (phi.isLoop) { - return phi; - } - var args = unique(phi.args); - if (args.length === 1) { - phi.seal(); - countTimeline("Builder: OptimizedPhi"); - return args[0]; - } - } - return x; - } - - optimize() { - this.local = this.local.map(State.tryOptimizePhi); - this.stack = this.stack.map(State.tryOptimizePhi); - this.store = State.tryOptimizePhi(this.store); - } - - static mergeValue(control: Control, a: Value, b: Value): Phi { - var phi; - if (a instanceof Phi && a.control === control) { - phi = a; - } else { - phi = new Phi(control, a); - phi.kind = a.kind; - } - if (a.kind === Kind.Store) { - release || assert(b.kind === Kind.Store, "Got " + Kind[b.kind] + " should be store."); - } else if (b === null || b === Illegal || stackKind(a.kind) !== stackKind(b.kind)) { - // TODO get rid of the null check by pushing Illegals for doubles/longs. - b = Illegal; - } - phi.pushValue(b); - return phi; - } - - static mergeValues(control: Control, a: Value [], b: Value []) { - for (var i = 0; i < a.length; i++) { - if (a[i] === null) { - continue; - } - a[i] = State.mergeValue(control, a[i], b[i]); - if (isTwoSlot(a[i].kind)) { - i++; - } - } - } - - merge(control: Control, other: State) { - release || assert (control); - release || assert (this.matches(other), this + " !== " + other); - State.mergeValues(control, this.local, other.local); - State.mergeValues(control, this.stack, other.stack); - this.store = State.mergeValue(control, this.store, other.store); - this.store.abstract = true; - } - - trace(writer: IndentingWriter) { - writer.writeLn(this.toString()); - } - - static toBriefString(x: Node) : string { - if (x instanceof Node) { - return kindCharacter(x.kind); // + x.toString(true); - } - if (x === null) { - return "_"; - } else if (x === undefined) { - return "undefined"; - } - return String(x); - } - - toString(): string { - return "<" + String(this.id + " @ " + this.bci).padRight(' ', 10) + - (" M: " + State.toBriefString(this.store)).padRight(' ', 14) + - (" L: " + this.local.map(State.toBriefString).join(", ")).padRight(' ', 40) + - (" S: " + this.stack.map(State.toBriefString).join(", ")).padRight(' ', 60) + - (" R: " + this.loads.map(State.toBriefString).join(", ")).padRight(' ', 60); - } - - /** - * Pushes a value onto the stack without checking the type. - */ - public xpush(x: Node) { - assert (x === null || !x.isDeleted); - assert (x === null || (x.kind !== Kind.Void && x.kind !== Kind.Illegal), "Unexpected value: " + x); - this.stack.push(x); - } - - /** - * Pushes a value onto the stack and checks that it is an int. - */ - public ipush(x: Node) { - this.push(Kind.Int, x); - } - - /** - * Pushes a value onto the stack and checks that it is a float. - * @param x the instruction to push onto the stack - */ - public fpush(x: Node) { - this.push(Kind.Float, x); - } - - /** - * Pushes a value onto the stack and checks that it is an object. - */ - public apush(x: Node) { - this.push(Kind.Reference, x); - } - - /** - * Pushes a value onto the stack and checks that it is a long. - */ - public lpush(x: Node) { - this.push(Kind.Long, x); - } - - /** - * Pushes a value onto the stack and checks that it is a double. - */ - public dpush(x: Node) { - this.push(Kind.Double, x); - } - - /** - * Pushes an instruction onto the stack with the expected type. - */ - public push(kind: Kind, x: Value) { - assert (kind !== Kind.Void); - if (x.kind === undefined) { - x.kind = kind; - } - - this.xpush(assertKind(kind, x)); - if (isTwoSlot(kind)) { - this.xpush(null); - } - } - - /** - * Pops an instruction off the stack with the expected type. - */ - public pop(kind: Kind): Value { - assert (kind !== Kind.Void); - kind = stackKind(kind); - if (isTwoSlot(kind)) { - this.xpop(); - } - return assertKind(kind, this.xpop()); - } - - /** - * Pops a value off of the stack without checking the type. - */ - public xpop(): Value { - var result = this.stack.pop(); - assert (result === null || !result.isDeleted); - return result; - } - - /** - * Pops a value off of the stack and checks that it is an int. - */ - public ipop(): Value { - return assertKind(Kind.Int, this.xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a float. - */ - public fpop(): Value { - return assertKind(Kind.Float, this.xpop()); - } - - /** - * Pops a value off of the stack and checks that it is an object. - */ - public apop(): Value { - return assertKind(Kind.Reference, this.xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a long. - */ - public lpop(): Value { - assertHigh(this.xpop()); - return assertKind(Kind.Long, this.xpop()); - } - - /** - * Pops a value off of the stack and checks that it is a double. - */ - public dpop(): Value { - assertHigh(this.xpop()); - return assertKind(Kind.Double, this.xpop()); - } - - public peek(): Value { - return this.stack[this.stack.length - 1]; - } - - /** - * Loads the local variable at the specified index. - */ - public loadLocal(i: number): Value { - var x = this.local[i]; - if (x != null) { - if (x instanceof Phi) { - // assert ((PhiNode) x).type() == PhiType.Value; - if (x.isDeleted) { - return null; - } - } - assert (!isTwoSlot(x.kind) || this.local[i + 1] === null || this.local[i + 1] instanceof Phi); - } - return x; - } - - /** - * Stores a given local variable at the specified index. If the value takes up two slots, - * then the next local variable index is also overwritten. - */ - public storeLocal(i: number, x: Value) { - assert (x === null || (x.kind !== Kind.Void && x.kind !== Kind.Illegal), "Unexpected value: " + x); - var local = this.local; - local[i] = x; - if (isTwoSlot(x.kind)) { - // (tw) if this was a double word then kill i + 1 - local[i + 1] = null; - } - if (i > 0) { - // if there was a double word at i - 1, then kill it - var p = local[i - 1]; - if (p !== null && isTwoSlot(p.kind)) { - local[i - 1] = null; - } - } - } - } - - interface WorklistItem { - region: Region; - block: Block; - } - - interface Stop { - - } - - function genConstant(x: any, kind: Kind): IR.Constant { - var constant; - if (kind === Kind.Long) { - constant = new IR.JVMLong(x, 0); - } else if (kind === Kind.Reference) { - if (isString(x)) { - constant = new IR.JVMString(x); - } else { - constant = new IR.Constant(x); - } - } else { - constant = new IR.Constant(x); - } - constant.kind = kind; - return constant; - } - - var Illegal = genConstant(undefined, Kind.Illegal); - - class StopInfo { - constructor( - public control: Control, - public target: Block, - public state: State) { - // ... - } - } - - class ReturnInfo { - constructor( - public control: Control, - public store: Node, - public value: Value) { - // ... - } - } - - - /** - * TODO: Consider using debug info for nicer parameter names, if available. - */ - function getParameterName(methodInfo: MethodInfo, i: number): string { - var parameterNames = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - return i < parameterNames.length ? parameterNames[i] : "p" + (parameterNames.length - i); - } - - class Builder { - peepholeOptimizer: PeepholeOptimizer; - signatureDescriptor: SignatureDescriptor; - - /** - * Current state vector. - */ - state: State; - - /** - * Current region. - */ - region: Region; - - /** - * Stop infos accumulated for the last processed block. - */ - blockStopInfos: StopInfo []; - - /** - * Methor return infos accumulated during the processing of this method. - */ - methodReturnInfos: ReturnInfo []; - - /** - * Current block map. - */ - blockMap: BlockMap; - - parameters: IR.Parameter []; - - referencedClasses: ClassInfo []; - - constructor(public methodInfo: MethodInfo, public target: CompilationTarget) { - // ... - this.peepholeOptimizer = new PeepholeOptimizer(); - this.signatureDescriptor = SignatureDescriptor.makeSignatureDescriptor(methodInfo.signature); - this.methodReturnInfos = []; - this.parameters = []; - this.referencedClasses = []; - } - - build(): C4.Backend.Compilation { - IR.Node.startNumbering(); - - var methodInfo = this.methodInfo; - - writer && writer.enter("Compiling Method: " + methodInfo.name + " " + methodInfo.signature + " {"); - writer && writer.writeLn("Size: " + methodInfo.code.length); - var blockMap = this.blockMap = new BlockMap(methodInfo); - blockMap.build(); - - var start = this.buildStart(); - var dfg = this.buildGraph(start, start.entryState.clone()); - - writer && dfg.trace(writer); - - enterTimeline("Build CFG"); - var cfg = dfg.buildCFG(); - leaveTimeline(); - - enterTimeline("Verify IR"); - cfg.verify(); - leaveTimeline(); - - enterTimeline("Optimize Phis"); - cfg.optimizePhis(); - leaveTimeline(); - - enterTimeline("Schedule Nodes"); - cfg.scheduleEarly(); - leaveTimeline(); - - writer && cfg.trace(writer); - - enterTimeline("Verify IR"); - cfg.verify(); - leaveTimeline(); - - enterTimeline("Allocate Variables"); - cfg.allocateVariables(); - leaveTimeline(); - - enterTimeline("Generate Source"); - var result = C4.Backend.generate(cfg); - leaveTimeline(); - - Node.stopNumbering(); - leaveTimeline(); - - writer && writer.leave("}"); - IR.Node.stopNumbering(); - return result; - } - - buildStart(): IR.Start { - var start = new IR.Start(); - var state = start.entryState = new State(); - var methodInfo = this.methodInfo; - - for (var i = 0; i < methodInfo.max_locals; i++) { - state.local.push(null); - } - - state.store = new IR.Projection(start, ProjectionType.STORE); - state.store.kind = Kind.Store; - - var signatureDescriptor = this.signatureDescriptor; - writer && writer.writeLn("SIG: " + signatureDescriptor); - - var typeDescriptors = signatureDescriptor.typeDescriptors; - - var localIndex = 0; - var parameterIndex = 1; - if (!methodInfo.isStatic) { - var self = new IR.This(start); - self.kind = Kind.Reference; - state.storeLocal(0, self); - parameterIndex++; - localIndex = 1; - } - // Skip the first typeDescriptor since it is the return type. - for (var i = 1; i < typeDescriptors.length; i++) { - var kind = Kind.Reference; - if (typeDescriptors[i] instanceof AtomicTypeDescriptor) { - kind = (typeDescriptors[i]).kind; - } - var parameter = new IR.Parameter(start, parameterIndex, getParameterName(this.methodInfo, parameterIndex - 1)); - this.parameters.push(parameter); - parameter.kind = kind; - parameterIndex++; - state.storeLocal(localIndex, parameter); - localIndex += isTwoSlot(kind) ? 2 : 1; - } - return start; - } - - buildGraph(start: Region, state: State): IR.DFG { - var worklist = new SortedList(function compare(a: WorklistItem, b: WorklistItem) { - return a.block.blockID - b.block.blockID; - }); - - worklist.push({ - region: start, - block: this.blockMap.blocks[0] - }); - - var next: WorklistItem; - while ((next = worklist.pop())) { - writer && writer.writeLn("Processing: " + next.region + " " + next.block.blockID + " " + next.region.entryState); - this.buildBlock(next.region, next.block, next.region.entryState.clone()); - if (!this.blockStopInfos) { - continue; - } - this.blockStopInfos.forEach(function (stop: StopInfo) { - var target = stop.target; - writer && writer.writeLn(String(target)); - var region = target.region; - if (region) { - writer && writer.enter("Merging into region: " + region + " @ " + target.startBci + ", block " + target.blockID + " {"); - writer && writer.writeLn(" R " + region.entryState); - writer && writer.writeLn("+ I " + stop.state); - - region.entryState.merge(region, stop.state); - region.predecessors.push(stop.control); - - writer && writer.writeLn(" = " + region.entryState); - writer && writer.leave("}"); - } else { - region = target.region = new Region(stop.control); - var dirtyLocals: boolean [] = []; -// if (target.loop) { -// dirtyLocals = enableDirtyLocals.value && target.loop.getDirtyLocals(); -// writer && writer.writeLn("Adding PHIs to loop region. " + dirtyLocals); -// } - region.entryState = target.isLoopHeader ? stop.state.makeLoopPhis(region, dirtyLocals) : stop.state.clone(target.startBci); - writer && writer.writeLn("Adding new region: " + region + " @ " + target.startBci + " to worklist."); - worklist.push({region: region, block: target}); - } - }); - - writer && writer.enter("Worklist: {"); - worklist.forEach(function (item) { - writer && writer.writeLn(item.region + " " + item.block.blockID + " " + item.region.entryState); - }); - writer && writer.leave("}"); - } - var signatureDescriptor = this.signatureDescriptor; - var returnType = signatureDescriptor.typeDescriptors[0]; - - // TODO handle void return types - var stop; - var returnInfos = this.methodReturnInfos; - assert (returnInfos.length > 0); - var returnRegion = new Region(null); - var returnValuePhi = new Phi(returnRegion, null); - var returnStorePhi = new Phi(returnRegion, null); - returnInfos.forEach(function (returnInfo) { - returnRegion.predecessors.push(returnInfo.control); - returnValuePhi.pushValue(returnInfo.value); - returnStorePhi.pushValue(returnInfo.store); - }); - stop = new IR.Stop(returnRegion, returnStorePhi, returnValuePhi); - return new IR.DFG(stop); - } - - buildBlock(region: Region, block: Block, state: State) { - this.blockStopInfos = null; - this.state = state; - this.region = region; - var code = this.methodInfo.code; - var stream = new BytecodeStream(code); - var bci = block.startBci; - stream.setBCI(bci); - - while (stream.currentBCI <= block.endBci) { - state.bci = bci; - this.processBytecode(stream, state); - if (Bytecode.isReturn(stream.currentBC()) || - Bytecodes.ATHROW === stream.currentBC()) { - release || assert (!this.blockStopInfos, "Should not have any stops."); - return; - } - stream.next(); - bci = stream.currentBCI; - } - - if (!this.blockStopInfos) { - this.blockStopInfos = [new StopInfo(region, - this.blockMap.getBlock(stream.currentBCI), - this.state - )]; - } - } - - private loadLocal(index: number, kind: Kind) { - this.state.push(kind, this.state.loadLocal(index)); - } - - private storeLocal(kind: Kind, index: number) { - this.state.storeLocal(index, this.state.pop(kind)); - } - - private stackOp(opcode: Bytecodes) { - var state = this.state; - switch (opcode) { - case Bytecodes.POP: { - state.xpop(); - break; - } - case Bytecodes.POP2: { - state.xpop(); - state.xpop(); - break; - } - case Bytecodes.DUP: { - var w = state.xpop(); - state.xpush(w); - state.xpush(w); - break; - } - case Bytecodes.DUP_X1: { - var w1 = state.xpop(); - var w2 = state.xpop(); - state.xpush(w1); - state.xpush(w2); - state.xpush(w1); - break; - } - case Bytecodes.DUP_X2: { - var w1 = state.xpop(); - var w2 = state.xpop(); - var w3 = state.xpop(); - state.xpush(w1); - state.xpush(w3); - state.xpush(w2); - state.xpush(w1); - break; - } - case Bytecodes.DUP2: { - var w1 = state.xpop(); - var w2 = state.xpop(); - state.xpush(w2); - state.xpush(w1); - state.xpush(w2); - state.xpush(w1); - break; - } - case Bytecodes.DUP2_X1: { - var w1 = state.xpop(); - var w2 = state.xpop(); - var w3 = state.xpop(); - state.xpush(w2); - state.xpush(w1); - state.xpush(w3); - state.xpush(w2); - state.xpush(w1); - break; - } - case Bytecodes.DUP2_X2: { - var w1 = state.xpop(); - var w2 = state.xpop(); - var w3 = state.xpop(); - var w4 = state.xpop(); - state.xpush(w2); - state.xpush(w1); - state.xpush(w4); - state.xpush(w3); - state.xpush(w2); - state.xpush(w1); - break; - } - case Bytecodes.SWAP: { - var w1 = state.xpop(); - var w2 = state.xpop(); - state.xpush(w1); - state.xpush(w2); - break; - } - default: - Debug.unexpected(""); - } - } - - genArithmeticOp(result: Kind, opcode: Bytecodes, canTrap: boolean) { - var state = this.state; - var y = state.pop(result); - var x = state.pop(result); - if (canTrap) { - this.genDivideByZeroCheck(y); - } - var v; -// var isStrictFP = false; // TODO - switch(opcode) { - case Bytecodes.IADD: v = new IR.Binary(Operator.IADD, x, y); break; - case Bytecodes.LADD: v = new IR.JVMLongBinary(Operator.LADD, x, y); break; - case Bytecodes.FADD: v = new IR.Binary(Operator.FADD, x, y/*, isStrictFP*/); break; - case Bytecodes.DADD: v = new IR.Binary(Operator.DADD, x, y/*, isStrictFP*/); break; - case Bytecodes.ISUB: v = new IR.Binary(Operator.ISUB, x, y); break; - case Bytecodes.LSUB: v = new IR.JVMLongBinary(Operator.LSUB, x, y); break; - case Bytecodes.FSUB: v = new IR.Binary(Operator.FSUB, x, y/*, isStrictFP*/); break; - case Bytecodes.DSUB: v = new IR.Binary(Operator.DSUB, x, y/*, isStrictFP*/); break; - case Bytecodes.IMUL: v = new IR.Binary(Operator.IMUL, x, y); break; - case Bytecodes.LMUL: v = new IR.JVMLongBinary(Operator.LMUL, x, y); break; - case Bytecodes.FMUL: v = new IR.Binary(Operator.FMUL, x, y/*, isStrictFP*/); break; - case Bytecodes.DMUL: v = new IR.Binary(Operator.DMUL, x, y/*, isStrictFP*/); break; - case Bytecodes.IDIV: v = new IR.Binary(Operator.IDIV, x, y); break; - case Bytecodes.LDIV: v = new IR.JVMLongBinary(Operator.LDIV, x, y); break; - case Bytecodes.FDIV: v = new IR.Binary(Operator.FDIV, x, y/*, isStrictFP*/); break; - case Bytecodes.DDIV: v = new IR.Binary(Operator.DDIV, x, y/*, isStrictFP*/); break; - case Bytecodes.IREM: v = new IR.Binary(Operator.IREM, x, y); break; - case Bytecodes.LREM: v = new IR.JVMLongBinary(Operator.LREM, x, y); break; - case Bytecodes.FREM: v = new IR.Binary(Operator.FREM, x, y/*, isStrictFP*/); break; - case Bytecodes.DREM: v = new IR.Binary(Operator.DREM, x, y/*, isStrictFP*/); break; - default: - assert(false); - } - v = this.peepholeOptimizer.fold(v); - state.push(result, v); - } - - genShiftOp(kind: Kind, opcode: Bytecodes) { - var state = this.state; - var s = state.ipop(); - var x = state.pop(kind); - var v; - switch(opcode){ - case Bytecodes.ISHL: v = new IR.Binary(Operator.LSH, x, s); break; - case Bytecodes.LSHL: v = new IR.JVMLongBinary(Operator.LSH, x, s); break; - case Bytecodes.ISHR: v = new IR.Binary(Operator.RSH, x, s); break; - case Bytecodes.LSHR: v = new IR.JVMLongBinary(Operator.RSH, x, s); break; - case Bytecodes.IUSHR: v = new IR.Binary(Operator.URSH, x, s); break; - case Bytecodes.LUSHR: v = new IR.JVMLongBinary(Operator.URSH, x, s); break; - default: - assert(false); - } - state.push(kind, v); - } - - genLogicOp(kind: Kind, opcode: Bytecodes) { - var state = this.state; - var y = state.pop(kind); - var x = state.pop(kind); - var v; - switch(opcode){ - case Bytecodes.IAND: v = new IR.Binary(Operator.AND, x, y); break; - case Bytecodes.LAND: v = new IR.JVMLongBinary(Operator.AND, x, y); break; - case Bytecodes.IOR: v = new IR.Binary(Operator.OR, x, y); break; - case Bytecodes.LOR: v = new IR.JVMLongBinary(Operator.OR, x, y); break; - case Bytecodes.IXOR: v = new IR.Binary(Operator.XOR, x, y); break; - case Bytecodes.LXOR: v = new IR.JVMLongBinary(Operator.XOR, x, y); break; - default: - assert(false); - } - state.push(kind, v); - } - - genNegateOp(kind: Kind) { - var x = this.state.pop(kind); - var v; - switch (kind) { - case Kind.Int: v = new IR.Unary(Operator.INEG, x); break; - case Kind.Long: v = new IR.JVMLongUnary(Operator.LNEG, x); break; - case Kind.Float: v = new IR.Unary(Operator.FNEG, x); break; - case Kind.Double: v = new IR.Unary(Operator.DNEG, x); break; - default: - assert(false); - } - this.state.push(kind, v); - } - - genNewInstance(cpi: number) { - var classInfo = this.lookupClass(cpi); - var jvmNew = new IR.JVMNew(this.region, this.state.store, classInfo); - this.recordStore(jvmNew); - this.state.apush(jvmNew); - } - - genNewTypeArray(typeCode: number) { - var kind = arrayTypeCodeToKind(typeCode); - var length = this.state.ipop(); - var result = new IR.JVMNewArray(this.region, this.state.store, kind, length); - this.recordStore(result); - this.state.apush(result); - } - - genNewObjectArray(cpi: number) { - var classInfo = this.lookupClass(cpi); - var length = this.state.ipop(); - var result = new IR.JVMNewObjectArray(this.region, this.state.store, classInfo, length); - this.recordStore(result); - this.state.apush(result); - } - - genLoadConstant(cpi: number, state: State) { - var cp = this.methodInfo.classInfo.constant_pool; - var entry = cp[cpi]; - switch (entry.tag) { - case TAGS.CONSTANT_Integer: - state.ipush(genConstant(entry.integer, Kind.Int)); - return; - case TAGS.CONSTANT_Float: - state.fpush(genConstant(entry.float, Kind.Float)); - return; - case TAGS.CONSTANT_Double: - state.dpush(genConstant(entry.double, Kind.Double)); - return; - case 5: // TAGS.CONSTANT_Long - state.lpush(new IR.JVMLong(entry.lowBits, entry.highBits)); - return; - case TAGS.CONSTANT_String: - entry = cp[entry.string_index]; - return state.push(Kind.Reference, genConstant(entry.bytes, Kind.Reference)); - - default: - throw "Not done for: " + entry.tag; - } - } - - genCheckCast(cpi: Bytecodes) { - var classInfo = this.lookupClass(cpi); - var object = this.state.peek(); - var checkCast = new IR.JVMCheckCast(this.region, this.state.store, object, classInfo); - this.recordStore(checkCast); - } - - genInstanceOf(cpi: Bytecodes) { - var classInfo = this.lookupClass(cpi); - var object = this.state.apop(); - var instanceOf = new IR.JVMInstanceOf(this.region, this.state.store, object, classInfo); - this.recordStore(instanceOf); - this.state.push(Kind.Boolean, instanceOf); - } - - genIncrement(stream: BytecodeStream) { - var index = stream.readLocalIndex(); - var local = this.state.loadLocal(index); - var increment = genConstant(stream.readIncrement(), Kind.Int); - var value = new IR.Binary(Operator.IADD, local, increment); - value.kind = stackKind(local.kind); - this.state.storeLocal(index, value); - } - - genConvert(from: Kind, to: Kind) { - var value = this.state.pop(from); - this.state.push(to, new IR.JVMConvert(from, to, value)); - } - - genIf(stream: BytecodeStream, predicate: IR.Binary) { - release || assert (!this.blockStopInfos); - var _if = new IR.If(this.region, predicate); - this.blockStopInfos = [new StopInfo( - new IR.Projection(_if, ProjectionType.TRUE), - this.blockMap.getBlock(stream.readBranchDest()), - this.state - ), new StopInfo( - new IR.Projection(_if, ProjectionType.FALSE), - this.blockMap.getBlock(stream.nextBCI), - this.state - )]; - } - - genIfNull(stream: BytecodeStream, condition: Condition) { - this.state.apush(Null); - var y = this.state.apop(); - var x = this.state.apop(); - this.genIf(stream, new IR.Binary(conditionToOperator(condition), x, y)); - } - - genIfSame(stream: BytecodeStream, kind: Kind, condition: Condition) { - var y = this.state.pop(kind); - var x = this.state.pop(kind); - this.genIf(stream, new IR.Binary(conditionToOperator(condition), x, y)); - } - - genIfZero(stream: BytecodeStream, condition: Condition) { - this.state.ipush(genConstant(0, Kind.Int)); - var y = this.state.ipop(); - var x = this.state.ipop(); - this.genIf(stream, new IR.Binary(conditionToOperator(condition), x, y)); - } - - genCompareOp(kind: Kind, isLessThan: boolean) { - var b = this.state.pop(kind); - var a = this.state.pop(kind); - var compare; - if (kind === Kind.Long) { - compare = new IR.JVMLongCompare(this.region, a, b); - } else { - compare = new IR.JVMFloatCompare(this.region, a, b, isLessThan); - } - this.state.ipush(compare); - } - - genGoto(stream: BytecodeStream) { - release || assert (!this.blockStopInfos); - this.blockStopInfos = [new StopInfo( - this.region, - this.blockMap.getBlock(stream.readBranchDest()), - this.state - )]; - } - - genReturn(value: Value) { - if (value === null) { - value = Undefined; - } - this.methodReturnInfos.push(new ReturnInfo( - this.region, - this.state.store, - value - )); - } - - lookupClass(cpi: number): ClassInfo { - var classInfo = this.methodInfo.classInfo.resolve(cpi, false); - ArrayUtilities.pushUnique(this.referencedClasses, classInfo); - return classInfo; - } - - lookupMethod(cpi: number, opcode: Bytecodes, isStatic: boolean): MethodInfo { - var methodInfo = this.methodInfo.classInfo.resolve(cpi, isStatic); - ArrayUtilities.pushUnique(this.referencedClasses, methodInfo.classInfo); - return methodInfo; - } - - lookupField(cpi: number, opcode: Bytecodes, isStatic: boolean): FieldInfo { - var fieldInfo = this.methodInfo.classInfo.resolve(cpi, isStatic); - ArrayUtilities.pushUnique(this.referencedClasses, fieldInfo.classInfo); - return fieldInfo; - } - - /** - * Marks the |node| as the active store node, with dependencies on all loads appearing after the - * previous active store node. - */ - recordStore(node: any) { - var state = this.state; - state.store = new IR.Projection(node, ProjectionType.STORE); - state.store.kind = Kind.Store; - node.loads = state.loads.slice(0); - state.loads.length = 0; - } - - /** - * Keeps track of the current set of loads. - */ - recordLoad(node: Node): Value { - var state = this.state; - state.loads.push(node); - return node; - } - - genDivideByZeroCheck(value: Value) { - var checkArithmetic = new IR.JVMCheckArithmetic(this.region, this.state.store, value); - this.recordStore(checkArithmetic); - } - - genThrow(bci: number) { - var object = this.state.peek(); - var _throw = new IR.JVMThrow(this.region, this.state.store, object); - this.recordStore(_throw); - this.methodReturnInfos.push(new ReturnInfo( - this.region, - this.state.store, - Undefined - )); - } - - genInvoke(methodInfo: MethodInfo, opcode: Bytecodes, currentBCI: number, nextBCI: number) { - var calleeCanYield = YieldReason.Virtual; - if (isStaticallyBound(opcode, methodInfo)) { - calleeCanYield = canYield(methodInfo); - } - var signature = SignatureDescriptor.makeSignatureDescriptor(methodInfo.signature); - var types = signature.typeDescriptors; - var args: Value [] = []; - for (var i = types.length - 1; i > 0; i--) { - args.unshift(this.state.pop(types[i].kind)); - } - var object = null; - if (opcode !== Bytecodes.INVOKESTATIC) { - object = this.state.pop(Kind.Reference); - } - - //switch (methodInfo.implKey) { - // // There's no reason to call the object initializer. - // case "java/lang/Object..()V": - // return; - //} - - var state; - if (calleeCanYield) { - // Only save the state if the callee can yield. - state = this.state.clone(currentBCI, nextBCI); - } - counter && counter.count("Yield Code: " + YieldReason[calleeCanYield] + " " + methodInfo.implKey); - counter && counter.count("Yield Code: " + YieldReason[calleeCanYield]); - var call = new IR.JVMInvoke(this.region, this.state.store, state, opcode, object, methodInfo, args); - this.recordStore(call); - if (types[0].kind !== Kind.Void) { - this.state.push(types[0].kind, call); - } - } - - genStoreIndexed(kind: Kind) { - var value = this.state.pop(stackKind(kind)); - var index = this.state.ipop(); - var array = this.state.apop(); - var arrayStore = new IR.JVMStoreIndexed(this.region, this.state.store, kind, array, index, value); - this.recordStore(arrayStore); - } - - genLoadIndexed(kind: Kind) { - var index = this.state.ipop(); - var array = this.state.apop(); - var arrayLoad = new IR.JVMLoadIndexed(this.region, this.state.store, kind, array, index); - this.recordLoad(arrayLoad); - this.state.push(kind, arrayLoad); - } - - genArrayLength() { - var array = this.state.apop(); - var getProperty = new IR.GetProperty(this.region, this.state.store, array, new Constant('length')); - this.recordLoad(getProperty); - this.state.ipush(getProperty); - } - - genClass(classInfo: ClassInfo): Value { - ArrayUtilities.pushUnique(this.referencedClasses, classInfo); - return new IR.JVMClass(classInfo); - } - - genGetField(fieldInfo: FieldInfo, isStatic: boolean) { - var signature = TypeDescriptor.makeTypeDescriptor(fieldInfo.signature); - var object = isStatic ? null : this.state.apop(); - var getField = new IR.JVMGetField(this.region, this.state.store, object, fieldInfo); - this.recordLoad(getField); - this.state.push(signature.kind, getField); - } - - genPutField(fieldInfo: FieldInfo, isStatic: boolean) { - var signature = TypeDescriptor.makeTypeDescriptor(fieldInfo.signature); - var value = this.state.pop(signature.kind); - var object = isStatic ? null : this.state.apop(); - var putField = new IR.JVMPutField(this.region, this.state.store, object, fieldInfo, value); - this.recordStore(putField); - } - - processBytecode(stream: BytecodeStream, state: State) { - var cpi: number; - var opcode: Bytecodes = stream.currentBC(); - writer && writer.enter("State Before: " + Bytecodes[opcode].padRight(" ", 12) + " " + state.toString()); - switch (opcode) { - case Bytecodes.NOP : break; - case Bytecodes.ACONST_NULL : state.apush(genConstant(null, Kind.Reference)); break; - case Bytecodes.ICONST_M1 : state.ipush(genConstant(-1, Kind.Int)); break; - case Bytecodes.ICONST_0 : state.ipush(genConstant(0, Kind.Int)); break; - case Bytecodes.ICONST_1 : state.ipush(genConstant(1, Kind.Int)); break; - case Bytecodes.ICONST_2 : state.ipush(genConstant(2, Kind.Int)); break; - case Bytecodes.ICONST_3 : state.ipush(genConstant(3, Kind.Int)); break; - case Bytecodes.ICONST_4 : state.ipush(genConstant(4, Kind.Int)); break; - case Bytecodes.ICONST_5 : state.ipush(genConstant(5, Kind.Int)); break; - case Bytecodes.LCONST_0 : state.lpush(genConstant(0, Kind.Long)); break; - case Bytecodes.LCONST_1 : state.lpush(genConstant(1, Kind.Long)); break; - case Bytecodes.FCONST_0 : state.fpush(genConstant(0, Kind.Float)); break; - case Bytecodes.FCONST_1 : state.fpush(genConstant(1, Kind.Float)); break; - case Bytecodes.FCONST_2 : state.fpush(genConstant(2, Kind.Float)); break; - case Bytecodes.DCONST_0 : state.dpush(genConstant(0, Kind.Double)); break; - case Bytecodes.DCONST_1 : state.dpush(genConstant(1, Kind.Double)); break; - case Bytecodes.BIPUSH : state.ipush(genConstant(stream.readByte(), Kind.Int)); break; - case Bytecodes.SIPUSH : state.ipush(genConstant(stream.readShort(), Kind.Int)); break; - case Bytecodes.LDC : - case Bytecodes.LDC_W : - case Bytecodes.LDC2_W : this.genLoadConstant(stream.readCPI(), state); break; - case Bytecodes.ILOAD : this.loadLocal(stream.readLocalIndex(), Kind.Int); break; - case Bytecodes.LLOAD : this.loadLocal(stream.readLocalIndex(), Kind.Long); break; - case Bytecodes.FLOAD : this.loadLocal(stream.readLocalIndex(), Kind.Float); break; - case Bytecodes.DLOAD : this.loadLocal(stream.readLocalIndex(), Kind.Double); break; - case Bytecodes.ALOAD : this.loadLocal(stream.readLocalIndex(), Kind.Reference); break; - case Bytecodes.ILOAD_0 : - case Bytecodes.ILOAD_1 : - case Bytecodes.ILOAD_2 : - case Bytecodes.ILOAD_3 : this.loadLocal(opcode - Bytecodes.ILOAD_0, Kind.Int); break; - case Bytecodes.LLOAD_0 : - case Bytecodes.LLOAD_1 : - case Bytecodes.LLOAD_2 : - case Bytecodes.LLOAD_3 : this.loadLocal(opcode - Bytecodes.LLOAD_0, Kind.Long); break; - case Bytecodes.FLOAD_0 : - case Bytecodes.FLOAD_1 : - case Bytecodes.FLOAD_2 : - case Bytecodes.FLOAD_3 : this.loadLocal(opcode - Bytecodes.FLOAD_0, Kind.Float); break; - case Bytecodes.DLOAD_0 : - case Bytecodes.DLOAD_1 : - case Bytecodes.DLOAD_2 : - case Bytecodes.DLOAD_3 : this.loadLocal(opcode - Bytecodes.DLOAD_0, Kind.Double); break; - case Bytecodes.ALOAD_0 : - case Bytecodes.ALOAD_1 : - case Bytecodes.ALOAD_2 : - case Bytecodes.ALOAD_3 : this.loadLocal(opcode - Bytecodes.ALOAD_0, Kind.Reference); break; - - case Bytecodes.IALOAD : this.genLoadIndexed(Kind.Int); break; - case Bytecodes.LALOAD : this.genLoadIndexed(Kind.Long); break; - case Bytecodes.FALOAD : this.genLoadIndexed(Kind.Float); break; - case Bytecodes.DALOAD : this.genLoadIndexed(Kind.Double); break; - case Bytecodes.AALOAD : this.genLoadIndexed(Kind.Reference); break; - case Bytecodes.BALOAD : this.genLoadIndexed(Kind.Byte); break; - case Bytecodes.CALOAD : this.genLoadIndexed(Kind.Char); break; - case Bytecodes.SALOAD : this.genLoadIndexed(Kind.Short); break; - case Bytecodes.ISTORE : this.storeLocal(Kind.Int, stream.readLocalIndex()); break; - case Bytecodes.LSTORE : this.storeLocal(Kind.Long, stream.readLocalIndex()); break; - case Bytecodes.FSTORE : this.storeLocal(Kind.Float, stream.readLocalIndex()); break; - case Bytecodes.DSTORE : this.storeLocal(Kind.Double, stream.readLocalIndex()); break; - case Bytecodes.ASTORE : this.storeLocal(Kind.Reference, stream.readLocalIndex()); break; - case Bytecodes.ISTORE_0 : - case Bytecodes.ISTORE_1 : - case Bytecodes.ISTORE_2 : - case Bytecodes.ISTORE_3 : this.storeLocal(Kind.Int, opcode - Bytecodes.ISTORE_0); break; - case Bytecodes.LSTORE_0 : - case Bytecodes.LSTORE_1 : - case Bytecodes.LSTORE_2 : - case Bytecodes.LSTORE_3 : this.storeLocal(Kind.Long, opcode - Bytecodes.LSTORE_0); break; - case Bytecodes.FSTORE_0 : - case Bytecodes.FSTORE_1 : - case Bytecodes.FSTORE_2 : - case Bytecodes.FSTORE_3 : this.storeLocal(Kind.Float, opcode - Bytecodes.FSTORE_0); break; - case Bytecodes.DSTORE_0 : - case Bytecodes.DSTORE_1 : - case Bytecodes.DSTORE_2 : - case Bytecodes.DSTORE_3 : this.storeLocal(Kind.Double, opcode - Bytecodes.DSTORE_0); break; - case Bytecodes.ASTORE_0 : - case Bytecodes.ASTORE_1 : - case Bytecodes.ASTORE_2 : - case Bytecodes.ASTORE_3 : this.storeLocal(Kind.Reference, opcode - Bytecodes.ASTORE_0); break; - - - case Bytecodes.IASTORE : this.genStoreIndexed(Kind.Int); break; - case Bytecodes.LASTORE : this.genStoreIndexed(Kind.Long); break; - case Bytecodes.FASTORE : this.genStoreIndexed(Kind.Float); break; - case Bytecodes.DASTORE : this.genStoreIndexed(Kind.Double); break; - case Bytecodes.AASTORE : this.genStoreIndexed(Kind.Reference); break; - case Bytecodes.BASTORE : this.genStoreIndexed(Kind.Byte); break; - case Bytecodes.CASTORE : this.genStoreIndexed(Kind.Char); break; - case Bytecodes.SASTORE : this.genStoreIndexed(Kind.Short); break; - - case Bytecodes.POP : - case Bytecodes.POP2 : - case Bytecodes.DUP : - case Bytecodes.DUP_X1 : - case Bytecodes.DUP_X2 : - case Bytecodes.DUP2 : - case Bytecodes.DUP2_X1 : - case Bytecodes.DUP2_X2 : - case Bytecodes.SWAP : this.stackOp(opcode); break; - - case Bytecodes.IADD : - case Bytecodes.ISUB : - case Bytecodes.IMUL : this.genArithmeticOp(Kind.Int, opcode, false); break; - case Bytecodes.IDIV : - case Bytecodes.IREM : this.genArithmeticOp(Kind.Int, opcode, true); break; - case Bytecodes.LADD : - case Bytecodes.LSUB : - case Bytecodes.LMUL : this.genArithmeticOp(Kind.Long, opcode, false); break; - case Bytecodes.LDIV : - case Bytecodes.LREM : this.genArithmeticOp(Kind.Long, opcode, true); break; - case Bytecodes.FADD : - case Bytecodes.FSUB : - case Bytecodes.FMUL : - case Bytecodes.FDIV : - case Bytecodes.FREM : this.genArithmeticOp(Kind.Float, opcode, false); break; - case Bytecodes.DADD : - case Bytecodes.DSUB : - case Bytecodes.DMUL : - case Bytecodes.DDIV : - case Bytecodes.DREM : this.genArithmeticOp(Kind.Double, opcode, false); break; - case Bytecodes.INEG : this.genNegateOp(Kind.Int); break; - case Bytecodes.LNEG : this.genNegateOp(Kind.Long); break; - case Bytecodes.FNEG : this.genNegateOp(Kind.Float); break; - case Bytecodes.DNEG : this.genNegateOp(Kind.Double); break; - case Bytecodes.ISHL : - case Bytecodes.ISHR : - case Bytecodes.IUSHR : this.genShiftOp(Kind.Int, opcode); break; - case Bytecodes.IAND : - case Bytecodes.IOR : - case Bytecodes.IXOR : this.genLogicOp(Kind.Int, opcode); break; - case Bytecodes.LSHL : - case Bytecodes.LSHR : - case Bytecodes.LUSHR : this.genShiftOp(Kind.Long, opcode); break; - case Bytecodes.LAND : - case Bytecodes.LOR : - case Bytecodes.LXOR : this.genLogicOp(Kind.Long, opcode); break; - case Bytecodes.IINC : this.genIncrement(stream); break; - case Bytecodes.I2L : this.genConvert(Kind.Int, Kind.Long); break; - case Bytecodes.I2F : this.genConvert(Kind.Int, Kind.Float); break; - case Bytecodes.I2D : this.genConvert(Kind.Int, Kind.Double); break; - case Bytecodes.L2I : this.genConvert(Kind.Long, Kind.Int); break; - case Bytecodes.L2F : this.genConvert(Kind.Long, Kind.Float); break; - case Bytecodes.L2D : this.genConvert(Kind.Long, Kind.Double); break; - case Bytecodes.F2I : this.genConvert(Kind.Float, Kind.Int); break; - case Bytecodes.F2L : this.genConvert(Kind.Float, Kind.Long); break; - case Bytecodes.F2D : this.genConvert(Kind.Float, Kind.Double); break; - case Bytecodes.D2I : this.genConvert(Kind.Double, Kind.Int); break; - case Bytecodes.D2L : this.genConvert(Kind.Double, Kind.Long); break; - case Bytecodes.D2F : this.genConvert(Kind.Double, Kind.Float); break; - case Bytecodes.I2B : this.genConvert(Kind.Int, Kind.Byte); break; - case Bytecodes.I2C : this.genConvert(Kind.Int, Kind.Char); break; - case Bytecodes.I2S : this.genConvert(Kind.Int, Kind.Short); break; - - case Bytecodes.LCMP : this.genCompareOp(Kind.Long, false); break; - case Bytecodes.FCMPL : this.genCompareOp(Kind.Float, true); break; - case Bytecodes.FCMPG : this.genCompareOp(Kind.Float, false); break; - case Bytecodes.DCMPL : this.genCompareOp(Kind.Double, true); break; - case Bytecodes.DCMPG : this.genCompareOp(Kind.Double, false); break; - case Bytecodes.IFEQ : this.genIfZero(stream, Condition.EQ); break; - case Bytecodes.IFNE : this.genIfZero(stream, Condition.NE); break; - case Bytecodes.IFLT : this.genIfZero(stream, Condition.LT); break; - case Bytecodes.IFGE : this.genIfZero(stream, Condition.GE); break; - case Bytecodes.IFGT : this.genIfZero(stream, Condition.GT); break; - case Bytecodes.IFLE : this.genIfZero(stream, Condition.LE); break; - case Bytecodes.IF_ICMPEQ : this.genIfSame(stream, Kind.Int, Condition.EQ); break; - case Bytecodes.IF_ICMPNE : this.genIfSame(stream, Kind.Int, Condition.NE); break; - case Bytecodes.IF_ICMPLT : this.genIfSame(stream, Kind.Int, Condition.LT); break; - case Bytecodes.IF_ICMPGE : this.genIfSame(stream, Kind.Int, Condition.GE); break; - case Bytecodes.IF_ICMPGT : this.genIfSame(stream, Kind.Int, Condition.GT); break; - case Bytecodes.IF_ICMPLE : this.genIfSame(stream, Kind.Int, Condition.LE); break; - case Bytecodes.IF_ACMPEQ : this.genIfSame(stream, Kind.Reference, Condition.EQ); break; - case Bytecodes.IF_ACMPNE : this.genIfSame(stream, Kind.Reference, Condition.NE); break; - case Bytecodes.GOTO : this.genGoto(stream); break; - /* - case Bytecodes.JSR : genJsr(stream.readBranchDest()); break; - case Bytecodes.RET : genRet(stream.readLocalIndex()); break; - case Bytecodes.TABLESWITCH : genTableswitch(); break; - case Bytecodes.LOOKUPSWITCH : genLookupswitch(); break; - */ - case Bytecodes.IRETURN : this.genReturn(state.ipop()); break; - case Bytecodes.LRETURN : this.genReturn(state.lpop()); break; - case Bytecodes.FRETURN : this.genReturn(state.fpop()); break; - case Bytecodes.DRETURN : this.genReturn(state.dpop()); break; - case Bytecodes.ARETURN : this.genReturn(state.apop()); break; - case Bytecodes.RETURN : this.genReturn(null); break; - case Bytecodes.GETSTATIC : cpi = stream.readCPI(); this.genGetField(this.lookupField(cpi, opcode, true), true); break; - case Bytecodes.PUTSTATIC : cpi = stream.readCPI(); this.genPutField(this.lookupField(cpi, opcode, true), true); break; - case Bytecodes.GETFIELD : cpi = stream.readCPI(); this.genGetField(this.lookupField(cpi, opcode, false), false); break; - case Bytecodes.PUTFIELD : cpi = stream.readCPI(); this.genPutField(this.lookupField(cpi, opcode, false), false); break; - case Bytecodes.INVOKEVIRTUAL : cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, false), opcode, stream.currentBCI, stream.nextBCI); break; - case Bytecodes.INVOKESPECIAL : cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, false), opcode, stream.currentBCI, stream.nextBCI); break; - case Bytecodes.INVOKESTATIC : cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, true), opcode, stream.currentBCI, stream.nextBCI); break; - case Bytecodes.INVOKEINTERFACE: cpi = stream.readCPI(); this.genInvoke(this.lookupMethod(cpi, opcode, false), opcode, stream.currentBCI, stream.nextBCI); break; - case Bytecodes.NEW : this.genNewInstance(stream.readCPI()); break; - case Bytecodes.NEWARRAY : this.genNewTypeArray(stream.readLocalIndex()); break; - case Bytecodes.ANEWARRAY : this.genNewObjectArray(stream.readCPI()); break; - case Bytecodes.ARRAYLENGTH : this.genArrayLength(); break; - case Bytecodes.ATHROW : this.genThrow(stream.currentBCI); break; - case Bytecodes.CHECKCAST : this.genCheckCast(stream.readCPI()); break; - case Bytecodes.INSTANCEOF : this.genInstanceOf(stream.readCPI()); break; - /* - case Bytecodes.MONITORENTER : genMonitorEnter(state.apop()); break; - case Bytecodes.MONITOREXIT : genMonitorExit(state.apop()); break; - case Bytecodes.MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break; - */ - case Bytecodes.IFNULL : this.genIfNull(stream, Condition.EQ); break; - case Bytecodes.IFNONNULL : this.genIfNull(stream, Condition.NE); break; - /* - case Bytecodes.GOTO_W : genGoto(stream.readFarBranchDest()); break; - case Bytecodes.JSR_W : genJsr(stream.readFarBranchDest()); break; - case Bytecodes.BREAKPOINT: - throw new CiBailout("concurrent setting of breakpoint"); - default: - throw new CiBailout("Unsupported opcode " + opcode + " (" + nameOf(opcode) + ") [bci=" + bci + "]"); - } - */ - default: - throw new Error("Not Implemented " + Bytecodes[opcode]); - } - writer && writer.leave("State After: " + Bytecodes[opcode].padRight(" ", 12) + " " + state.toString()); - writer && writer.writeLn(""); - } - } - - export function optimizerCompileMethod(methodInfo: MethodInfo, target: CompilationTarget): CompiledMethodInfo { - if (!methodInfo.code) { - throw new Error("Method: " + methodInfo.implKey + " has no code."); - } - if (methodInfo.exception_table.length) { - throw new Error("Method: " + methodInfo.implKey + " has exception handlers."); - } - var builder = new Builder(methodInfo, target); - var fn; - var compilation = builder.build(); - var args = []; - for (var i = 0; i < builder.parameters.length; i++) { - var parameter = builder.parameters[i]; - args.push(parameter.name); - } - var body = compilation.body; - return new CompiledMethodInfo(args, body, builder.referencedClasses); - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jit/c4/ast.ts b/jit/c4/ast.ts deleted file mode 100644 index 5f370846..00000000 --- a/jit/c4/ast.ts +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Copyright 2014 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Like most JITs we don't need all the fancy AST serialization, this - * is a quick and dirty AST writer. - */ -module J2ME.C4.AST { - import notImplemented = Debug.notImplemented; - // The top part of this file is copied from escodegen. - - var json = false; - var escapeless = false; - var hexadecimal = false; - var renumber = false; - var quotes = "double"; - - var generateNumberCacheCount = 0; - var generateNumberCache = Object.create(null); - - function generateNumber(value) { - var result, point, temp, exponent, pos; - - if (value !== value) { - throw new Error('Numeric literal whose value is NaN'); - } - if (value < 0 || (value === 0 && 1 / value < 0)) { - throw new Error('Numeric literal whose value is negative'); - } - - if (value === 1 / 0) { - return json ? 'null' : renumber ? '1e400' : '1e+400'; - } - - result = generateNumberCache[value]; - if (result) { - return result; - } - if (generateNumberCacheCount === 1024) { - generateNumberCache = Object.create(null); - generateNumberCacheCount = 0; - } - result = '' + value; - if (!renumber || result.length < 3) { - generateNumberCache[value] = result; - generateNumberCacheCount ++; - return result; - } - - point = result.indexOf('.'); - if (!json && result.charAt(0) === '0' && point === 1) { - point = 0; - result = result.slice(1); - } - temp = result; - result = result.replace('e+', 'e'); - exponent = 0; - if ((pos = temp.indexOf('e')) > 0) { - exponent = +temp.slice(pos + 1); - temp = temp.slice(0, pos); - } - if (point >= 0) { - exponent -= temp.length - point - 1; - temp = +(temp.slice(0, point) + temp.slice(point + 1)) + ''; - } - pos = 0; - while (temp.charAt(temp.length + pos - 1) === '0') { - --pos; - } - if (pos !== 0) { - exponent -= pos; - temp = temp.slice(0, pos); - } - if (exponent !== 0) { - temp += 'e' + exponent; - } - if ((temp.length < result.length || - (hexadecimal && value > 1e12 && Math.floor(value) === value && (temp = '0x' + value.toString(16)).length < result.length)) && - +temp === value) { - result = temp; - } - generateNumberCache[value] = result; - generateNumberCacheCount ++; - return result; - } - - var Precedence = { - Default: 0, - Sequence: 0, - Assignment: 1, - Conditional: 2, - ArrowFunction: 2, - LogicalOR: 3, - LogicalAND: 4, - BitwiseOR: 5, - BitwiseXOR: 6, - BitwiseAND: 7, - Equality: 8, - Relational: 9, - BitwiseSHIFT: 10, - Additive: 11, - Multiplicative: 12, - Unary: 13, - Postfix: 14, - Call: 15, - New: 16, - Member: 17, - Primary: 18 - }; - - var BinaryPrecedence = { - '||': Precedence.LogicalOR, - '&&': Precedence.LogicalAND, - '|': Precedence.BitwiseOR, - '^': Precedence.BitwiseXOR, - '&': Precedence.BitwiseAND, - '==': Precedence.Equality, - '!=': Precedence.Equality, - '===': Precedence.Equality, - '!==': Precedence.Equality, - 'is': Precedence.Equality, - 'isnt': Precedence.Equality, - '<': Precedence.Relational, - '>': Precedence.Relational, - '<=': Precedence.Relational, - '>=': Precedence.Relational, - 'in': Precedence.Relational, - 'instanceof': Precedence.Relational, - '<<': Precedence.BitwiseSHIFT, - '>>': Precedence.BitwiseSHIFT, - '>>>': Precedence.BitwiseSHIFT, - '+': Precedence.Additive, - '-': Precedence.Additive, - '*': Precedence.Multiplicative, - '%': Precedence.Multiplicative, - '/': Precedence.Multiplicative - }; - - function toLiteralSource(value) { - if (value === null) { - return 'null'; - } - if (value === undefined) { - return 'undefined'; - } - if (typeof value === 'string') { - return escapeString(value); - } - if (typeof value === 'number') { - return generateNumber(value); - } - if (typeof value === 'boolean') { - return value ? 'true' : 'false'; - } - notImplemented(value); - } - - function nodesToSource(nodes: Node [], precedence: number, separator?: string) { - var result = ""; - for (var i = 0; i < nodes.length; i++) { - result += nodes[i].toSource(precedence); - if (separator && (i < nodes.length - 1)) { - result += separator; - } - } - return result; - } - - function alwaysParenthesize(text: string) { - return '(' + text + ')'; - } - - function parenthesize(text: string, current: number, should: number) { - if (current < should) { - return '(' + text + ')'; - } - return text; - } - - export class Node { - type: string; - - toSource(precedence: number) : string { - notImplemented(this.type); - return ""; - } - } - - export class Statement extends Node { - - } - - export class Expression extends Node { - - } - - export class Program extends Node { - constructor (public body: Node []) { - super(); - } - } - - export class EmptyStatement extends Statement { - - } - - export class BlockStatement extends Statement { - end: IR.Node; - constructor (public body: Statement []) { - super(); - } - toSource(precedence: number) : string { - return "{" + nodesToSource(this.body, precedence, "\n") + "}"; - } - } - - export class ExpressionStatement extends Statement { - constructor (public expression: Expression) { - super(); - } - toSource(precedence: number) : string { - return this.expression.toSource(Precedence.Sequence) + ";"; - } - } - - export class IfStatement extends Statement { - constructor (public test: Expression, public consequent: Statement, public alternate?: Statement) { - super(); - } - toSource(precedence: number) : string { - var result = "if(" + this.test.toSource(Precedence.Sequence) + "){" + this.consequent.toSource(Precedence.Sequence) + "}"; - if (this.alternate) { - result += "else{" + this.alternate.toSource(Precedence.Sequence) + "}"; - } - return result; - } - } - - export class LabeledStatement extends Statement { - constructor (public label: Identifier, public body: Statement) { - super(); - } - } - - export class BreakStatement extends Statement { - constructor (public label: Identifier) { - super(); - } - toSource(precedence: number) : string { - var result = "break"; - if (this.label) { - result += " " + this.label.toSource(Precedence.Default); - } - return result + ";"; - } - } - - export class ContinueStatement extends Statement { - constructor (public label: Identifier) { - super(); - } - toSource(precedence: number) : string { - var result = "continue"; - if (this.label) { - result += " " + this.label.toSource(Precedence.Default); - } - return result + ";"; - } - } - - export class WithStatement extends Statement { - constructor (public object: Expression, public body: Statement) { - super(); - } - } - - export class SwitchStatement extends Statement { - constructor (public discriminant: Expression, public cases: SwitchCase [], public lexical: boolean) { - super(); - } - toSource(precedence: number) : string { - return "switch(" + this.discriminant.toSource(Precedence.Sequence) + "){" + nodesToSource(this.cases, Precedence.Default, ";") + "};"; - } - } - - export class ReturnStatement extends Statement { - constructor (public argument: Expression) { - super(); - } - toSource(precedence: number) : string { - var result = "return"; - if (this.argument) { - result += " " + this.argument.toSource(Precedence.Sequence); - } - return result + ";"; - } - } - - export class ThrowStatement extends Statement { - constructor (public argument: Expression) { - super(); - } - toSource(precedence: number) : string { - return "throw " + this.argument.toSource(Precedence.Sequence) + ";\n"; - } - } - - export class TryStatement extends Statement { - constructor (public block: BlockStatement, public handlers: CatchClause, public guardedHandlers: CatchClause [], public finalizer: BlockStatement) { - super(); - } - toSource(precedence: number) : string { - if (this.guardedHandlers.length !== 0 || this.finalizer !== null) { - throw "TODO"; - } - return "try " + this.block.toSource(Precedence.Sequence) + " catch(" + this.handlers.param.toSource(Precedence.Sequence) + ") " + this.handlers.body.toSource(Precedence.Sequence); - } - } - - export class WhileStatement extends Statement { - constructor (public test: Expression, public body: Statement) { - super(); - } - toSource(precedence: number) : string { - return "while(" + this.test.toSource(Precedence.Sequence) + "){" + this.body.toSource(Precedence.Sequence) + "}"; - } - } - - export class DoWhileStatement extends Statement { - constructor (public body: Statement, public test: Expression) { - super(); - } - } - - export class ForStatement extends Statement { - constructor (public init: Node, public test: Expression, public update: Expression, public body: Statement) { - super(); - } - } - - export class ForInStatement extends Statement { - constructor (public left: Node, public right: Expression, public body: Statement, public each: boolean) { - super(); - } - } - - export class DebuggerStatement extends Statement { - } - - export class Declaration extends Statement { - } - - export class FunctionDeclaration extends Declaration { - constructor (public id: Identifier, public params: Node[], public defaults: Expression[], public rest: Identifier, public body: BlockStatement, public generator: boolean, public expression: boolean) { - super(); - } - } - - export class VariableDeclaration extends Declaration { - constructor (public declarations: VariableDeclarator[], public kind: string) { - super(); - } - toSource(precedence: number) : string { - return this.kind + " " + nodesToSource(this.declarations, precedence, ",") + ";\n"; - } - } - - export class VariableDeclarator extends Node { - constructor (public id: Node, public init?: Node) { - super(); - } - toSource(precedence: number) : string { - var result = this.id.toSource(Precedence.Assignment); - if (this.init) { - result += "=" + this.init.toSource(Precedence.Assignment); - } - return result; - } - } - - export class Identifier extends Expression { - constructor (public name: string) { - super(); - } - toSource(precedence: number) : string { - return this.name; - } - } - - export class Literal extends Expression { - constructor (public value: any) { - super(); - } - toSource(precedence: number) : string { - return toLiteralSource(this.value); - } - } - - export class ThisExpression extends Expression { - toSource(precedence: number) : string { - return "this"; - } - } - - export class ArrayExpression extends Expression { - constructor (public elements: Expression []) { - super(); - } - toSource(precedence: number) : string { - return "[" + nodesToSource(this.elements, Precedence.Assignment, ",") + "]"; - } - } - - export class ObjectExpression extends Expression { - constructor (public properties: Property []) { - super(); - } - toSource(precedence: number) : string { - return "{" + nodesToSource(this.properties, Precedence.Sequence, ",") + "}"; - } - } - - export class FunctionExpression extends Expression { - constructor (public id: Identifier, public params: Node[], public defaults: Expression [], public rest: Identifier, public body: BlockStatement, public generator: boolean, public expression: boolean) { - super(); - } - } - - export class SequenceExpression extends Expression { - constructor (public expressions: Expression []) { - super(); - } - toSource(precedence: number) : string { - return "(" + this.expressions.map(x => x.toSource(precedence)).join(", ") + ")"; - } - } - - export class UnaryExpression extends Expression { - constructor (public operator: string, public prefix: boolean, public argument: Expression) { - super(); - } - toSource(precedence: number) : string { - var argument = this.argument.toSource(Precedence.Unary); - var result = this.prefix ? this.operator + argument : argument + this.operator; - result = " " + result; - result = parenthesize(result, Precedence.Unary, precedence); - return result; - } - } - - export class BinaryExpression extends Expression { - constructor (public operator: string, public left: Expression, public right: Expression) { - super(); - } - toSource(precedence: number) : string { - var currentPrecedence = BinaryPrecedence[this.operator]; - var result = this.left.toSource(currentPrecedence) + " " + this.operator + " " + this.right.toSource(currentPrecedence + 1); - return parenthesize(result, currentPrecedence, precedence); - } - } - - export class AssignmentExpression extends Expression { - constructor (public operator: string, public left: Expression, public right: Expression) { - super(); - } - toSource(precedence: number) : string { - var result = this.left.toSource(Precedence.Assignment) + this.operator + this.right.toSource(Precedence.Assignment); - return parenthesize(result, Precedence.Assignment, precedence); - } - } - - export class UpdateExpression extends Expression { - constructor (public operator: string, public argument: Expression, public prefix: boolean) { - super(); - } - } - - export class LogicalExpression extends BinaryExpression { - constructor (operator: string, left: Expression, right: Expression) { - super(operator, left, right); - } - } - - export class ConditionalExpression extends Expression { - constructor (public test: Expression, public consequent: Expression, public alternate: Expression) { - super(); - } - toSource(precedence: number) : string { - return this.test.toSource(Precedence.LogicalOR) + "?" + this.consequent.toSource(Precedence.Assignment) + ":" + this.alternate.toSource(Precedence.Assignment); - } - } - - export class NewExpression extends Expression { - arguments: Expression []; - constructor (public callee: Expression, _arguments: Expression []) { - super(); - this.arguments = _arguments; - } - toSource(precedence: number) : string { - return "new " + this.callee.toSource(precedence) + "(" + nodesToSource(this.arguments, precedence, ",") + ")"; - } - } - - export class CallExpression extends Expression { - arguments: Expression []; - constructor (public callee: Expression, _arguments: Expression []) { - super(); - this.arguments = _arguments; - } - toSource(precedence: number) : string { - return this.callee.toSource(precedence) + "(" + nodesToSource(this.arguments, precedence, ",") + ")"; - } - } - - export class MemberExpression extends Expression { - constructor (public object: Expression, public property: Node, public computed: boolean) { - super(); - } - toSource(precedence: number) : string { - var result = this.object.toSource(Precedence.Call); - if (this.object instanceof Literal) { - result = alwaysParenthesize(result); - } - var property = this.property.toSource(Precedence.Sequence); - if (this.computed) { - result += "[" + property + "]"; - } else { - result += "." + property; - } - return parenthesize(result, Precedence.Member, precedence); - } - } - - export class Property extends Node { - constructor (public key: Node, public value: Expression, public kind: string) { - super(); - } - toSource(precedence: number) : string { - return this.key.toSource(precedence) + ":" + this.value.toSource(precedence); - } - } - - export class SwitchCase extends Node { - constructor (public test: Expression, public consequent: Statement []) { - super(); - } - toSource(precedence: number) : string { - var result = this.test ? "case " + this.test.toSource(precedence) : "default"; - return result + ": " + nodesToSource(this.consequent, precedence, ";"); - } - } - - export class CatchClause extends Node { - constructor (public param: Node, public guard: Expression, public body: BlockStatement) { - super(); - } - } - - Node.prototype.type = "Node"; - Program.prototype.type = "Program"; - Statement.prototype.type = "Statement"; - EmptyStatement.prototype.type = "EmptyStatement"; - BlockStatement.prototype.type = "BlockStatement"; - ExpressionStatement.prototype.type = "ExpressionStatement"; - IfStatement.prototype.type = "IfStatement"; - LabeledStatement.prototype.type = "LabeledStatement"; - BreakStatement.prototype.type = "BreakStatement"; - ContinueStatement.prototype.type = "ContinueStatement"; - WithStatement.prototype.type = "WithStatement"; - SwitchStatement.prototype.type = "SwitchStatement"; - ReturnStatement.prototype.type = "ReturnStatement"; - ThrowStatement.prototype.type = "ThrowStatement"; - TryStatement.prototype.type = "TryStatement"; - WhileStatement.prototype.type = "WhileStatement"; - DoWhileStatement.prototype.type = "DoWhileStatement"; - ForStatement.prototype.type = "ForStatement"; - ForInStatement.prototype.type = "ForInStatement"; - DebuggerStatement.prototype.type = "DebuggerStatement"; - Declaration.prototype.type = "Declaration"; - FunctionDeclaration.prototype.type = "FunctionDeclaration"; - VariableDeclaration.prototype.type = "VariableDeclaration"; - VariableDeclarator.prototype.type = "VariableDeclarator"; - Expression.prototype.type = "Expression"; - Identifier.prototype.type = "Identifier"; - Literal.prototype.type = "Literal"; - ThisExpression.prototype.type = "ThisExpression"; - ArrayExpression.prototype.type = "ArrayExpression"; - ObjectExpression.prototype.type = "ObjectExpression"; - FunctionExpression.prototype.type = "FunctionExpression"; - SequenceExpression.prototype.type = "SequenceExpression"; - UnaryExpression.prototype.type = "UnaryExpression"; - BinaryExpression.prototype.type = "BinaryExpression"; - AssignmentExpression.prototype.type = "AssignmentExpression"; - UpdateExpression.prototype.type = "UpdateExpression"; - LogicalExpression.prototype.type = "LogicalExpression"; - ConditionalExpression.prototype.type = "ConditionalExpression"; - NewExpression.prototype.type = "NewExpression"; - CallExpression.prototype.type = "CallExpression"; - MemberExpression.prototype.type = "MemberExpression"; - Property.prototype.type = "Property"; - SwitchCase.prototype.type = "SwitchCase"; - CatchClause.prototype.type = "CatchClause"; -} diff --git a/jit/c4/backend.ts b/jit/c4/backend.ts deleted file mode 100644 index 2d1eab6a..00000000 --- a/jit/c4/backend.ts +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright 2013 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module J2ME.C4.Backend { - import assert = Debug.assert; - import unexpected = Debug.unexpected; - import notImplemented = Debug.notImplemented; - import pushUnique = ArrayUtilities.pushUnique; - - import Literal = AST.Literal; - import Identifier = AST.Identifier; - import VariableDeclaration = AST.VariableDeclaration; - import VariableDeclarator = AST.VariableDeclarator; - import MemberExpression = AST.MemberExpression; - import BinaryExpression = AST.BinaryExpression; - import CallExpression = AST.CallExpression; - import AssignmentExpression = AST.AssignmentExpression; - import ExpressionStatement = AST.ExpressionStatement; - import ReturnStatement = AST.ReturnStatement; - import FunctionDeclaration = AST.FunctionDeclaration; - import ConditionalExpression = AST.ConditionalExpression; - import ObjectExpression = AST.ObjectExpression; - import ArrayExpression = AST.ArrayExpression; - import UnaryExpression = AST.UnaryExpression; - import NewExpression = AST.NewExpression; - import Property = AST.Property; - import BlockStatement = AST.BlockStatement; - import ThisExpression = AST.ThisExpression; - import ThrowStatement = AST.ThrowStatement; - import IfStatement = AST.IfStatement; - import WhileStatement = AST.WhileStatement; - import BreakStatement = AST.BreakStatement; - import ContinueStatement = AST.ContinueStatement; - import SwitchStatement = AST.SwitchStatement; - import SwitchCase = AST.SwitchCase; - - import Start = IR.Start; - import Block = IR.Block; - import Variable = IR.Variable; - import Constant = IR.Constant; - import Operator = IR.Operator; - import Projection = IR.Projection; - - var Control = Looper.Control; - - import ControlNode = Looper.Control.ControlNode; - import last = ArrayUtilities.last; - - Control.Break.prototype.compile = function (cx: Context): AST.Node { - return cx.compileBreak(this); - }; - - Control.Continue.prototype.compile = function (cx: Context): AST.Node { - return cx.compileContinue(this); - }; - - Control.Exit.prototype.compile = function (cx: Context): AST.Node { - return cx.compileExit(this); - }; - - Control.LabelSwitch.prototype.compile = function (cx: Context): AST.Node { - return cx.compileLabelSwitch(this); - }; - - Control.Seq.prototype.compile = function (cx: Context): AST.Node { - return cx.compileSequence(this); - }; - - Control.Loop.prototype.compile = function (cx: Context): AST.Node { - return cx.compileLoop(this); - }; - - Control.Switch.prototype.compile = function (cx: Context): AST.Node { - return cx.compileSwitch(this); - }; - - Control.If.prototype.compile = function (cx: Context): AST.Node { - return cx.compileIf(this); - }; - - Control.Try.prototype.compile = function (cx: Context): AST.Node { - notImplemented("try"); - return null; - }; - - var F = new Identifier("$F"); - var C = new Identifier("$C"); - - function isLazyConstant(value) { - return false; - } - - export function constant(value, cx?: Context): AST.Node { - if (typeof value === "string" || value === null || value === true || value === false) { - return new Literal(value); - } else if (value === undefined) { - return new Identifier("undefined"); - } else if (typeof value === "object" || typeof value === "function") { - if (isLazyConstant(value)) { - return call(property(F, "C"), [new Literal(cx.useConstant(value))]); - } else { - return new MemberExpression(C, new Literal(cx.useConstant(value)), true); - } - } else if (typeof value === "number" && isNaN(value)) { - return new Identifier("NaN"); - } else if (value === Infinity) { - return new Identifier("Infinity"); - } else if (value === -Infinity) { - return new UnaryExpression("-", true, new Identifier("Infinity")); - } else if (typeof value === "number" && (1 / value) < 0) { - return new UnaryExpression("-", true, new Literal(Math.abs(value))); - } else if (typeof value === "number") { - return new Literal(value); - } else { - unexpected("Cannot emit constant for value: " + value); - } - } - - export function id(name) { - release || assert (typeof name === "string"); - return new Identifier(name); - } - - export function property(obj, ...args) { - for (var i = 0; i < args.length; i++) { - var x = args[i]; - if (typeof x === "string") { - if (isIdentifierName(x)) { - obj = new MemberExpression(obj, new Identifier(x), false); - } else { - obj = new MemberExpression(obj, new Literal(x), true); - } - } else if (x instanceof Literal && isIdentifierName(x.value)) { - obj = new MemberExpression(obj, new Identifier(x.value), false); - } else { - obj = new MemberExpression(obj, x, true); - } - } - return obj; - } - - export function call(callee, args): CallExpression { - release || assert(args instanceof Array); - release || args.forEach(function (x) { - release || assert(!(x instanceof Array)); - release || assert(x !== undefined); - }); - return new CallExpression(callee, args); - } - - function callAsCall(callee, object, args) { - return call(property(callee, "asCall"), [object].concat(args)); - } - - export function callCall(callee, object, args) { - return call(property(callee, "call"), [object].concat(args)); - } - - export function assignment(left, right) { - release || assert(left && right); - return new AssignmentExpression("=", left, right); - } - - function variableDeclaration(declarations) { - return new VariableDeclaration(declarations, "var"); - } - - function negate(node) { - if (node instanceof Constant) { - if (node.value === true || node.value === false) { - return constant(!node.value); - } - } else if (node instanceof Identifier) { - return new UnaryExpression(Operator.FALSE.name, true, node); - } - release || assert(node instanceof BinaryExpression || node instanceof UnaryExpression, node); - var left = node instanceof BinaryExpression ? node.left : node.argument; - var right = node.right; - var operator = Operator.fromName(node.operator); - if (operator === Operator.EQ && right instanceof Literal && right.value === false) { - return left; - } - if (operator === Operator.FALSE) { - return left; - } - if (operator.not) { - if (node instanceof BinaryExpression) { - return new BinaryExpression(operator.not.name, left, right); - } else { - return new UnaryExpression(operator.not.name, true, left); - } - } - return new UnaryExpression(Operator.FALSE.name, true, node); - } - - export class Context { - label = new Variable("$L"); - variables = []; - constants = []; - parameters = []; - - useConstant(constant: IR.Constant): number { - return pushUnique(this.constants, constant); - } - - useVariable(variable: IR.Variable) { - release || assert (variable); - return pushUnique(this.variables, variable); - } - - useParameter(parameter: IR.Parameter) { - return this.parameters[parameter.index] = parameter; - } - - compileLabelBody(node) { - var body = []; - if (node.label !== undefined) { - this.useVariable(this.label); - body.push(new ExpressionStatement(assignment(id(this.label.name), new Literal(node.label)))); - } - return body; - } - - compileBreak(node) { - var body = this.compileLabelBody(node); - body.push(new BreakStatement(null)); - return new BlockStatement(body); - } - - compileContinue(node) { - var body = this.compileLabelBody(node); - body.push(new ContinueStatement(null)); - return new BlockStatement(body); - } - - compileExit(node) { - return new BlockStatement(this.compileLabelBody(node)); - } - - compileIf(node) { - var cr = node.cond.compile(this); - var tr = null, er = null; - if (node.then) { - tr = node.then.compile(this); - } - if (node.else) { - er = node.else.compile(this); - } - var condition = compileValue(cr.end.predicate, this); - condition = node.negated ? negate(condition) : condition; - cr.body.push(new IfStatement(condition, tr || new BlockStatement([]), er || null)); - return cr; - } - - compileSwitch(node) { - var dr = node.determinant.compile(this); - var cases = []; - node.cases.forEach(function (x) { - var br; - if (x.body) { - br = x.body.compile(this); - } - var test = typeof x.index === "number" ? new Literal(x.index) : undefined; - cases.push(new SwitchCase(test, br ? [br] : [])); - }, this); - var determinant = compileValue(dr.end.determinant, this); - dr.body.push(new SwitchStatement(determinant, cases, false)) - return dr; - } - - compileLabelSwitch(node) { - var statement = null; - var labelName = id(this.label.name); - - function compileLabelTest(labelID) { - release || assert(typeof labelID === "number"); - return new BinaryExpression("===", labelName, new Literal(labelID)); - } - - for (var i = node.cases.length - 1; i >= 0; i--) { - var c = node.cases[i]; - var labels = c.labels; - - var labelTest = compileLabelTest(labels[0]); - - for (var j = 1; j < labels.length; j++) { - labelTest = new BinaryExpression("||", labelTest, compileLabelTest(labels[j])); - } - - statement = new IfStatement( - labelTest, - c.body ? c.body.compile(this) : new BlockStatement([]), - statement); - } - return statement; - } - - compileLoop(node) { - var br = node.body.compile(this); - return new WhileStatement(constant(true), br); - } - - compileSequence(node) { - var cx = this; - var body = []; - node.body.forEach(function (x) { - var result = x.compile(cx); - if (result instanceof BlockStatement) { - body = body.concat(result.body); - } else { - body.push(result); - } - }); - return new BlockStatement(body); - } - - compileBlock(block) { - var body = []; - /* - for (var i = 1; i < block.nodes.length - 1; i++) { - print("Block[" + i + "]: " + block.nodes[i]); - } - */ - for (var i = 1; i < block.nodes.length - 1; i++) { - var node = block.nodes[i]; - var statement; - var to; - var from; - - if (node instanceof IR.Throw) { - statement = compileValue(node, this, true); - } else { - if (node instanceof IR.Move) { - to = id(node.to.name); - this.useVariable(node.to); - from = compileValue(node.from, this); - } else { - from = compileValue(node, this, true); - if (from instanceof AST.Statement) { - body.push(from); - continue; - } else { - if (node.variable) { - to = id(node.variable.name); - this.useVariable(node.variable); - } else { - to = null; - } - } - } - if (to) { - statement = new ExpressionStatement(assignment(to, from)); - } else { - statement = new ExpressionStatement(from); - } - } - body.push(statement); - } - var end = last(block.nodes); - if (end instanceof IR.Stop) { - body.push(new ReturnStatement(compileValue(end.argument, this))); - } - var result = new BlockStatement(body); - result.end = last(block.nodes); - release || assert (result.end instanceof IR.End); - // print("Block: " + block + " -> " + generateSource(result)); - return result; - } - } - - export function compileValue(value, cx: Context, noVariable?) { - release || assert (value); - release || assert (value.compile, "Implement |compile| for " + value + " (" + value.nodeName + ")"); - release || assert (cx instanceof Context); - release || assert (!isArray(value)); - if (noVariable || !value.variable) { - var node = value.compile(cx); - return node; - } - release || assert (value.variable, "Value has no variable: " + value); - return id(value.variable.name); - } - - function isArray(array) { - return array instanceof Array; - } - - export function compileValues(values, cx: Context) { - release || assert (isArray(values)); - return values.map(function (value) { - return compileValue(value, cx); - }); - } - - IR.Parameter.prototype.compile = function (cx: Context): AST.Node { - cx.useParameter(this); - return id(this.name); - }; - - IR.Constant.prototype.compile = function (cx: Context): AST.Node { - return constant(this.value, cx); - }; - - IR.Variable.prototype.compile = function (cx: Context): AST.Node { - return id(this.name); - }; - - IR.Phi.prototype.compile = function (cx: Context): AST.Node { - release || assert (this.variable); - return compileValue(this.variable, cx); - }; - - IR.Latch.prototype.compile = function (cx: Context): AST.Node { - return new ConditionalExpression ( - compileValue(this.condition, cx), - compileValue(this.left, cx), - compileValue(this.right, cx) - ); - }; - - IR.Unary.prototype.compile = function (cx: Context): AST.Node { - var result = new UnaryExpression ( - this.operator.name, - true, - compileValue(this.argument, cx) - ); - if (this.operator === Operator.INEG) { - return new BinaryExpression("|", result, constant(0)); - } - // Float and double don't need conversion. - return result; - }; - - IR.Copy.prototype.compile = function (cx: Context): AST.Node { - return compileValue(this.argument, cx); - }; - - IR.Binary.prototype.compile = function (cx: Context): AST.Expression { - var left = compileValue(this.left, cx); - var right = compileValue(this.right, cx); - var result = new BinaryExpression (this.operator.name, left, right); - if (this.operator === Operator.IADD || - this.operator === Operator.ISUB || - this.operator === Operator.IMUL || - this.operator === Operator.IDIV || - this.operator === Operator.IREM) { - return new BinaryExpression("|", result, constant(0)); - } else if (this.operator === Operator.FADD || - this.operator === Operator.FSUB || - this.operator === Operator.FMUL || - this.operator === Operator.FDIV || - this.operator === Operator.FREM) { - return call(id("Math.fround"), [result]); - } else if (this.operator === Operator.DADD || - this.operator === Operator.DSUB || - this.operator === Operator.DMUL || - this.operator === Operator.DDIV || - this.operator === Operator.DREM) { - return new UnaryExpression("+", true, result); - } - return result; - }; - - IR.CallProperty.prototype.compile = function (cx: Context): AST.Node { - var object = compileValue(this.object, cx); - var name = compileValue(this.name, cx); - var callee = property(object, name); - var args = this.args.map(function (arg) { - return compileValue(arg, cx); - }); - return call(callee, args); - }; - - IR.Call.prototype.compile = function (cx: Context): AST.Node { - var args = this.args.map(function (arg) { - return compileValue(arg, cx); - }); - var callee = compileValue(this.callee, cx); - var object; - if (this.object) { - object = compileValue(this.object, cx); - } else { - object = new Literal(null); - } - return callCall(callee, object, args); - }; - - IR.This.prototype.compile = function (cx: Context): AST.Node { - return new ThisExpression(); - }; - - IR.Throw.prototype.compile = function (cx: Context): AST.Node { - var argument = compileValue(this.argument, cx); - return new ThrowStatement(argument); - }; - - IR.Arguments.prototype.compile = function (cx: Context): AST.Node { - return id("arguments"); - }; - - IR.GlobalProperty.prototype.compile = function (cx: Context): AST.Node { - return id(this.name); - }; - - IR.GetProperty.prototype.compile = function (cx: Context): AST.Node { - var object = compileValue(this.object, cx); - var name = compileValue(this.name, cx); - return property(object, name); - }; - - IR.SetProperty.prototype.compile = function (cx: Context): AST.Node { - var object = compileValue(this.object, cx); - var name = compileValue(this.name, cx); - var value = compileValue(this.value, cx); - return assignment(property(object, name), value); - }; - - IR.Projection.prototype.compile = function (cx: Context): AST.Node { - release || assert (this.type === IR.ProjectionType.CONTEXT); - release || assert (this.argument instanceof Start); - return compileValue(this.argument.scope, cx); - }; - - IR.NewArray.prototype.compile = function (cx: Context): AST.Node { - return new ArrayExpression(compileValues(this.elements, cx)); - }; - - IR.NewObject.prototype.compile = function (cx: Context): AST.Node { - var properties = this.properties.map(function (property) { - var key = compileValue(property.key, cx); - var value = compileValue(property.value, cx); - return new Property(key, value, "init"); - }); - return new ObjectExpression(properties); - }; - - IR.Block.prototype.compile = function (cx: Context): AST.Node { - return cx.compileBlock(this); - }; - - function generateSource(node) { - return node.toSource(); - } - - export class Compilation { - static id: number = 0; - constructor(public parameters: string [], - public body: string, - public constants: any []) { - // ... - } - - /** - * Object references are stored on the compilation object in a property called |constants|. Some of - * these constants are |LazyInitializer|s and the backend makes sure to emit a call to a function - * named |C| that resolves them. - */ - public C(index: number) { - var value = this.constants[index]; - // TODO: Avoid using |instanceof| here since this can be called quite frequently. - if (value._isLazyInitializer) { - this.constants[index] = value.resolve(); - } - return this.constants[index]; - } - } - - export function generate(cfg): Compilation { - enterTimeline("Looper"); - var root = Looper.analyze(cfg); - leaveTimeline(); - - var writer = new IndentingWriter(); - - var cx = new Context(); - enterTimeline("Construct AST"); - var code = root.compile(cx); - leaveTimeline(); - - var parameters = []; - for (var i = 0; i < cx.parameters.length; i++) { - // Closure Compiler complains if the parameter names are the same even if they are not used, - // so we differentiate them here. - var name = cx.parameters[i] ? cx.parameters[i].name : "_" + i; - parameters.push(id(name)); - } - var compilationId = Compilation.id ++; - var compilationGlobalPropertyName = "$$F" + compilationId; - if (cx.constants.length) { - var compilation = new Identifier(compilationGlobalPropertyName); - var constants = new MemberExpression(compilation, new Identifier("constants"), false); - code.body.unshift(variableDeclaration([ - new VariableDeclarator(id("$F"), compilation), - new VariableDeclarator(id("$C"), constants) - ])); - } - if (cx.variables.length) { - countTimeline("Backend: Locals", cx.variables.length); - var variables = variableDeclaration(cx.variables.map(function (variable) { - return new VariableDeclarator(id(variable.name)); - })); - code.body.unshift(variables); - } - - enterTimeline("Serialize AST"); - var source = generateSource(code); - leaveTimeline(); - // Save compilation as a globa property name. - return jsGlobal[compilationGlobalPropertyName] = new Compilation ( - parameters.map(function (p) { return p.name; }), - source, - cx.constants - ); - } -} diff --git a/jit/c4/ir.ts b/jit/c4/ir.ts deleted file mode 100644 index 214e891c..00000000 --- a/jit/c4/ir.ts +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright 2014 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * SSA-based Sea-of-Nodes IR based on Cliff Click's Work: A simple graph-based intermediate - * representation (http://doi.acm.org/10.1145/202530.202534) - * - * Node Hierarchy: - * - * Node - * - Control - * - Region - * - Start - * - End - * - Stop - * - If - * - Jump - * - Value - * - Constant, Parameter, Phi, Binary, GetProperty ... - * - * Control flow is modeled with control edges rather than with CFGs. Each basic block is represented - * as a region node which has control dependencies on predecessor regions. Nodes that are dependent - * on the execution of a region, have a |control| property assigned to the region they belong to. - * - * Memory (and the external world) is modeled as an SSA value called the Store. Nodes that mutate the - * Store produce a new Store. - * - * Nodes that produce multiple values, such as Ifs which produce two values (a True and False control - * value) can have their values projected (extracted) using Projection nodes. - * - * A node scheduler is responsible for serializing nodes back into a CFG such that all dependencies - * are satisfied. - * - * Compiler Pipeline: - * - * Graph Builder -> IR (DFG) -> Optimizations -> CFG -> Restructuring -> Backend - * - */ - -module J2ME.C4.IR { - import assert = Debug.assert; - import unexpected = Debug.unexpected; - import createEmptyObject = ObjectUtilities.createEmptyObject; - - // TODO is there a better way to deal with Math.fround - declare var Math: any; - - export interface NodeVisitor { - (node: Node): void; - } - - export interface BlockVisitor { - (block: Block): void; - } - - export function visitArrayInputs(array: Node [], visitor: NodeVisitor, ignoreNull: boolean = false) { - for (var i = 0; i < array.length; i++) { - if (ignoreNull && array[i] === null) { - continue; - } - visitor(array[i]); - } - } - - export enum NodeFlags { - None - } - - export class Node { - abstract: boolean; // TODO: No idea what this is for. - kind: Kind; - isDeleted: boolean; - - private static _nextID: number [] = []; - - static getNextID(): number { - return Node._nextID[Node._nextID.length - 1] += 1 - } - - id: number; - control: Control; - nodeName: string; - variable: Variable; - - // TODO: Remove all these. - mustFloat: boolean; - mustNotFloat: boolean; - shouldFloat: boolean; - shouldNotFloat: boolean; - handlesAssignment: boolean; - - constructor() { - this.id = Node.getNextID(); - } - - compile: (cx) => void; - - visitInputs(visitor: NodeVisitor) { - - } - - static startNumbering() { - Node._nextID.push(0); - } - - static stopNumbering() { - Node._nextID.pop(); - } - - toString(brief?: boolean) { - if (brief) { - return nameOf(this); - } - var inputs = []; - this.visitInputs(function (input) { - inputs.push(nameOf(input)); - }); - var result = nameOf(this) + " = " + this.nodeName.toUpperCase(); - if (inputs.length) { - result += " " + inputs.join(", "); - } - return result; - } - - visitInputsNoConstants(visitor: NodeVisitor) { - this.visitInputs(function (node) { - if (isConstant(node)) { - return; - } - visitor(node); - }); - } - - replaceInput(oldInput: Node, newInput: Node) { - var count = 0; - for (var k in this) { - var v = this[k]; - if (v instanceof Node) { - if (v === oldInput) { - this[k] = newInput; - count ++; - } - } - if (v instanceof Array) { - count += (v).replace(oldInput, newInput); - } - } - return count; - } - } - - Node.prototype.nodeName = "Node"; - - export class Control extends Node { - block: Block; - constructor() { - super(); - } - } - Control.prototype.nodeName = "Control"; - - export class Region extends Control { - entryState: any; - predecessors: Control []; - constructor(control: Control) { - super(); - this.predecessors = control ? [control] : []; - } - visitInputs(visitor: NodeVisitor) { - visitArrayInputs(this.predecessors, visitor); - } - } - Region.prototype.nodeName = "Region"; - - export class Start extends Region { - constructor() { - super(null); - this.control = this; - } - visitInputs(visitor: NodeVisitor) { - visitArrayInputs(this.predecessors, visitor); - } - } - Start.prototype.nodeName = "Start"; - - export class End extends Control { - constructor(public control: Control) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - } - } - End.prototype.nodeName = "End"; - - export class Stop extends End { - constructor(control: Control, public store: Store, public argument: Value) { - super(control); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - visitor(this.store); - visitor(this.argument); - } - } - Stop.prototype.nodeName = "Stop"; - - export class If extends End { - constructor(control: Control, public predicate: Value) { - super(control); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - visitor(this.predicate); - } - } - If.prototype.nodeName = "If"; - - export class Switch extends End { - constructor(control, public determinant: Value) { - super(control); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - visitor(this.determinant); - } - } - Switch.prototype.nodeName = "Switch"; - - export class Jump extends End { - constructor(control) { - super(control); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - } - } - Jump.prototype.nodeName = "Jump"; - - export class Value extends Node { - constructor() { - super(); - } - } - Value.prototype.nodeName = "Value"; - - export class Store extends Value { - constructor() { - super(); - } - } - Store.prototype.nodeName = "Store"; - - export class StoreDependent extends Value { - public loads: Node []; - constructor(public control: Control, public store: Store) { - super(); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - } - } - - StoreDependent.prototype.nodeName = "StoreDependent"; - - export class Call extends StoreDependent { - constructor(control: Control, store: Store, public callee: Value, public object: Value, public args: Value []) { - super(control, store); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - visitor(this.callee); - this.object && visitor(this.object); - visitArrayInputs(this.args, visitor); - } - } - - Call.prototype.nodeName = "Call"; - - export class New extends StoreDependent { - constructor(control: Control, store: Store, public callee: Value, public args: Value []) { - super(control, store); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - visitor(this.callee); - visitArrayInputs(this.args, visitor); - } - } - - New.prototype.nodeName = "New"; - - export class GetProperty extends StoreDependent { - constructor(control: Control, store: Store, public object: Value, public name: Value) { - super(control, store); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - visitor(this.object); - visitor(this.name); - } - } - - GetProperty.prototype.nodeName = "GetProperty"; - - export class SetProperty extends StoreDependent { - constructor(control: Control, store: Store, public object: Value, public name: Value, public value: Value) { - super(control, store); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - visitor(this.object); - visitor(this.name); - visitor(this.value); - } - } - - SetProperty.prototype.nodeName = "SetProperty"; - - export class DeleteProperty extends StoreDependent { - constructor(control, store, public object: Value, public name: Value) { - super(control, store); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - visitor(this.object); - visitor(this.name); - } - } - - DeleteProperty.prototype.nodeName = "DeleteProperty"; - - export class CallProperty extends StoreDependent { - constructor(control: Control, store: Store, public object: Value, public name: Value, public args: Value []) { - super(control, store); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - this.store && visitor(this.store); - this.loads && visitArrayInputs(this.loads, visitor); - visitor(this.object); - visitor(this.name); - visitArrayInputs(this.args, visitor); - } - } - - CallProperty.prototype.nodeName = "CallProperty"; - - export class Phi extends Value { - isLoop: boolean; - sealed: boolean; - args: Value []; - constructor(public control: Control, value: Value) { - super(); - this.control = control; - this.args = value ? [value] : []; - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - visitArrayInputs(this.args, visitor); - } - seal() { - this.sealed = true; - } - pushValue(x: Value) { - release || assert (!this.sealed); - this.args.push(x); - } - } - - Phi.prototype.nodeName = "Phi"; - - export class Variable extends Value { - constructor(public name: string) { - super(); - } - } - - Variable.prototype.nodeName = "Variable"; - - export class Copy extends Value { - constructor(public argument: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.argument); - } - } - - Copy.prototype.nodeName = "Copy"; - - export class Move extends Value { - constructor(public to: Variable, public from: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.to); - visitor(this.from); - } - } - - Move.prototype.nodeName = "Move"; - - export enum ProjectionType { - CASE, - TRUE, - FALSE, - STORE, - CONTEXT - } - - export class Projection extends Value { - constructor(public argument: Node, public type: ProjectionType, public selector?: Constant) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.argument); - } - project(): Node { - return this.argument; - } - } - - Projection.prototype.nodeName = "Projection"; - - export class Latch extends Value { - constructor(public control: Control, public condition: Value, public left: Value, public right: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - this.control && visitor(this.control); - visitor(this.condition); - visitor(this.left); - visitor(this.right); - } - } - - Latch.prototype.nodeName = "Latch"; - - export class Operator { - not: Operator; - static byName: Map = createEmptyObject(); - - constructor(public name: string, public evaluate: Function, public isBinary: boolean) { - Operator.byName[name] = this; - } - - static IADD = new Operator("+", (l, r) => (l + r) | 0, true); - static LADD = new Operator("+", (l, r) => l.add(r), true); - static FADD = new Operator("+", (l, r) => Math.fround(l + r), true); - static DADD = new Operator("+", (l, r) => +(l + r), true); - - static ISUB = new Operator("-", (l, r) => (l - r) | 0, true); - static LSUB = new Operator("-", (l, r) => l.subtract(r), true); - static FSUB = new Operator("-", (l, r) => Math.fround(l - r), true); - static DSUB = new Operator("-", (l, r) => +(l - r), true); - - static IMUL = new Operator("*", (l, r) => (l * r) | 0, true); - static LMUL = new Operator("*", (l, r) => l.multiply(r), true); - static FMUL = new Operator("*", (l, r) => Math.fround(l * r), true); - static DMUL = new Operator("*", (l, r) => +(l * r), true); - - static IDIV = new Operator("/", (l, r) => (l / r) | 0, true); - static LDIV = new Operator("/", (l, r) => l.div(r), true); - static FDIV = new Operator("/", (l, r) => Math.fround(l / r), true); - static DDIV = new Operator("/", (l, r) => +(l / r), true); - - static IREM = new Operator("%", (l, r) => (l % r) | 0, true); - static LREM = new Operator("%", (l, r) => l.modulo(r), true); - static FREM = new Operator("%", (l, r) => Math.fround(l % r), true); - static DREM = new Operator("%", (l, r) => +(l % r), true); - - static INEG = new Operator("-", (a) => (-a) | 0, false); - static LNEG = new Operator("-", (a) => a.negate(), false); - static FNEG = new Operator("-", (a) => -a, false); - static DNEG = new Operator("-", (a) => -a, false); - -// static ADD = new Operator("+", (l, r) => l + r, true); -// static SUB = new Operator("-", (l, r) => l - r, true); -// static MUL = new Operator("*", (l, r) => l * r, true); -// static DIV = new Operator("/", (l, r) => l / r, true); -// static MOD = new Operator("%", (l, r) => l % r, true); - static AND = new Operator("&", (l, r) => l & r, true); - static OR = new Operator("|", (l, r) => l | r, true); - static XOR = new Operator("^", (l, r) => l ^ r, true); - static LSH = new Operator("<<", (l, r) => l << r, true); - static RSH = new Operator(">>", (l, r) => l >> r, true); - static URSH = new Operator(">>>", (l, r) => l >>> r, true); - static SEQ = new Operator("===", (l, r) => l === r, true); - static SNE = new Operator("!==", (l, r) => l !== r, true); - static EQ = new Operator("==", (l, r) => l == r, true); - static NE = new Operator("!=", (l, r) => l != r, true); - static LE = new Operator("<=", (l, r) => l <= r, true); - static GT = new Operator(">", (l, r) => l > r, true); - static LT = new Operator("<", (l, r) => l < r, true); - static GE = new Operator(">=", (l, r) => l >= r, true); - static PLUS = new Operator("+", (a) => +a, false); - static NEG = new Operator("-", (a) => -a, false); - static TRUE = new Operator("!!", (a) => !!a, false); - static FALSE = new Operator("!", (a) => !a, false); - - static TYPE_OF = new Operator("typeof", (a) => typeof a, false); - static BITWISE_NOT = new Operator("~", (a) => ~a, false); - - static linkOpposites(a: Operator, b: Operator) { - a.not = b; - b.not = a; - } - - static fromName(name: string) { - return Operator.byName[name]; - } - } - - Operator.linkOpposites(Operator.SEQ, Operator.SNE); - Operator.linkOpposites(Operator.EQ, Operator.NE); - Operator.linkOpposites(Operator.TRUE, Operator.FALSE); - - export class Binary extends Value { - constructor(public operator: Operator, public left: Value, public right: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.left); - visitor(this.right); - } - } - - Binary.prototype.nodeName = "Binary"; - - export class Unary extends Value { - constructor(public operator: Operator, public argument: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.argument); - } - } - - Unary.prototype.nodeName = "Unary"; - - export class Constant extends Value { - constructor(public value: any) { - super(); - } - } - - Constant.prototype.nodeName = "Constant"; - - export class GlobalProperty extends Value { - constructor(public name: string) { - super(); - } - } - - GlobalProperty.prototype.nodeName = "GlobalProperty"; - - export class This extends Value { - constructor(public control: Control) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - } - } - - This.prototype.nodeName = "This"; - - export class Throw extends Value { - constructor(public control: Control, public argument: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - visitor(this.argument); - } - } - - Throw.prototype.nodeName = "Throw"; - - export class Arguments extends Value { - constructor(public control: Control) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - } - } - - Arguments.prototype.nodeName = "Arguments"; - - export class Parameter extends Value { - constructor(public control: Control, public index: number, public name: string) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - } - } - - Parameter.prototype.nodeName = "Parameter"; - - export class NewArray extends Value { - constructor(public control: Control, public elements: Value []) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - visitArrayInputs(this.elements, visitor); - } - } - - NewArray.prototype.nodeName = "NewArray"; - - export class NewObject extends Value { - constructor(public control: Control, public properties: KeyValuePair []) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.control); - visitArrayInputs(this.properties, visitor); - } - } - - NewObject.prototype.nodeName = "NewObject"; - - export class KeyValuePair extends Value { - constructor(public key: Value, public value: Value) { - super(); - } - visitInputs(visitor: NodeVisitor) { - visitor(this.key); - visitor(this.value); - } - } - - KeyValuePair.prototype.mustFloat = true; - KeyValuePair.prototype.nodeName = "KeyValuePair"; - - export function nameOf(node: any) { - var useColors = false; - var result; - var m = StringUtilities; - if (node instanceof Constant) { - return kindCharacter(node.kind) + node.value; - } else if (node instanceof Variable) { - return node.name; - } else if (node instanceof Parameter) { - return node.name; - } else if (node instanceof Phi) { - return result = m.concat3("|", node.id, "|"), useColors ? m.concat3(IndentingWriter.PURPLE, result, IndentingWriter.ENDC) : result; - } else if (node instanceof Control) { - return result = m.concat3("{", node.id, "}"), useColors ? m.concat3(IndentingWriter.RED, result, IndentingWriter.ENDC) : result; - } else if (node instanceof Projection) { - if (node.type === ProjectionType.STORE) { - return result = m.concat5("[", node.id, "->", node.argument.id, "]"), useColors ? m.concat3(IndentingWriter.YELLOW, result, IndentingWriter.ENDC) : result; - } - return result = m.concat3("(", node.id, ")"), useColors ? m.concat3(IndentingWriter.GREEN, result, IndentingWriter.ENDC) : result; - } else if (node instanceof Value) { - return result = m.concat3("(", node.id, ")"), useColors ? m.concat3(IndentingWriter.GREEN, result, IndentingWriter.ENDC) : result; - } else if (node instanceof Node) { - return node.id; - } - unexpected(node + " " + typeof node); - } -} diff --git a/jit/c4/looper.ts b/jit/c4/looper.ts deleted file mode 100644 index bfb3160b..00000000 --- a/jit/c4/looper.ts +++ /dev/null @@ -1,1065 +0,0 @@ -/* - * Copyright 2014 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module J2ME.C4.Looper { - import top = ArrayUtilities.top; - import peek = ArrayUtilities.peek; - - import CFG = IR.CFG; - import Block = IR.Block; - import BlockVisitor = IR.BlockVisitor; - - import assert = Debug.assert; - - export module Control { - export enum Kind { - SEQ = 1, - LOOP = 2, - IF = 3, - CASE = 4, - SWITCH = 5, - LABEL_CASE = 6, - LABEL_SWITCH = 7, - EXIT = 8, - BREAK = 9, - CONTINUE = 10, - TRY = 11, - CATCH = 12 - } - - export class ControlNode { - constructor(public kind: Kind) { - - } - - compile: (cx: Backend.Context) => AST.Node; - } - - export class Seq extends ControlNode { - constructor(public body) { - super(Kind.SEQ); - } - - trace(writer) { - var body = this.body; - for (var i = 0, j = body.length; i < j; i++) { - body[i].trace(writer); - } - } - - first() { - return this.body[0]; - } - - slice(begin, end) { - return new Seq(this.body.slice(begin, end)); - } - } - - export class Loop extends ControlNode { - constructor(public body) { - super(Kind.LOOP); - } - - trace(writer) { - writer.enter("loop {"); - this.body.trace(writer); - writer.leave("}"); - } - } - - export class If extends ControlNode { - negated: boolean; - else: any; - constructor(public cond, public then, els, public nothingThrownLabel?) { - super(Kind.IF); - this.negated = false; - this.else = els; - } - - trace(writer) { - this.cond.trace(writer); - if (this.nothingThrownLabel) { - writer.enter("if (label is " + this.nothingThrownLabel + ") {"); - } - writer.enter("if" + (this.negated ? " not" : "") + " {"); - this.then && this.then.trace(writer); - if (this.else) { - writer.outdent(); - writer.enter("} else {"); - this.else.trace(writer); - } - writer.leave("}"); - if (this.nothingThrownLabel) { - writer.leave("}"); - } - } - } - - export class Case extends ControlNode { - constructor(public index, public body) { - super(Kind.CASE); - } - - trace(writer) { - if (this.index >= 0) { - writer.writeLn("case " + this.index + ":"); - } else { - writer.writeLn("default:"); - } - writer.indent(); - this.body && this.body.trace(writer); - writer.outdent(); - } - } - - export class Switch extends ControlNode { - constructor(public determinant, public cases, public nothingThrownLabel?) { - super(Kind.SWITCH); - } - - trace(writer) { - if (this.nothingThrownLabel) { - writer.enter("if (label is " + this.nothingThrownLabel + ") {"); - } - this.determinant.trace(writer); - writer.writeLn("switch {"); - for (var i = 0, j = this.cases.length; i < j; i++) { - this.cases[i].trace(writer); - } - writer.writeLn("}"); - if (this.nothingThrownLabel) { - writer.leave("}"); - } - } - } - - export class LabelCase extends ControlNode { - constructor(public labels, public body) { - super(Kind.LABEL_CASE); - } - - trace(writer) { - writer.enter("if (label is " + this.labels.join(" or ") + ") {"); - this.body && this.body.trace(writer); - writer.leave("}"); - } - } - - export class LabelSwitch extends ControlNode { - labelMap: any; - constructor(public cases) { - super(Kind.LABEL_SWITCH); - var labelMap = {}; - - for (var i = 0, j = cases.length; i < j; i++) { - var c = cases[i]; - if (!c.labels) { - // print(c.toSource()); - } - for (var k = 0, l = c.labels.length; k < l; k++) { - labelMap[c.labels[k]] = c; - } - } - - - this.labelMap = labelMap; - } - - trace(writer) { - for (var i = 0, j = this.cases.length; i < j; i++) { - this.cases[i].trace(writer); - } - } - } - - export class Exit extends ControlNode { - constructor(public label) { - super(Kind.EXIT); - } - - trace(writer) { - writer.writeLn("label = " + this.label); - } - } - - export class Break extends ControlNode { - constructor(public label, public head) { - super(Kind.BREAK); - } - - trace(writer) { - this.label && writer.writeLn("label = " + this.label); - writer.writeLn("break"); - } - } - - export class Continue extends ControlNode { - necessary: boolean; - constructor (public label, public head) { - super(Kind.CONTINUE); - this.necessary = true; - } - trace(writer) { - this.label && writer.writeLn("label = " + this.label); - this.necessary && writer.writeLn("continue"); - } - } - - export class Try extends ControlNode { - nothingThrownLabel: boolean; - constructor(public body, public catches) { - super(Kind.TRY); - } - - trace(writer) { - writer.enter("try {"); - this.body.trace(writer); - writer.writeLn("label = " + this.nothingThrownLabel); - for (var i = 0, j = this.catches.length; i < j; i++) { - this.catches[i].trace(writer); - } - writer.leave("}"); - } - } - - export class Catch extends ControlNode { - constructor (public varName, public typeName, public body) { - super(Kind.CATCH); - } - - trace(writer) { - writer.outdent(); - writer.enter("} catch (" + (this.varName || "e") + - (this.typeName ? (" : " + this.typeName) : "") + ") {"); - this.body.trace(writer); - } - } - } - - import BITS_PER_WORD = BitSets.BITS_PER_WORD; - import ADDRESS_BITS_PER_WORD = BitSets.ADDRESS_BITS_PER_WORD; - import BIT_INDEX_MASK = BitSets.BIT_INDEX_MASK; - - export class BlockSet extends BitSets.Uint32ArrayBitSet { - constructor(length: number, public blockById: Map) { - super(length); - } - - forEachBlock(fn: BlockVisitor) { - release || assert(fn); - var byId = this.blockById; - var bits = this.bits; - for (var i = 0, j = bits.length; i < j; i++) { - var word = bits[i]; - if (word) { - for (var k = 0; k < BITS_PER_WORD; k++) { - if (word & (1 << k)) { - fn(byId[i * BITS_PER_WORD + k]); - } - } - } - } - } - - choose(): Block { - var byId = this.blockById; - var bits = this.bits; - for (var i = 0, j = bits.length; i < j; i++) { - var word = bits[i]; - if (word) { - for (var k = 0; k < BITS_PER_WORD; k++) { - if (word & (1 << k)) { - return byId[i * BITS_PER_WORD + k]; - } - } - } - } - } - - members(): Block [] { - var byId = this.blockById; - var set = []; - var bits = this.bits; - for (var i = 0, j = bits.length; i < j; i++) { - var word = bits[i]; - if (word) { - for (var k = 0; k < BITS_PER_WORD; k++) { - if (word & (1 << k)) { - set.push(byId[i * BITS_PER_WORD + k]); - } - } - } - } - return set; - } - - setBlocks(bs: Block []) { - var bits = this.bits; - for (var i = 0, j = bs.length; i < j; i++) { - var id = bs[i].id; - bits[id >> ADDRESS_BITS_PER_WORD] |= 1 << (id & BIT_INDEX_MASK); - } - } - } - - export class Analysis { - blocks: Block []; - boundBlockSet: any; - analyzedControlFlow: boolean; - markedLoops: boolean; - hasExceptions: boolean; - restructuredControlFlow: boolean; - controlTree: Control.ControlNode; - constructor (cfg: CFG) { - this.makeBlockSetFactory(cfg.blocks.length, cfg.blocks); - this.hasExceptions = false; - this.normalizeReachableBlocks(cfg.root); - } - - makeBlockSetFactory(length: number, blockById: Block []) { - release || assert (!this.boundBlockSet); - this.boundBlockSet = (function blockSet() { - return new BlockSet(length, blockById); - }); - } - - normalizeReachableBlocks(root) { - - // The root must not have preds! - release || assert(root.predecessors.length === 0); - - var ONCE = 1; - var BUNCH_OF_TIMES = 2; - var BlockSet = this.boundBlockSet; - - var blocks = []; - var visited = {}; - var ancestors = {}; - var worklist = [root]; - var node; - - ancestors[root.id] = true; - while ((node = top(worklist))) { - if (visited[node.id]) { - if (visited[node.id] === ONCE) { - visited[node.id] = BUNCH_OF_TIMES; - blocks.push(node); - - // Doubly link reachable blocks. - // var successors = node.successors; - // for (var i = 0, j = successors.length; i < j; i++) { - // successors[i].preds.push(node); - // } - } - - ancestors[node.id] = false; - worklist.pop(); - continue; - } - - visited[node.id] = ONCE; - ancestors[node.id] = true; - - var successors = node.successors; - for (var i = 0, j = successors.length; i < j; i++) { - var s = successors[i]; - - if (ancestors[s.id]) { - if (!node.spbacks) { - node.spbacks = new BlockSet(); - } - node.spbacks.set(s.id); - } - !visited[s.id] && worklist.push(s); - } - } - - this.blocks = blocks.reverse(); - } - - // - // Calculate the dominance relation iteratively. - // - // Algorithm is from [1]. - // - // [1] Cooper et al. "A Simple, Fast Dominance Algorithm" - // - computeDominance() { - function intersectDominators(doms, b1, b2) { - var finger1 = b1; - var finger2 = b2; - while (finger1 !== finger2) { - while (finger1 > finger2) { - finger1 = doms[finger1]; - } - while (finger2 > finger1) { - finger2 = doms[finger2]; - } - } - return finger1; - } - - var blocks = this.blocks; - var n = blocks.length; - var doms = new Array(n); - doms[0] = 0; - - // Blocks must be given to us in reverse postorder. - var rpo = []; - for (var b = 0; b < n; b++) { - rpo[blocks[b].id] = b; - blocks[b].dominatees = []; - } - - var changed = true; - while (changed) { - changed = false; - - // Iterate all blocks but the starting block. - for (var b = 1; b < n; b++) { - var predecessors = blocks[b].predecessors; - var j = predecessors.length; - - var newIdom = rpo[predecessors[0].id]; - // Because 0 is falsy, have to use |in| here. - if (!(newIdom in doms)) { - for (var i = 1; i < j; i++) { - newIdom = rpo[predecessors[i].id]; - if (newIdom in doms) { - break; - } - } - } - release || assert(newIdom in doms); - - for (var i = 0; i < j; i++) { - var p = rpo[predecessors[i].id]; - if (p === newIdom) { - continue; - } - - if (p in doms) { - newIdom = intersectDominators(doms, p, newIdom); - } - } - - if (doms[b] !== newIdom) { - doms[b] = newIdom; - changed = true; - } - } - } - - blocks[0].dominator = blocks[0]; - var block; - for (var b = 1; b < n; b++) { - block = blocks[b]; - var idom = blocks[doms[b]]; - - // Store the immediate dominator. - block.dominator = idom; - idom.dominatees.push(block); - - block.npredecessors = block.predecessors.length; - } - - // Assign dominator tree levels. - var worklist = [blocks[0]]; - blocks[0].level || (blocks[0].level = 0); - while ((block = worklist.shift())) { - var dominatees = block.dominatees; - for (var i = 0; i < dominatees.length; i++) { - dominatees[i].level = block.level + 1; - } - worklist.push.apply(worklist, dominatees); - } - } - - computeFrontiers() { - var BlockSet = this.boundBlockSet; - var blocks = this.blocks; - - for (var b = 0, n = blocks.length; b < n; b++) { - blocks[b].frontier = new BlockSet(); - } - - for (var b = 1, n = blocks.length; b < n; b++) { - var block = blocks[b]; - var predecessors = block.predecessors; - - if (predecessors.length >= 2) { - var idom = block.dominator; - for (var i = 0, j = predecessors.length; i < j; i++) { - var runner = predecessors[i]; - - while (runner !== idom) { - runner.frontier.set(block.id); - runner = runner.dominator; - } - } - } - } - } - - analyzeControlFlow() { - this.computeDominance(); - this.analyzedControlFlow = true; - return true; - } - - markLoops() { - if (!this.analyzedControlFlow && !this.analyzeControlFlow()) { - return false; - } - - var BlockSet = this.boundBlockSet; - - // - // Find all SCCs at or below the level of some root that are not already - // natural loops. - // - function findSCCs(root) { - var preorderId = 1; - var preorder = {}; - var assigned = {}; - var unconnectedNodes = []; - var pendingNodes = []; - var sccs = []; - var level = root.level + 1; - var worklist = [root]; - var node; - var u, s; - - while ((node = top(worklist))) { - if (preorder[node.id]) { - if (peek(pendingNodes) === node) { - pendingNodes.pop(); - - var scc = []; - do { - u = unconnectedNodes.pop(); - assigned[u.id] = true; - scc.push(u); - } while (u !== node); - - if (scc.length > 1 || (u.spbacks && u.spbacks.get(u.id))) { - sccs.push(scc); - } - } - - worklist.pop(); - continue; - } - - preorder[node.id] = preorderId++; - unconnectedNodes.push(node); - pendingNodes.push(node); - - var successors = node.successors; - for (var i = 0, j = successors.length; i < j; i++) { - s = successors[i]; - if (s.level < level) { - continue; - } - - var sid = s.id; - if (!preorder[sid]) { - worklist.push(s); - } else if (!assigned[sid]) { - while (preorder[peek(pendingNodes).id] > preorder[sid]) { - pendingNodes.pop(); - } - } - } - } - - return sccs; - } - - function findLoopHeads(blocks) { - var heads = new BlockSet(); - - for (var i = 0, j = blocks.length; i < j; i++) { - var block = blocks[i]; - var spbacks = block.spbacks; - - if (!spbacks) { - continue; - } - - var successors = block.successors; - for (var k = 0, l = successors.length; k < l; k++) { - var s = successors[k]; - if (spbacks.get(s.id)) { - heads.set(s.dominator.id); - } - } - } - - return heads.members(); - } - - function LoopInfo(scc, loopId) { - var body = new BlockSet(); - body.setBlocks(scc); - body.recount(); - - this.id = loopId; - this.body = body; - this.exit = new BlockSet(); - this.save = {}; - this.head = new BlockSet(); - this.npredecessors = 0; - } - - - var heads = findLoopHeads(this.blocks); - if (heads.length <= 0) { - this.markedLoops = true; - return true; - } - - var worklist = heads.sort(function (a, b) { - return a.level - b.level; - }); - var loopId = 0; - - for (var n = worklist.length - 1; n >= 0; n--) { - var t = worklist[n]; - var sccs = findSCCs(t); - if (sccs.length === 0) { - continue; - } - - for (var i = 0, j = sccs.length; i < j; i++) { - var scc = sccs[i]; - var loop = new LoopInfo(scc, loopId++); - for (var k = 0, l = scc.length; k < l; k++) { - var h = scc[k]; - if (h.level === t.level + 1 && !h.loop) { - h.loop = loop; - loop.head.set(h.id); - - var predecessors = h.predecessors; - for (var pi = 0, pj = predecessors.length; pi < pj; pi++) { - loop.body.get(predecessors[pi].id) && h.npredecessors--; - } - loop.npredecessors += h.npredecessors; - } - } - - for (var k = 0, l = scc.length; k < l; k++) { - var h = scc[k]; - if (h.level === t.level + 1) { - h.npredecessors = loop.npredecessors; - } - } - - loop.head.recount(); - } - } - - this.markedLoops = true; - return true; - } - - induceControlTree() { - var hasExceptions = this.hasExceptions; - var BlockSet = this.boundBlockSet; - - function maybe(exit, save) { - exit.recount(); - if (exit.count === 0) { - return null; - } - exit.save = save; - return exit; - } - - var exceptionId = this.blocks.length; - - // - // Based on emscripten's relooper algorithm. - // The algorithm is O(|E|) -- it visits every edge in the CFG once. - // - // Loop header detection is done separately, using an overlaid DJ graph. - // - // For a vertex v, let successor(v) denote its non-exceptional successoressors. - // - // Basic blocks can be restructured into 4 types of nodes: - // - // 1. Switch. |successor(v) > 2| - // 2. If. |successor(v) = 2| - // 3. Plain. |successor(v) = 1| - // 4. Loop. marked as a loop header. - // - // The idea is fairly simple: start at a set of heads, induce all its - // successoressors recursively in that head's context, discharging the edges - // that we take. If a vertex no longer has any incoming edges when we - // visit it, emit the vertex, else emit a label marking that we need to - // go to that vertex and mark that vertex as an exit in the current - // context. - // - // The algorithm starts at the root, the first instruction. - // - // Exceptions are restructured via rewriting. AVM bytecode stores try - // blocks as a range of bytecode positions. Our basic blocks respects - // these range boundaries. Each basic block which is in one or more of - // such exception ranges have exceptional successoressors (jumps) to all - // matching catch blocks. We then restructure the entire basic block as - // a try and have the restructuring take care of the jumps to the actual - // catch blocks. Finally blocks fall out naturally, but are not emitted - // as JavaScript |finally|. - // - // Implementation Notes - // -------------------- - // - // We discharge edges by keeping a property |npredecessors| on each block that - // says how many incoming edges we have _not yet_ discharged. We - // discharge edges as we recur on the tree, but in case we can't emit a - // block (i.e. its |npredecessors| > 0), we need to restore its |npredecessors| before - // we pop out. We do this via a |save| proeprty on each block that says - // how many predecessors we should restore. - // - // |exit| is the set of exits in the current context, i.e. the set of - // vertices that we visited but have not yet discharged every incoming - // edge. - // - // |save| is a mapping of block id -> save numbers. - // - // When setting an exit in the exit set, the save number must be set for - // it also in the save set. - // - function induce(head, exit, save, loop?, inLoopHead?, lookupSwitch?, fallthrough?) { - var v = []; - - while (head) { - if (head.count > 1) { - var exit2 = new BlockSet(); - var save2 = {}; - - var cases = []; - var heads = head.members(); - - for (var i = 0, j = heads.length; i < j; i++) { - var h = heads[i]; - var bid = h.id; - var c; - - if (h.loop && head.contains(h.loop.head)) { - var loop2 = h.loop; - if (!loop2.induced) { - var lheads = loop2.head.members(); - var lheadsave = 0; - - for (k = 0, l = lheads.length; k < l; k++) { - lheadsave += head.save[lheads[k].id]; - } - - if (h.npredecessors - lheadsave > 0) { - // Don't even enter the loop if we're just going to exit - // anyways. - h.npredecessors -= head.save[bid]; - h.save = head.save[bid]; - c = induce(h, exit2, save2, loop); - cases.push(new Control.LabelCase([bid], c)); - } else { - for (k = 0, l = lheads.length; k < l; k++) { - var lh = lheads[k]; - lh.npredecessors -= lheadsave; - lh.save = lheadsave; - } - c = induce(h, exit2, save2, loop); - cases.push(new Control.LabelCase(loop2.head.toArray(), c)); - loop2.induced = true; - } - } - } else { - h.npredecessors -= head.save[bid]; - h.save = head.save[bid]; - c = induce(h, exit2, save2, loop); - cases.push(new Control.LabelCase([bid], c)); - } - } - - var pruned = []; - var k = 0; - var c; - for (var i = 0; i < cases.length; i++) { - c = cases[i]; - var labels = c.labels; - var lk = 0; - for (var ln = 0, nlabels = labels.length; ln < nlabels; ln++) { - var bid = labels[ln]; - if (exit2.get(bid) && heads[i].npredecessors - head.save[bid] > 0) { - pruned.push(bid); - } else { - labels[lk++] = bid; - } - } - labels.length = lk; - - // Prune the case unless it still has some entry labels. - if (labels.length > 0) { - cases[k++] = c; - } - } - cases.length = k; - - if (cases.length === 0) { - for (var i = 0; i < pruned.length; i++) { - var bid = pruned[i]; - save[bid] = (save[bid] || 0) + head.save[bid]; - exit.set(bid); - } - break; - } - - v.push(new Control.LabelSwitch(cases)); - - head = maybe(exit2, save2); - continue; - } - - var h, bid, c; - - if (head.count === 1) { - h = head.choose(); - bid = h.id; - h.npredecessors -= head.save[bid]; - h.save = head.save[bid]; - } else { - h = head; - bid = h.id; - } - - if (inLoopHead) { - inLoopHead = false; - } else { - if (loop && !loop.body.get(bid)) { - h.npredecessors += h.save; - loop.exit.set(bid); - loop.save[bid] = (loop.save[bid] || 0) + h.save; - v.push(new Control.Break(bid, loop)); - break; - } - - if (loop && h.loop === loop) { - h.npredecessors += h.save; - v.push(new Control.Continue(bid, loop)); - break; - } - - if (h === fallthrough) { - break; - } - - if (h.npredecessors > 0) { - h.npredecessors += h.save; - save[bid] = (save[bid] || 0) + h.save; - exit.set(bid); - v.push(lookupSwitch ? - new Control.Break(bid, lookupSwitch) : - new Control.Exit(bid)); - break; - } - - if (h.loop) { - var l = h.loop; - - var body; - if (l.head.count === 1) { - body = induce(l.head.choose(), null, null, l, true); - } else { - var lcases = []; - var lheads = l.head.members(); - - for (var i = 0, j = lheads.length; i < j; i++) { - var lh = lheads[i]; - var lbid = lh.id; - var c = induce(lh, null, null, l, true); - lcases.push(new Control.LabelCase([lbid], c)); - } - - body = new Control.LabelSwitch(lcases); - } - - v.push(new Control.Loop(body)); - head = maybe(l.exit, l.save); - continue; - } - } - - var sv; - var successors; - var exit2 = new BlockSet(); - var save2 = {}; - - if (hasExceptions && h.hasCatches) { - var allsuccessors = h.successors; - var catchsuccessors = []; - successors = []; - - for (var i = 0, j = allsuccessors.length; i < j; i++) { - var s = allsuccessors[i]; - (s.exception ? catchsuccessors : successors).push(s); - } - - var catches = []; - for (var i = 0; i < catchsuccessors.length; i++) { - var t = catchsuccessors[i]; - t.npredecessors -= 1; - t.save = 1; - var c = induce(t, exit2, save2, loop); - var ex = t.exception; - catches.push(new Control.Catch(ex.varName, ex.typeName, c)); - } - - sv = new Control.Try(h, catches); - } else { - successors = h.successors; - sv = h; - } - - /* - if (h.end.op === OP_lookupswitch) { - var cases = []; - var targets = h.end.targets; - - for (var i = targets.length - 1; i >= 0; i--) { - var t = targets[i]; - t.npredecessors -= 1; - t.save = 1; - c = induce(t, exit2, save2, loop, null, h, targets[i + 1]); - cases.unshift(new Control.Case(i, c)); - } - - // The last case is the default case. - cases.top().index = undefined; - - if (hasExceptions && h.hasCatches) { - sv.nothingThrownLabel = exceptionId; - sv = new Control.Switch(sv, cases, exceptionId++); - } else { - sv = new Control.Switch(sv, cases); - } - - head = maybe(exit2, save2); - } else - */ - - if (successors.length > 2) { - var cases = []; - var targets = successors; - - for (var i = targets.length - 1; i >= 0; i--) { - var t = targets[i]; - t.npredecessors -= 1; - t.save = 1; - c = induce(t, exit2, save2, loop, null, h, targets[i + 1]); - cases.unshift(new Control.Case(i, c)); - } - - // The last case is the default case. - top(cases).index = undefined; - - if (hasExceptions && h.hasCatches) { - sv.nothingThrownLabel = exceptionId; - sv = new Control.Switch(sv, cases, exceptionId++); - } else { - sv = new Control.Switch(sv, cases); - } - - head = maybe(exit2, save2); - } else if (successors.length === 2) { - var branch1 = h.hasFlippedSuccessors ? successors[1] : successors[0]; - var branch2 = h.hasFlippedSuccessors ? successors[0] : successors[1]; - branch1.npredecessors -= 1; - branch1.save = 1; - var c1 = induce(branch1, exit2, save2, loop); - - branch2.npredecessors -= 1; - branch2.save = 1; - var c2 = induce(branch2, exit2, save2, loop); - - if (hasExceptions && h.hasCatches) { - sv.nothingThrownLabel = exceptionId; - sv = new Control.If(sv, c1, c2, exceptionId++); - } else { - sv = new Control.If(sv, c1, c2); - } - - head = maybe(exit2, save2); - } else { - c = successors[0]; - - if (c) { - if (hasExceptions && h.hasCatches) { - sv.nothingThrownLabel = c.id; - save2[c.id] = (save2[c.id] || 0) + 1; - exit2.set(c.id); - - head = maybe(exit2, save2); - } else { - c.npredecessors -= 1; - c.save = 1; - head = c; - } - } else { - if (hasExceptions && h.hasCatches) { - sv.nothingThrownLabel = -1; - head = maybe(exit2, save2); - } else { - head = c; - } - } - } - - v.push(sv); - } - - - if (v.length > 1) { - return new Control.Seq(v); - } - - return v[0]; - } - - var root = this.blocks[0]; - this.controlTree = induce(root, new BlockSet(), {}); - } - - restructureControlFlow() { - enterTimeline("Restructure Control Flow"); - if (!this.markedLoops && !this.markLoops()) { - leaveTimeline(); - return false; - } - this.induceControlTree(); - this.restructuredControlFlow = true; - leaveTimeline(); - return true; - } - } - - export function analyze(cfg: CFG): Control.ControlNode { - var analysis = new Analysis(cfg); - analysis.restructureControlFlow(); - return analysis.controlTree; - } -} diff --git a/jit/c4/optimizer.ts b/jit/c4/optimizer.ts deleted file mode 100644 index 10d6039a..00000000 --- a/jit/c4/optimizer.ts +++ /dev/null @@ -1,1254 +0,0 @@ -/* - * Copyright 2014 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module J2ME.C4.IR { - import assert = Debug.assert; - import unexpected = Debug.unexpected; - import createEmptyObject = ObjectUtilities.createEmptyObject; - import top = ArrayUtilities.top; - import bitCount = IntegerUtilities.bitCount; - import pushUnique = ArrayUtilities.pushUnique; - import unique = ArrayUtilities.unique; - - var debug = false; - - function toID(node) { - return node.id; - } - - function visitNothing() { - - } - - export function isNotPhi(phi) { - return !isPhi(phi); - } - - export function isPhi(phi) { - return phi instanceof Phi; - } - -// export function isScope(scope) { -// return isPhi(scope) || scope instanceof ASScope || isProjection(scope, ProjectionType.SCOPE); -// } - -// export function isMultinameConstant(node) { -// return node instanceof Constant && node.value instanceof Multiname; -// } -// -// export function isMultiname(name) { -// return isMultinameConstant(name) || name instanceof ASMultiname; -// } - - export function isStore(store) { - return isPhi(store) || store instanceof Store || isProjection(store, ProjectionType.STORE); - } - - export function isConstant(constant) { - return constant instanceof Constant; - } - - function isBoolean(value) { - return value === true || value === false; - } - - export function isControlOrNull(control) { - return isControl(control) || control === null; - } - - export function isStoreOrNull(store) { - return isStore(store) || store === null; - } - - export function isControl(control) { - return control instanceof Control; - } - - export function isValueOrNull(value) { - return isValue(value) || value === null; - } - - export function isValue(value) { - return value instanceof Value; - } - - export function isProjection(node, type) { - return node instanceof Projection && (!type || node.type === type); - } - - function followProjection(node) { - return node instanceof Projection ? node.project() : node; - } - - export var Null = new Constant(null); - export var Undefined = new Constant(undefined); - export var True = new Constant(true); - export var False = new Constant(false); - - export class Block { - id: number; - rpo: number; - name: string; - phis: Phi []; - loops: number; - nodes: Node []; - region: Node; - dominator: Block; - successors: Block []; - predecessors: Block []; - isLoopHeader: boolean; - /** - * This is added by the codegen. - */ - compile: (cx, state) => void; - - /** - * This is stuff added on by the looper which needs to be really cleaned up. - */ - dominatees: Block []; - npredecessors: number; - level: number; - frontier: any; - - constructor(id: number, start?: Region, end?: Node) { - this.id = id; - this.nodes = [start, end]; - this.region = start; - this.successors = []; - this.predecessors = []; - } - pushSuccessorAt(successor: Block, index: number, pushPredecessor?: boolean) { - release || assert (successor); - release || assert (!this.successors[index]); - this.successors[index] = successor; - if (pushPredecessor) { - successor.pushPredecessor(this); - } - } - pushSuccessor(successor: Block, pushPredecessor?: boolean) { - release || assert (successor); - this.successors.push(successor); - if (pushPredecessor) { - successor.pushPredecessor(this); - } - } - pushPredecessor(predecessor: Block) { - release || assert (predecessor); - this.predecessors.push(predecessor); - } - visitNodes(fn: NodeVisitor) { - var nodes = this.nodes; - for (var i = 0, j = nodes.length; i < j; i++) { - fn(nodes[i]); - } - } - visitSuccessors(fn: BlockVisitor) { - var successors = this.successors; - for (var i = 0, j = successors.length; i < j; i++) { - fn(successors[i]); - } - } - visitPredecessors(fn: BlockVisitor) { - var predecessors = this.predecessors; - for (var i = 0, j = predecessors.length; i < j; i++) { - fn(predecessors[i]); - } - } - append(node: Node) { - release || assert (this.nodes.length >= 2); - release || assert (isValue(node), node); - release || assert (isNotPhi(node)); - release || assert (this.nodes.indexOf(node) < 0); - if (node.mustFloat) { - return; - } - this.nodes.splice(this.nodes.length - 1, 0, node); - } - toString() { - return "B" + this.id + (this.name ? " (" + this.name + ")" : ""); - } - trace(writer: IndentingWriter) { - writer.writeLn(this.toString()); - } - } - - export class DFG { - start: Node; - constructor(public exit: Node) { - this.exit = exit; - } - - buildCFG() { - return CFG.fromDFG(this); - } - - static preOrderDepthFirstSearch(root: Node, visitChildren, pre: NodeVisitor) { - var visited = []; - var worklist = [root]; - var push = worklist.push.bind(worklist); - var node; - while ((node = worklist.pop())) { - if (visited[node.id] === 1) { - continue; - } - visited[node.id] = 1; - pre(node); - worklist.push(node); - visitChildren(node, push); - } - } - - static postOrderDepthFirstSearch(root: Node, visitChildren, post: NodeVisitor) { - var ONE_TIME = 1, MANY_TIMES = 2; - var visited = []; - var worklist = [root]; - function visitChild(child) { - if (!visited[child.id]) { - worklist.push(child); - } - } - var node; - while ((node = top(worklist))) { - if (visited[node.id]) { - if (visited[node.id] === ONE_TIME) { - visited[node.id] = MANY_TIMES; - post(node); - } - worklist.pop(); - continue; - } - visited[node.id] = ONE_TIME; - visitChildren(node, visitChild); - } - } - - forEachInPreOrderDepthFirstSearch(visitor: NodeVisitor) { - var visited = new Array(1024); - var worklist = [this.exit]; - function push(node) { - if (isConstant(node)) { - return; - } - release || assert (node instanceof Node); - worklist.push(node); - } - var node; - while ((node = worklist.pop())) { - if (visited[node.id]) { - continue; - } - visited[node.id] = 1; - visitor && visitor(node); - worklist.push(node); - node.visitInputs(push); - } - } - - forEach(visitor: NodeVisitor, postOrder: boolean) { - var search = postOrder ? DFG.postOrderDepthFirstSearch : DFG.preOrderDepthFirstSearch; - search(this.exit, function (node, v) { - node.visitInputsNoConstants(v); - }, visitor); - } - - traceMetrics(writer: IndentingWriter) { - var counter = new Metrics.Counter(true); - DFG.preOrderDepthFirstSearch(this.exit, function (node, visitor) { - node.visitInputsNoConstants(visitor); - }, function (node) { - countTimeline(node.nodeName); - }); - counter.trace(writer); - } - - trace(writer: IndentingWriter) { - var nodes = []; - var visited = {}; - - function colorOf(node) { - if (node instanceof Control) { - return "yellow"; - } else if (node instanceof Phi) { - return "purple"; - } else if (node instanceof Value) { - return "green"; - } - return "white"; - } - - var blocks = []; - - function followProjection(node: Node): Node { - return node instanceof Projection ? node.project() : node; - } - - function next(node: Node) { - node = followProjection(node); - if (!visited[node.id]) { - visited[node.id] = true; - var control = node; - if (control.block) { - blocks.push(control.block); - } - nodes.push(node); - node.visitInputsNoConstants(next); - } - } - - next(this.exit); - - writer.writeLn(""); - writer.enter("digraph DFG {"); - writer.writeLn("graph [bgcolor = gray10];"); - writer.writeLn("edge [color = white];"); - writer.writeLn("node [shape = box, fontname = Consolas, fontsize = 11, color = white, fontcolor = white];"); - writer.writeLn("rankdir = BT;"); - - function writeNode(node) { - writer.writeLn("N" + node.id + " [label = \"" + node.toString() + - "\", color = \"" + colorOf(node) + "\"];"); - } - - function defineNode(node) { - writer.writeLn("N" + node.id + ";"); - } - - blocks.forEach(function (block) { - writer.enter("subgraph cluster" + block.nodes[0].id + " { bgcolor = gray20;"); - block.visitNodes(function (node) { - defineNode(followProjection(node)); - }); - writer.leave("}"); - }); - - nodes.forEach(writeNode); - - nodes.forEach(function (node) { - node.visitInputsNoConstants(function (input) { - input = followProjection(input); - writer.writeLn("N" + node.id + " -> " + "N" + input.id + " [color=" + colorOf(input) + "];"); - }); - }); - - writer.leave("}"); - writer.writeLn(""); - } - } - - export interface UseEntry { - def: Node; - uses: Node []; - } - - export class Uses { - entries: UseEntry []; - constructor() { - this.entries = []; - } - addUse(def: Node, use: Node) { - var entry = this.entries[def.id]; - if (!entry) { - entry = this.entries[def.id] = {def: def, uses: []}; - } - pushUnique(entry.uses, use); - } - trace(writer) { - writer.enter("> Uses"); - this.entries.forEach(function (entry) { - writer.writeLn(entry.def.id + " -> [" + entry.uses.map(toID).join(", ") + "] " + entry.def); - }); - writer.leave("<"); - } - replace(def: Node, value: Node) { - var entry = this.entries[def.id]; - if (entry.uses.length === 0) { - return false; - } - var count = 0; - entry.uses.forEach(function (use) { - count += use.replaceInput(def, value); - }); - release || assert (count >= entry.uses.length); - entry.uses = []; - return true; - } - updateUses(def: Node, value: Node, useEntries: UseEntry [], writer: IndentingWriter) { - debug && writer.writeLn("Update " + def + " with " + value); - var entry = useEntries[def.id]; - if (entry.uses.length === 0) { - return false; - } - debug && writer.writeLn("Replacing: " + def.id + " in [" + entry.uses.map(toID).join(", ") + "] with " + value.id); - var count = 0; - entry.uses.forEach(function (use) { - count += use.replaceInput(def, value); - }); - release || assert (count >= entry.uses.length); - entry.uses = []; - return true; - } - } - - export class CFG { - dfg: DFG; - exit: Block; - root: Block; - order: Block []; - blocks: Block []; - nextBlockID: number; - blockNames: Map; - setConstructor: any; - constructor() { - this.nextBlockID = 0; - this.blocks = []; - } - - static fromDFG(dfg) { - var cfg = new CFG(); - - release || assert (dfg && dfg instanceof DFG); - cfg.dfg = dfg; - - var visited = []; - - function buildEnd(end) { - if (end instanceof Projection) { - end = end.project(); - } - release || assert (end instanceof End || end instanceof Start, end); - if (visited[end.id]) { - return; - } - visited[end.id] = true; - var start = end.control; - if (!(start instanceof Region)) { - start = end.control = new Region(start); - } - var block = start.block = cfg.buildBlock(start, end); - if (start instanceof Start) { - cfg.root = block; - } - for (var i = 0; i < start.predecessors.length; i++) { - var c = start.predecessors[i]; - var d; - var trueProjection = false; - if (c instanceof Projection) { - d = c.project(); - trueProjection = c.type === ProjectionType.TRUE; - } else { - d = c; - } - if (d instanceof Region) { - d = new Jump(c); - d = new Projection(d, ProjectionType.TRUE); - start.predecessors[i] = d; - d = d.project(); - trueProjection = true; - } - buildEnd(d); - var controlBlock = d.control.block; - if (d instanceof Switch) { - release || assert (isProjection(c, ProjectionType.CASE)); - controlBlock.pushSuccessorAt(block, c.selector.value, true); - } else if (trueProjection && controlBlock.successors.length > 0) { - controlBlock.pushSuccessor(block, true); - controlBlock.hasFlippedSuccessors = true; - } else { - controlBlock.pushSuccessor(block, true); - } - } - } - - buildEnd(dfg.exit); - cfg.splitCriticalEdges(); - cfg.exit = dfg.exit.control.block; - cfg.computeDominators(true); - return cfg; - } - - /** - * Makes sure root node has no predecessors and that there is only one - * exit node. - */ - buildRootAndExit() { - release || assert (!this.root && !this.exit); - - // Create new root node if the root node has predecessors. - if (this.blocks[0].predecessors.length > 0) { - this.root = new Block(this.nextBlockID++); - this.blocks.push(this.root); - this.root.pushSuccessor(this.blocks[0], true); - } else { - this.root = this.blocks[0]; - } - var exitBlocks = []; - - // Collect exit blocks (blocks with no successors). - for (var i = 0; i < this.blocks.length; i++) { - var block = this.blocks[i]; - if (block.successors.length === 0) { - exitBlocks.push(block); - } - } - - if (exitBlocks.length === 0) { - unexpected("Must have an exit block."); - } else if (exitBlocks.length === 1 && exitBlocks[0] !== this.root) { - this.exit = exitBlocks[0]; - } else { - // Create new exit block to merge flow. - this.exit = new Block(this.nextBlockID++); - this.blocks.push(this.exit); - for (var i = 0; i < exitBlocks.length; i++) { - exitBlocks[i].pushSuccessor(this.exit, true); - } - } - - release || assert (this.root && this.exit); - release || assert (this.root !== this.exit); - } - - buildBlock(start, end): Block { - var block = new Block(this.nextBlockID++, start, end); - this.blocks.push(block); - return block; - } - - createBlockSet() { - if (!this.setConstructor) { - this.setConstructor = BitSets.BitSetFunctor(this.blocks.length); - } - return new this.setConstructor(); - } - - computeReversePostOrder() { - if (this.order) { - return this.order; - } - var order = this.order = []; - this.depthFirstSearch(null, order.push.bind(order)); - order.reverse(); - for (var i = 0; i < order.length; i++) { - order[i].rpo = i; - } - return order; - } - - depthFirstSearch(preFn, postFn?) { - var visited = this.createBlockSet(); - function visit(node) { - visited.set(node.id); - if (preFn) preFn(node); - var successors = node.successors; - for (var i = 0, j = successors.length; i < j; i++) { - var s = successors[i]; - if (!visited.get(s.id)) { - visit(s); - } - } - if (postFn) postFn(node); - } - visit(this.root); - } - - computeDominators(apply) { - release || assert (this.root.predecessors.length === 0, "Root node " + this.root + " must not have predecessors."); - - var dom = new Int32Array(this.blocks.length); - for (var i = 0; i < dom.length; i++) { - dom[i] = -1; - } - var map = this.createBlockSet(); - function computeCommonDominator(a, b) { - map.clearAll(); - while (a >= 0) { - map.set(a); - a = dom[a]; - } - while (b >= 0 && !map.get(b)) { - b = dom[b]; - } - return b; - } - function computeDominator(blockID, parentID) { - if (dom[blockID] < 0) { - dom[blockID] = parentID; - } else { - dom[blockID] = computeCommonDominator(dom[blockID], parentID); - } - } - this.depthFirstSearch ( - function visit(block) { - var s = block.successors; - for (var i = 0, j = s.length; i < j; i++) { - computeDominator(s[i].id, block.id); - } - } - ); - if (apply) { - for (var i = 0, j = this.blocks.length; i < j; i++) { - this.blocks[i].dominator = this.blocks[dom[i]]; - } - function computeDominatorDepth(block) { - var dominatorDepth; - if (block.dominatorDepth !== undefined) { - return block.dominatorDepth; - } else if (!block.dominator) { - dominatorDepth = 0; - } else { - dominatorDepth = computeDominatorDepth(block.dominator) + 1; - } - return block.dominatorDepth = dominatorDepth; - } - for (var i = 0, j = this.blocks.length; i < j; i++) { - computeDominatorDepth(this.blocks[i]); - } - } - return dom; - } - - computeLoops() { - var active = this.createBlockSet(); - var visited = this.createBlockSet(); - var nextLoop = 0; - - function makeLoopHeader(block) { - if (!block.isLoopHeader) { - release || assert(nextLoop < 32, "Can't handle too many loops, fall back on BitMaps if it's a problem."); - block.isLoopHeader = true; - block.loops = 1 << nextLoop; - nextLoop += 1; - } - release || assert(bitCount(block.loops) === 1); - } - - function visit(block: Block) { - if (visited.get(block.id)) { - if (active.get(block.id)) { - makeLoopHeader(block); - } - return block.loops; - } - visited.set(block.id); - active.set(block.id); - var loops = 0; - for (var i = 0, j = block.successors.length; i < j; i++) { - loops |= visit(block.successors[i]); - } - if (block.isLoopHeader) { - release || assert(bitCount(block.loops) === 1); - loops &= ~block.loops; - } - block.loops = loops; - active.clear(block.id); - return loops; - } - - var loop = visit(this.root); - release || assert(loop === 0); - } - - /** - * Computes def-use chains. - * - * () -> Map[id -> {def:Node, uses:Array[Node]}] - */ - computeUses(): Uses { - enterTimeline("computeUses"); - var writer = debug && new IndentingWriter(); - - debug && writer.enter("> Compute Uses"); - var dfg = this.dfg; - - var uses = new Uses(); - - dfg.forEachInPreOrderDepthFirstSearch(function (use) { - use.visitInputs(function (def) { - uses.addUse(def, use); - }); - }); - - if (debug) { - writer.enter("> Uses"); - uses.entries.forEach(function (entry) { - writer.writeLn(entry.def.id + " -> [" + entry.uses.map(toID).join(", ") + "] " + entry.def); - }); - writer.leave("<"); - writer.leave("<"); - } - leaveTimeline(); - return uses; - } - - verify() { - var writer = debug && new IndentingWriter(); - debug && writer.enter("> Verify"); - - var order = this.computeReversePostOrder(); - - order.forEach(function (block) { - if (block.phis) { - block.phis.forEach(function (phi) { - release || assert (phi.control === block.region); - release || assert (phi.args.length === block.predecessors.length); - }); - } - }); - - debug && writer.leave("<"); - } - - /** - * Simplifies phis of the form: - * - * replace |x = phi(y)| -> y - * replace |x = phi(x, y)| -> y - * replace |x = phi(y, y, x, y, x)| -> |phi(y, x)| -> y - */ - optimizePhis() { - var writer = debug && new IndentingWriter(); - debug && writer.enter("> Optimize Phis"); - - var phis = []; - var useEntries = this.computeUses().entries; - useEntries.forEach(function (entry) { - if (isPhi(entry.def)) { - phis.push(entry.def); - } - }); - - debug && writer.writeLn("Trying to optimize " + phis.length + " phis."); - - /** - * Updates all uses to a new definition. Returns true if anything was updated. - */ - function updateUses(def, value) { - debug && writer.writeLn("Update " + def + " with " + value); - var entry = useEntries[def.id]; - if (entry.uses.length === 0) { - return false; - } - debug && writer.writeLn("Replacing: " + def.id + " in [" + entry.uses.map(toID).join(", ") + "] with " + value.id); - var count = 0; - var entryUses = entry.uses; - for (var i = 0, j = entryUses.length; i < j; i++) { - count += entryUses[i].replaceInput(def, value); - } - release || assert (count >= entry.uses.length); - entry.uses = []; - return true; - } - - function simplify(phi, args) { - args = unique(args); - if (args.length === 1) { - // x = phi(y) -> y - return args[0]; - } else { - if (args.length === 2) { - // x = phi(y, x) -> y - if (args[0] === phi) { - return args[1]; - } else if (args[1] === phi) { - return args[0]; - } - return phi; - } - } - return phi; - } - - var count = 0; - var iterations = 0; - var changed = true; - while (changed) { - iterations ++; - changed = false; - phis.forEach(function (phi) { - var value = simplify(phi, phi.args); - if (value !== phi) { - if (updateUses(phi, value)) { - changed = true; - count ++; - } - } - }); - } - - if (debug) { - writer.writeLn("Simplified " + count + " phis, in " + iterations + " iterations."); - writer.leave("<"); - } - } - - /** - * "A critical edge is an edge which is neither the only edge leaving its source block, nor the only edge entering - * its destination block. These edges must be split: a new block must be created in the middle of the edge, in order - * to insert computations on the edge without affecting any other edges." - Wikipedia - */ - splitCriticalEdges() { - var writer = debug && new IndentingWriter(); - var blocks = this.blocks; - var criticalEdges = []; - debug && writer.enter("> Splitting Critical Edges"); - for (var i = 0; i < blocks.length; i++) { - var successors = blocks[i].successors; - if (successors.length > 1) { - for (var j = 0; j < successors.length; j++) { - if (successors[j].predecessors.length > 1) { - criticalEdges.push({from: blocks[i], to: successors[j]}); - } - } - } - } - - var criticalEdgeCount = criticalEdges.length; - if (criticalEdgeCount && debug) { - writer.writeLn("Splitting: " + criticalEdgeCount); - this.trace(writer); - } - - var edge; - while ((edge = criticalEdges.pop())) { - var fromIndex = edge.from.successors.indexOf(edge.to); - var toIndex = edge.to.predecessors.indexOf(edge.from); - release || assert (fromIndex >= 0 && toIndex >= 0); - debug && writer.writeLn("Splitting critical edge: " + edge.from + " -> " + edge.to); - var toBlock = edge.to; - var toRegion = toBlock.region; - var control = toRegion.predecessors[toIndex]; - var region = new Region(control); - var jump = new Jump(region); - var block = this.buildBlock(region, jump); - toRegion.predecessors[toIndex] = new Projection(jump, ProjectionType.TRUE); - - var fromBlock = edge.from; - fromBlock.successors[fromIndex] = block; - block.pushPredecessor(fromBlock); - block.pushSuccessor(toBlock); - toBlock.predecessors[toIndex] = block; - } - - if (criticalEdgeCount && debug) { - this.trace(writer); - } - - if (criticalEdgeCount && !release) { - release || assert (this.splitCriticalEdges() === 0); - } - - debug && writer.leave("<"); - - return criticalEdgeCount; - } - - /** - * Allocate virtual registers and break out of SSA. - */ - allocateVariables() { - var writer = debug && new IndentingWriter(); - - var uses = this.computeUses(); - - debug && writer.enter("> Allocating Virtual Registers"); - var order = this.computeReversePostOrder(); - - function allocate(node) { - if (isProjection(node, ProjectionType.STORE)) { - return; - } - //if (!uses[node.id]) { - // return; - //} - if (node instanceof SetProperty) { - return; - } - if (node instanceof Value) { - node.variable = new Variable("v" + node.id); - debug && writer.writeLn("Allocated: " + node.variable + " to " + node); - } - } - - order.forEach(function (block) { - block.nodes.forEach(allocate); - if (block.phis) { - block.phis.forEach(allocate); - } - }); - - /** - * To break out of SSA form we need to emit moves in the phi's predecessor blocks. Here we - * collect the set of all moves in |blockMoves| : Map[id -> Array[Move]] - * - * The moves actually need to be emitted along the phi's predecessor edges. Emitting them in the - * predecessor blocks is only correct in the absence of CFG critical edges. - */ - - var blockMoves = []; - for (var i = 0; i < order.length; i++) { - var block = order[i]; - var phis = block.phis; - var predecessors = block.predecessors; - if (phis) { - for (var j = 0; j < phis.length; j++) { - var phi = phis[j]; - debug && writer.writeLn("Emitting moves for: " + phi); - var arguments = phi.args; - release || assert (predecessors.length === arguments.length); - for (var k = 0; k < predecessors.length; k++) { - var predecessor = predecessors[k]; - var argument = arguments[k]; - if (argument.abstract || isProjection(argument, ProjectionType.STORE)) { - continue; - } - var moves = blockMoves[predecessor.id] || (blockMoves[predecessor.id] = []); - argument = argument.variable || argument; - if (phi.variable !== argument) { - moves.push(new Move(phi.variable, argument)); - } - } - } - } - } - - /** - * All move instructions must execute simultaneously. Since there may be dependencies between - * source and destination operands we need to sort moves topologically. This is not always - * possible because of cyclic dependencies. In such cases break the cycles using temporaries. - * - * Simplest example where this happens: - * var a, b, t; - * while (true) { - * t = a; a = b; b = t; - * } - */ - var blocks = this.blocks; - blockMoves.forEach(function (moves, blockID) { - var block = blocks[blockID]; - var temporary = 0; - debug && writer.writeLn(block + " Moves: " + moves); - while (moves.length) { - // Find a move that is safe to emit, i.e. no other move depends on its destination. - for (var i = 0; i < moves.length; i++) { - var move = moves[i]; - // Find a move that depends on the move's destination? - for (var j = 0; j < moves.length; j++) { - if (i === j) { - continue; - } - if (moves[j].from === move.to) { - move = null; - break; - } - } - if (move) { - moves.splice(i--, 1); - block.append(move); - } - } - - if (moves.length) { - // We have a cycle, break it with a temporary. - debug && writer.writeLn("Breaking Cycle"); - // 1. Pick any move. - var move = moves[0]; - // 2. Emit a move to save its destination in a temporary. - var temp = new Variable("t" + temporary++); - blocks[blockID].append(new Move(temp, move.to)); - // 3. Update all moves's source to refer to the temporary. - for (var i = 1; i < moves.length; i++) { - if (moves[i].from === move.to) { - moves[i].from = temp; - } - } - // 4. Loop, baby, loop. - } - } - }); - - debug && writer.leave("<"); - } - - scheduleEarly() { - var debugScheduler = false; - var writer = debugScheduler && new IndentingWriter(); - - debugScheduler && writer.enter("> Schedule Early"); - - var cfg = this; - var dfg = this.dfg; - - var scheduled = []; - - var roots = []; - - dfg.forEachInPreOrderDepthFirstSearch(function (node) { - if (node instanceof Region || node instanceof Jump) { - return; - } - if (node.control) { - roots.push(node); - } - if (isPhi(node)) { - var phi = node; - /** - * When breaking out of SSA, move instructions need to have non-floating source nodes. Otherwise - * the topological sorting of moves gets more complicated, especially when cyclic dependencies - * are involved. Here we just mark all floating inputs of phi nodes as non-floating which forces - * them to get scheduled. - * - * TODO: Find out if this requirement is too expensive. We can make the move insertion algorithm - * more intelligent so that it walks the inputs of floating nodes when looking for dependencies. - */ - phi.args.forEach(function (input) { - if (shouldFloat(input)) { - input.mustNotFloat = true; - } - }); - } - }); - - if (debugScheduler) { - roots.forEach(function (node) { - writer && writer.writeLn("Root: " + node); - }); - } - - for (var i = 0; i < roots.length; i++) { - var root = roots[i]; - if (root instanceof Phi) { - var block = root.control.block; - (block.phis || (block.phis = [])).push(root); - } - if (root.control) { - schedule(root); - } - } - - function isScheduled(node) { - return scheduled[node.id]; - } - - function shouldFloat(node) { - if (node.mustNotFloat || node.shouldNotFloat) { - return false; - } - if (node.mustFloat || node.shouldFloat) { - return true; - } - if (node instanceof Parameter || node instanceof This || node instanceof Arguments) { - return true; - } - return node instanceof Binary || node instanceof Unary || node instanceof Parameter; - } - - function append(node) { - release || assert (!isScheduled(node), "Already scheduled " + node); - scheduled[node.id] = true; - release || assert (node.control, node); - if (shouldFloat(node)) { - - } else { - node.control.block.append(node); - } - } - - function scheduleIn(node, region) { - release || assert (!node.control, node); - release || assert (!isScheduled(node)); - release || assert (region); - debugScheduler && writer.writeLn("Scheduled: " + node + " in " + region); - node.control = region; - append(node); - } - - function schedule(node) { - debugScheduler && writer.enter("> Schedule: " + node); - - var inputs = []; - // node.checkInputVisitors(); - node.visitInputs(function (input) { - if (isConstant(input)) {{ - return; - }} - if (isValue(input)) { - inputs.push(followProjection(input)); - } - }); - - debugScheduler && writer.writeLn("Inputs: [" + inputs.map(toID) + "], length: " + inputs.length); - - for (var i = 0; i < inputs.length; i++) { - var input = inputs[i]; - if (isNotPhi(input) && !isScheduled(input)) { - schedule(input); - } - } - - if (node.control) { - if (node instanceof End || node instanceof Phi || node instanceof Start || isScheduled(node)) { - - } else { - append(node); - } - } else { - if (inputs.length) { - var x = inputs[0].control; - for (var i = 1; i < inputs.length; i++) { - var y = inputs[i].control; - if (x.block.dominatorDepth < y.block.dominatorDepth) { - x = y; - } - } - scheduleIn(node, x); - } else { - scheduleIn(node, cfg.root.region); - } - } - - debugScheduler && writer.leave("<"); - } - - debugScheduler && writer.leave("<"); - - roots.forEach(function (node) { - node = followProjection(node); - if (node === dfg.start || node instanceof Region) { - return; - } - release || assert (node.control, "Node is not scheduled: " + node); - }); - } - - trace (writer: IndentingWriter) { - var visited = []; - var blocks = []; - - function next(block) { - if (!visited[block.id]) { - visited[block.id] = true; - blocks.push(block); - block.visitSuccessors(next); - } - } - - var root = this.root; - var exit = this.exit; - - next(root); - - function colorOf(block) { - return "black"; - } - - function styleOf(block) { - return "filled"; - } - - function shapeOf(block) { - release || assert (block); - if (block === root) { - return "house"; - } else if (block === exit) { - return "invhouse"; - } - return "box"; - } - - writer.writeLn(""); - writer.enter("digraph CFG {"); - - writer.writeLn("graph [bgcolor = gray10];"); - writer.writeLn("edge [fontname = Consolas, fontsize = 11, color = white, fontcolor = white];"); - writer.writeLn("node [shape = box, fontname = Consolas, fontsize = 11, color = white, fontcolor = white, style = filled];"); - writer.writeLn("rankdir = TB;"); - - blocks.forEach(function (block) { - var loopInfo = ""; - var blockInfo = ""; - var intervalInfo = ""; - // if (block.dominatorDepth !== undefined) { - // blockInfo = " D" + block.dominatorDepth; - // } - if (block.loops !== undefined) { - // loopInfo = "loop: " + block.loops + ", nest: " + bitCount(block.loops); - // loopInfo = " L" + bitCount(block.loops); - } - if (block.name !== undefined) { - blockInfo += " " + block.name; - } - if (block.rpo !== undefined) { - blockInfo += " O: " + block.rpo; - } - writer.writeLn("B" + block.id + " [label = \"B" + block.id + blockInfo + loopInfo + "\", fillcolor = \"" + colorOf(block) + "\", shape=" + shapeOf(block) + ", style=" + styleOf(block) + "];"); - }); - - blocks.forEach(function (block) { - block.visitSuccessors(function (successor) { - writer.writeLn("B" + block.id + " -> " + "B" + successor.id); - }); - if (block.dominator) { - writer.writeLn("B" + block.id + " -> " + "B" + block.dominator.id + " [color = orange];"); - } - if (block.follow) { - writer.writeLn("B" + block.id + " -> " + "B" + block.follow.id + " [color = purple];"); - } - }); - - writer.leave("}"); - writer.writeLn(""); - } - } - - /** - * Peephole optimizations: - */ - export class PeepholeOptimizer { - foldUnary(node) { - release || assert (node instanceof Unary); - if (isConstant(node.argument)) { - return new Constant(node.operator.evaluate(node.argument.value)); - } - var argument = this.fold(node.argument); - if (node.operator === Operator.TRUE) { - return argument; - } - if (argument instanceof Unary) { - if (node.operator === Operator.FALSE && argument.operator === Operator.FALSE) { - return argument.argument; - } - } else { - return new Unary(node.operator, argument); - } - return node; - } - foldBinary(node) { - release || assert (node instanceof Binary); - if (isConstant(node.left) && isConstant(node.right)) { - return new Constant(node.operator.evaluate(node.left.value, node.right.value)); - } - return node; - } - fold(node) { - if (node instanceof Unary) { - return this.foldUnary(node); - } else if (node instanceof Binary) { - return this.foldBinary(node); - } - return node; - } - } -} diff --git a/jit/compiler.ts b/jit/compiler.ts index 101fb0d8..9e0f23d7 100644 --- a/jit/compiler.ts +++ b/jit/compiler.ts @@ -179,8 +179,8 @@ module J2ME { } if (emitter.definitions) { - emitFields(classInfo.fields, false); - emitFields(classInfo.fields, true); + emitFields(classInfo.getFields(), false); + emitFields(classInfo.getFields(), true); return; } @@ -192,15 +192,15 @@ module J2ME { // writer.writeLn("this._hashCode = $.nextHashCode(this);"); writer.writeLn("this._hashCode = 0;"); getClassInheritanceChain(classInfo).forEach(function (ci) { - emitFields(ci.fields, false); + emitFields(ci.getFields(), false); }); writer.leave("}"); // Emit class static initializer if it has any static fields. We don't emit this for now // since it probably doesn't pay off to emit code that only gets executed once. - if (false && classInfo.fields.some(f => f.isStatic)) { + if (false && classInfo.getFields().some(f => f.isStatic)) { writer.enter(mangledClassName + ".staticInitializer = function() {"); - emitFields(classInfo.fields, true); + emitFields(classInfo.getFields(), true); writer.leave("}"); } @@ -268,7 +268,7 @@ module J2ME { emitKlass(emitter, classInfo); - var methods = classInfo.methods; + var methods = classInfo.getMethods(); var compiledMethods: CompiledMethodInfo [] = []; for (var i = 0; i < methods.length; i++) { var method = methods[i]; @@ -447,7 +447,7 @@ module J2ME { var filteredClassInfoList: ClassInfo [] = []; for (var i = 0; i < orderedClassInfoList.length; i++) { var classInfo = orderedClassInfoList[i]; - var methods = classInfo.methods; + var methods = classInfo.getMethods(); for (var j = 0; j < methods.length; j++) { var method = methods[j]; if (methodFilterList === null || methodFilterList.indexOf(method.implKey) >= 0) { diff --git a/jsshell.js b/jsshell.js index d322e318..e1fbf50e 100755 --- a/jsshell.js +++ b/jsshell.js @@ -13,6 +13,14 @@ console.print = function (c) { putstr(String.fromCharCode(c)); }; +console.info = function (c) { + putstr(String.fromCharCode(c)); +}; + +console.error = function (c) { + putstr(String.fromCharCode(c)); +}; + function check() { } @@ -91,9 +99,9 @@ var config = { }; try { - load("libs/relooper.js", "build/j2me.js","libs/zipfile.js", "blackBox.js", + load("build/shumway.js", "libs/relooper.js", "build/j2me.js","libs/zipfile.js", "blackBox.js", "libs/encoding.js", "util.js", - "override.js", "vm/tags.js", "native.js", "tests/override.js", + "override.js", "vm/tags.js", "native.js", "tests/override.js", "midp/midp.js", "midp/gestures.js", "libs/long.js", "midp/crypto.js", "libs/forge/md5.js", "libs/forge/util.js", "build/classes.jar.js"); @@ -111,21 +119,26 @@ try { CLASSES.addPath("java/classes.jar", snarf("java/classes.jar", "binary").buffer); CLASSES.addPath("tests/tests.jar", snarf("tests/tests.jar", "binary").buffer); CLASSES.addPath("bench/benchmark.jar", snarf("bench/benchmark.jar", "binary").buffer); - //CLASSES.addPath("program.jar", snarf("program.jar", "binary").buffer); + CLASSES.addPath("program.jar", snarf("program.jar", "binary").buffer); CLASSES.initializeBuiltinClasses(); var start = dateNow(); var jvm = new JVM(); - J2ME.writers = J2ME.WriterFlags.None; - start = dateNow(); var runtime = jvm.startIsolate0(scriptArgs[0], config.args); - while (callbacks.length) { - (callbacks.shift())(); - } + //J2ME.writers = J2ME.WriterFlags.All; + //J2ME.loadWriter = new J2ME.IndentingWriter(); + //J2ME.linkWriter = new J2ME.IndentingWriter(); + //J2ME.classCounter.clear(); + //CLASSES.loadAllClassFilesInJARFile("program.jar"); + //J2ME.classCounter.trace(J2ME.loadWriter); + // + ////while (callbacks.length) { + //// (callbacks.shift())(); + ////} // J2ME.interpreterCounter.traceSorted(new J2ME.IndentingWriter()); diff --git a/parser.ts b/parser.ts index 8a68763b..b4eb2444 100644 --- a/parser.ts +++ b/parser.ts @@ -1,8 +1,30 @@ -module J2ME.Parser { +module J2ME { declare var util; import assert = J2ME.Debug.assert; - export class ByteStream { - u8: Uint8Array; + var utf8ToString = util.decodeUtf8Array; + + module UTF8 { + export var Code = new Uint8Array([67, 111, 100, 101]); + export var InnerClasses = new Uint8Array([67, 111, 100, 101]); + } + + function strcmp(a: Uint8Array, b: Uint8Array): boolean { + if (a === b) { + return true; + } + if (a.length !== b.length) { + return false; + } + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; + } + + export class Bytes { + u1: Uint8Array; offset: number; static arrays: string [][] = ArrayUtilities.makeArrays(128); @@ -11,39 +33,47 @@ module J2ME.Parser { return Reader.arrays[length]; } - constructor(buffer: ArrayBuffer, offset: number = 0) { - this.u8 = new Uint8Array(buffer); + constructor(buffer: Uint8Array, offset: number) { + this.u1 = buffer; this.offset = offset; } - readU8() { - return this.u8[this.offset++]; + clone() { + return new Bytes(this.u1, this.offset); } - readU16() { - var u8 = this.u8; + readU1() { + return this.u1[this.offset++]; + } + + peekU1() { + return this.u1[this.offset]; + } + + readU2() { + var u1 = this.u1; var o = this.offset; this.offset += 2; - return u8[o] << 8 | u8[o + 1]; + return u1[o] << 8 | u1[o + 1]; } peekU16() { - var u8 = this.u8; + var u1 = this.u1; var o = this.offset; - return u8[o] << 8 | u8[o + 1]; + return u1[o] << 8 | u1[o + 1]; } - readU32() { + readU4() { return this.readI32() >>> 0; } readI32() { var o = this.offset; - var u8 = this.u8; - var a = u8[o + 0]; - var b = u8[o + 1]; - var c = u8[o + 2]; - var d = u8[o + 3]; + var u1 = this.u1; + var a = u1[o + 0]; + var b = u1[o + 1]; + var c = u1[o + 2]; + var d = u1[o + 3]; this.offset = o + 4; return (a << 24) | (b << 16) | (c << 8) | d; } @@ -60,8 +90,14 @@ module J2ME.Parser { // return data; //} - skip(length: number) { + seek(offset: number): Bytes { + this.offset = offset; + return this; + } + + skip(length: number): Bytes { this.offset += length; + return this; } // Decode Java's modified UTF-8 (JVM specs, $ 4.4.7) @@ -71,9 +107,9 @@ module J2ME.Parser { var i = 0, j = 0; var o = this.offset; var e = o + length; - var u8 = this.u8; + var u1 = this.u1; while (o < e) { - var x = u8[o++]; + var x = u1[o++]; if (x <= 0x7f) { // Code points in the range '\u0001' to '\u007F' are represented by a // single byte. @@ -83,13 +119,13 @@ module J2ME.Parser { } else if (x <= 0xdf) { // The null code point ('\u0000') and code points in the range '\u0080' // to '\u07FF' are represented by a pair of bytes x and y. - var y = u8[o++] + var y = u1[o++] a[j++] = String.fromCharCode(((x & 0x1f) << 6) + (y & 0x3f)); } else { // Code points in the range '\u0800' to '\uFFFF' are represented by 3 // bytes x, y, and z. - var y = u8[o++]; - var z = u8[o++]; + var y = u1[o++]; + var z = u1[o++]; a[j++] = String.fromCharCode(((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)); } } @@ -106,7 +142,7 @@ module J2ME.Parser { readString(length) { if (length === 1) { - var c = this.u8[this.offset]; + var c = this.u1[this.offset]; if (c <= 0x7f) { this.offset++; return String.fromCharCode(c); @@ -123,7 +159,7 @@ module J2ME.Parser { // UTF-8 implementation. try { // NB: no need to create a new slice. - var data = new Uint8Array(this.u8.buffer, this.offset, length); + var data = new Uint8Array(this.u1.buffer, this.offset, length); var s = util.decodeUtf8Array(data); this.offset += length; return s; @@ -132,34 +168,76 @@ module J2ME.Parser { } } - readBytes(length) { - var data = this.u8.buffer.slice(this.offset, this.offset + length); + readBytes(length): Uint8Array { + var data = this.u1.subarray(this.offset, this.offset + length); this.offset += length; return data; } - //static readU32(u8: Uint8Array, o: number): number { - // return Bytes.readI32(u8, o) >>> 0; + //static readU4(u1: Uint8Array, o: number): number { + // return Bytes.readI32(u1, o) >>> 0; //} // - //static readI32(u8: Uint8Array, o: number): number { - // var a = u8[o + 0]; - // var b = u8[o + 1]; - // var c = u8[o + 2]; - // var d = u8[o + 3]; + //static readI32(u1: Uint8Array, o: number): number { + // var a = u1[o + 0]; + // var b = u1[o + 1]; + // var c = u1[o + 2]; + // var d = u1[o + 3]; // return (a << 24) | (b << 16) | (c << 8) | d; //} // - static readU16(u8: Uint8Array, o: number): number { - return u8[o] << 8 | u8[o + 1]; + static readU16(u1: Uint8Array, o: number): number { + return u1[o] << 8 | u1[o + 1]; } } + export class ExceptionHandler { + start_pc: number; + end_pc: number; + handler_pc: number; + catch_type: number; + } + + export enum ACCESS_FLAGS { + ACC_PUBLIC = 0x0001, + ACC_PRIVATE = 0x0002, + ACC_PROTECTED = 0x0004, + ACC_STATIC = 0x0008, + ACC_FINAL = 0x0010, + ACC_SYNCHRONIZED = 0x0020, + ACC_VOLATILE = 0x0040, + ACC_TRANSIENT = 0x0080, + ACC_NATIVE = 0x0100, + ACC_INTERFACE = 0x0200, + ACC_ABSTRACT = 0x0400 + } + + export enum TAGS { + CONSTANT_Class = 7, + CONSTANT_Fieldref = 9, + CONSTANT_Methodref = 10, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_String = 8, + CONSTANT_Integer = 3, + CONSTANT_Float = 4, + CONSTANT_Long = 5, + CONSTANT_Double = 6, + CONSTANT_NameAndType = 12, + CONSTANT_Utf8 = 1, + CONSTANT_Unicode = 2 + } + + export enum CONSTANT_Class_info { + tag = 0, // u1 + name_index = 1 // u2 CONSTANT_Utf8_info + } + export class ConstantPool { - stream: ByteStream; + bytes: Bytes; offset: number; count: number; entries: Uint32Array; + resolved: any []; private static tagSize = new Int8Array([ -1, // ? @@ -177,9 +255,9 @@ module J2ME.Parser { 4 // CONSTANT_NameAndType ]); - constructor(stream: ByteStream) { - this.stream = stream; - this.offset = stream.offset; + constructor(bytes: Bytes) { + this.bytes = bytes.clone(); + this.offset = bytes.offset; this.scanEntries(); } @@ -187,18 +265,19 @@ module J2ME.Parser { * Quickly scan over the constant pool and record the position of each constant pool entry. */ private scanEntries() { - var s = this.stream; - var c = this.count = s.readU16(); + var s = this.bytes; + var c = this.count = s.readU2(); this.entries = new Uint32Array(this.count); + this.resolved = new Array(this.count); var S = ConstantPool.tagSize; var o = s.offset; - var u8 = s.u8; + var u1 = s.u1; var e = this.entries; for (var i = 1; i < c; i++) { e[i] = o; - var t = u8[o++]; + var t = u1[o++]; if (t === TAGS.CONSTANT_Utf8) { - o += 2 + ByteStream.readU16(u8, o); + o += 2 + Bytes.readU16(u1, o); } else { o += S[t]; } @@ -208,100 +287,627 @@ module J2ME.Parser { } s.offset = o; } + + utf8(i: number): Uint8Array { + return this.resolve(i, TAGS.CONSTANT_Utf8); + } + + u2(i: number, tag: TAGS, offset: number) { + var s = this.bytes.seek(this.entries[i]); + release || assert (s.peekU1() === tag); + return s.skip(offset).readU2(); + } + + seek(i: number, tag: TAGS) { + this.bytes.seek(this.entries[i]); + release || assert(this.bytes.peekU1() === tag); + } + + resolve(i: number, tag: TAGS): any { + var b = this.bytes, r = this.resolved[i]; + if (r === undefined) { + this.seek(i, tag); + switch (b.readU1()) { + case TAGS.CONSTANT_Utf8: + r = this.resolved[i] = b.readBytes(b.readU2()); + break; + case TAGS.CONSTANT_Class: + r = this.resolved[i] = CLASSES.loadClass(utf8ToString(this.resolve(b.readU2(), TAGS.CONSTANT_Utf8))); + break; + default: + assert(false); + break; + } + } + return r; + } + + resolveClass(index: number): ClassInfo { + return this.resolve(index, TAGS.CONSTANT_Class); + } } export class FieldInfo { + offset: number; + classInfo: ClassInfo; + kind: Kind; + name: string; + mangledName: string; + signature: string; + access_flags: ACCESS_FLAGS; + constructor(classInfo: ClassInfo, offset: number) { + this.offset = offset; + this.classInfo = classInfo; + } + + get isStatic(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_STATIC); + } + + get(object: java.lang.Object) { + return object[this.mangledName]; + } + + set(object: java.lang.Object, value: any) { + object[this.mangledName] = value + } + + getStatic() { + return this.get(this.classInfo.getStaticObject($.ctx)); + } + + setStatic(value: any) { + return this.set(this.classInfo.getStaticObject($.ctx), value); + } + } + + export class SourceLocation { + constructor(public className: string, + public sourceFile: string, + public lineNumber: number) { + // ... + } + toString() { + return this.sourceFile + ":" + this.lineNumber; + } + equals(other: SourceLocation): boolean { + if (!other) { + return false; + } + return this.sourceFile === other.sourceFile && + this.lineNumber === other.lineNumber; + } } export class MethodInfo { + access_flags: number; + name_index: number; + descriptor_index: number; + + fn: any; + + classInfo: ClassInfo; offset: number; - name: Uint16Array; + code: Uint8Array; + codeAttribute: CodeAttribute; + + name: string; + state: MethodState; + signature: string; + mangledName: string; + mangledClassAndMethodName: string; + + onStackReplacementEntryPoints: number []; + + callCount: number; + bytecodeCount: number; + backwardsBranchCount: number; + interpreterCallCount: number; + + argumentSlots: number; + /** + * The number of arguments to pop of the stack when calling this function. + */ + consumeArgumentSlots: number; + + hasTwoSlotArguments: boolean; + + // Remove these + max_locals: number; + max_stack: number; + + exception_table: any []; + implKey: string; + isOptimized: boolean; + signatureDescriptor: SignatureDescriptor; + constructor(classInfo: ClassInfo, offset: number) { this.classInfo = classInfo; this.offset = offset; + + var b = classInfo.bytes.seek(offset); + this.access_flags = b.readU2(); + this.name_index = b.readU2(); + this.descriptor_index = b.readU2(); + this.scanMethodInfoAttributes(b); + } + + scanMethodInfoAttributes(s: Bytes) { + var count = s.readU2(); + for (var i = 0; i < count; i++) { + var attribute_name_index = s.readU2(); + var attribute_length = s.readU4(); + var o = s.offset; + var attribute_name = this.classInfo.constantPool.utf8(attribute_name_index); + if (strcmp(attribute_name, UTF8.Code)) { + this.codeAttribute = new CodeAttribute(this.classInfo, o); + } + s.seek(o + attribute_length); + } + } + + public getReturnKind(): Kind { + return this.signatureDescriptor.typeDescriptors[0].kind; + } + + get isNative(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_NATIVE); + } + + get isFinal(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_FINAL); + } + + get isPublic(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_PUBLIC); + } + + get isStatic(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_STATIC); + } + + get isSynchronized(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_SYNCHRONIZED); + } + + get isAbstract(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_ABSTRACT); + } + + getSourceLocationForPC(pc: number): SourceLocation { + return null; + } + + + } + + enum ResolvedFlags { + None = 0, + Fields = 1, + Methods = 2, + Classes = 4, + Interfaces = 8 + } + + export class CodeAttribute { + max_stack: number; + max_locals: number; + code: Uint8Array; + constructor(classInfo: ClassInfo, offset: number) { + var s = classInfo.bytes; + this.max_stack = s.readU2(); + this.max_locals = s.readU2(); + var code_length = s.readU4(); + this.code = s.readBytes(code_length); + var exception_table_length = s.readU2(); } } export class ClassInfo { - stream: ByteStream; - magic: number; - minor_version: number; - major_version: number; - constantPool: ConstantPool; + bytes: Bytes = null; + constantPool: ConstantPool = null; - access_flags: number; - this_class: number; - super_class: number; - fields: number | FieldInfo []; - methods: number | MethodInfo []; - interfaces: number | ClassInfo []; + access_flags: number = 0; + this_class: number = 0; + super_class: number= 0; - constructor(stream: ByteStream) { - var s = this.stream = stream; + name: string = null; + superClassName: string = null; + superClass: ClassInfo = null; + subClasses: ClassInfo [] = null; + allSubClasses: ClassInfo [] = null; - this.magic = s.readU32(); - this.minor_version = s.readU16(); - this.major_version = s.readU16(); - this.constantPool = new ConstantPool(s); - this.access_flags = s.readU16(); - this.this_class = s.readU16(); - this.super_class = s.readU16(); + staticInitializer: MethodInfo = null; + + klass: Klass = null; + private resolvedFlags: ResolvedFlags = ResolvedFlags.None; + private fields: (number | FieldInfo) [] = null; + private methods: (number | MethodInfo) [] = null; + private classes: (number | ClassInfo) [] = null; + private interfaces: (number | ClassInfo) [] = null; + + sourceFile: string = null; + mangledName: string = null; + + private _className: Uint16Array = null; + + // Delete me: + constant_pool: any = null; + thread: any = null; + + constructor(buffer: Uint8Array) { + if (!buffer) { + return; + } + + var b = this.bytes = new Bytes(buffer, 0); + b.readU4(); // magic + b.readU2(); // minor_version + b.readU2(); // major_version + this.constantPool = new ConstantPool(b); + b.seek(this.constantPool.bytes.offset); + this.access_flags = b.readU2(); + this.this_class = b.readU2(); + this.super_class = b.readU2(); this.scanInterfaces(); this.scanFields(); this.scanMethods(); - this.skipAttributes(); + this.scanClassInfoAttributes(); + + this.mangledName = mangleClass(this); + this.getMethods(); } private scanInterfaces() { - var s = this.stream; - var count = s.readU16(); - this.interfaces = new Array(count); - for (var i = 0; i < count; i++) { - this.interfaces[i] = s.offset; - s.readU16(); + var b = this.bytes; + var interfaces_count = b.readU2(); + this.interfaces = new Array(interfaces_count); + for (var i = 0; i < interfaces_count; i++) { + this.interfaces[i] = b.readU2(); } } private scanFields() { - var s = this.stream; - var count = s.readU16(); - var f = this.fields = new Array(count); - for (var i = 0; i < count; i++) { - f[i] = s.offset; - s.skip(6); + var b = this.bytes; + var fields_count = b.readU2(); + var f = this.fields = new Array(fields_count); + for (var i = 0; i < fields_count; i++) { + f[i] = b.offset; + b.skip(6); this.skipAttributes(); } } private scanMethods() { - var s = this.stream; - var count = s.readU16(); - var m = this.methods = new Array(count); - for (var i = 0; i < count; i++) { - m[i] = s.offset; - s.skip(6); + var b = this.bytes; + var methods_count = b.readU2(); + var m = this.methods = new Array(methods_count); + for (var i = 0; i < methods_count; i++) { + m[i] = b.offset; + b.skip(6); this.skipAttributes(); } } private skipAttributes() { - var s = this.stream; - var count = s.readU16(); - for (var i = 0; i < count; i++) { - s.readU16(); - s.skip(s.readU32()); + var b = this.bytes; + var attributes_count = b.readU2(); + for (var i = 0; i < attributes_count; i++) { + b.readU2(); + b.skip(b.readU4()); } } - getMethodInfo(i: number): MethodInfo { - if (typeof this.methods[i] === "number") { - this.methods[i] = new MethodInfo(this, this.methods[i]); + scanClassInfoAttributes() { + var b = this.bytes; + var attributes_count = b.readU2(); + for (var i = 0; i < attributes_count; i++) { + var attribute_name_index = b.readU2(); + var attribute_length = b.readU4(); + var o = b.offset; + var attribute_name = this.constantPool.utf8(attribute_name_index); + if (strcmp(attribute_name, UTF8.InnerClasses)) { + var number_of_classes = b.readU2(); + assert (!this.classes); + this.classes = new Array(number_of_classes); + for(var i = 0; i < number_of_classes; i++) { + this.classes[i] = b.offset; + b.skip(8); + } + } + b.seek(o + attribute_length); } - return this.methods[i]; + } + + getMethod(i: number): MethodInfo { + if (typeof this.methods[i] === "number") { + this.methods[i] = new MethodInfo(this, this.methods[i]); + } + return this.methods[i]; + } + + getMethods(): MethodInfo [] { + if (!this.methods) { + return null; + } + if (this.resolvedFlags & ResolvedFlags.Methods) { + return this.methods; + } + for (var i = 0; i < this.methods.length; i++) { + this.getMethod(i); + } + this.resolvedFlags |= ResolvedFlags.Methods; + return this.methods; + } + + getField(i: number): FieldInfo { + if (typeof this.fields[i] === "number") { + this.fields[i] = new FieldInfo(this, this.fields[i]); + } + return this.fields[i]; + } + + getFields(): FieldInfo [] { + if (!this.fields) { + return null; + } + if (this.resolvedFlags & ResolvedFlags.Fields) { + return this.fields; + } + for (var i = 0; i < this.fields.length; i++) { + this.getField(i); + } + this.resolvedFlags |= ResolvedFlags.Fields; + return this.fields; + } + + getClass(i: number): ClassInfo { + if (typeof this.classes[i] === "number") { + this.classes[i] = new ClassInfo(null); + } + return this.classes[i]; + } + + getClasses(): ClassInfo [] { + if (!this.classes) { + return null; + } + if (this.resolvedFlags & ResolvedFlags.Classes) { + return this.classes; + } + for (var i = 0; i < this.classes.length; i++) { + this.getClass(i); + } + this.resolvedFlags |= ResolvedFlags.Classes; + return this.classes; + } + + getInterface(i: number): ClassInfo { + if (typeof this.interfaces[i] === "number") { + this.interfaces[i] = this.constantPool.resolveClass(this.interfaces[i]); + } + return this.interfaces[i]; + } + + getInterfaces(): ClassInfo [] { + if (!this.interfaces) { + return null; + } + if (this.resolvedFlags & ResolvedFlags.Interfaces) { + return this.interfaces; + } + for (var i = 0; i < this.interfaces.length; i++) { + this.getInterface(i); + } + this.resolvedFlags |= ResolvedFlags.Interfaces; + return this.interfaces; + } + + getFieldByName(fieldKey: string): FieldInfo { + // return CLASSES.getField(this, fieldKey); + return null; + } + + /** + * Object that holds static properties for this class. + */ + getStaticObject(ctx: Context): java.lang.Object { + return getRuntimeKlass(ctx.runtime, this.klass); + } + + getClassNameIndex(): number { + return this.constantPool.u2(this.this_class, TAGS.CONSTANT_Class, CONSTANT_Class_info.name_index); + } + + getClassName(): Uint8Array { + return this.constantPool.utf8(this.getClassNameIndex()); + } + + getClassNameString(): string { + return util.decodeUtf8Array(this.getClassName()); + } + + // TODO: Remove + get className(): string { + return this.getClassNameString(); + } + + get isInterface(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_INTERFACE); + } + + get isFinal(): boolean { + return !!(this.access_flags & ACCESS_FLAGS.ACC_FINAL); + } + + implementsInterface(i: ClassInfo): boolean { + var classInfo = this; + do { + var interfaces = classInfo.interfaces; + for (var n = 0; n < interfaces.length; ++n) { + if (interfaces[n] === i) + return true; + } + classInfo = classInfo.superClass; + } while (classInfo); + return false; + } + + isAssignableTo(toClass: ClassInfo): boolean { + if (this === toClass || toClass === CLASSES.java_lang_Object) + return true; + if (toClass.isInterface && this.implementsInterface(toClass)) + return true; + return this.superClass ? this.superClass.isAssignableTo(toClass) : false; + } + + /** + * java.lang.Class object for this class info. This is a not where static properties + * are stored for this class. + */ + getClassObject(): java.lang.Class { + return getRuntimeKlass($, this.klass).classObject; + } + + resolveClass(i: number): ClassInfo { + return null; + } + + resolveMethod(i: number, isStatic: boolean): MethodInfo { + return null; + } + + resolveField(i: number): FieldInfo { + return null; + } + + /** + * Resolves a constant pool reference. + */ + resolve(index: number, isStatic: boolean) { + // return this.constantPool.resolve(index); + + //var rp = this.resolved_constant_pool; + //var constant: any = rp[index]; + //if (constant !== undefined) { + // return constant; + //} + //var cp = this.constant_pool; + //var entry = this.constant_pool[index]; + //switch (entry.tag) { + // case TAGS.CONSTANT_Integer: + // constant = entry.integer; + // break; + // case TAGS.CONSTANT_Float: + // constant = entry.float; + // break; + // case TAGS.CONSTANT_String: + // constant = $.newStringConstant(cp[entry.string_index].bytes); + // break; + // case TAGS.CONSTANT_Long: + // constant = Long.fromBits(entry.lowBits, entry.highBits); + // break; + // case TAGS.CONSTANT_Double: + // constant = entry.double; + // break; + // case TAGS.CONSTANT_Class: + // constant = CLASSES.getClass(cp[entry.name_index].bytes); + // break; + // case TAGS.CONSTANT_Fieldref: + // var classInfo = this.resolve(entry.class_index, isStatic); + // var fieldName = cp[cp[entry.name_and_type_index].name_index].bytes; + // var signature = cp[cp[entry.name_and_type_index].signature_index].bytes; + // constant = CLASSES.getField(classInfo, (isStatic ? "S" : "I") + "." + fieldName + "." + signature); + // if (!constant) { + // throw $.newRuntimeException( + // classInfo.className + "." + fieldName + "." + signature + " not found"); + // } + // break; + // case TAGS.CONSTANT_Methodref: + // case TAGS.CONSTANT_InterfaceMethodref: + // var classInfo = this.resolve(entry.class_index, isStatic); + // var methodName = cp[cp[entry.name_and_type_index].name_index].bytes; + // var signature = cp[cp[entry.name_and_type_index].signature_index].bytes; + // constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature); + // if (!constant) { + // constant = CLASSES.getMethod(classInfo, (isStatic ? "S" : "I") + "." + methodName + "." + signature); + // throw $.newRuntimeException( + // classInfo.className + "." + methodName + "." + signature + " not found"); + // } + // break; + // default: + // throw new Error("not support constant type"); + //} + //return rp[index] = constant; } } + + export class PrimitiveClassInfo extends ClassInfo { + private primitiveClassName: string; + + constructor(className, mangledName) { + super(null); + this.primitiveClassName = className; + this.mangledName = mangledName; + } + + getClassNameString(): string { + return this.primitiveClassName; + } + + static Z = new PrimitiveClassInfo("Z", "boolean"); + static C = new PrimitiveClassInfo("C", "char"); + static F = new PrimitiveClassInfo("F", "float"); + static D = new PrimitiveClassInfo("D", "double"); + static B = new PrimitiveClassInfo("B", "byte"); + static S = new PrimitiveClassInfo("S", "short"); + static I = new PrimitiveClassInfo("I", "int"); + static J = new PrimitiveClassInfo("J", "long"); + } + + export class ArrayClassInfo extends ClassInfo { + elementClass: ClassInfo; + + constructor(elementClass: ClassInfo) { + super(null); + this.elementClass = elementClass; + } + + getClassNameString(): string { + return "[" + this.elementClass.getClassNameString(); + } + + isAssignableTo(toClass: ClassInfo): boolean { + if (this === toClass || toClass === CLASSES.java_lang_Object) + return true; + if (toClass.isInterface && this.implementsInterface(toClass)) + return true; + if (toClass instanceof ArrayClassInfo) { + if (this.elementClass && toClass.elementClass) + return this.elementClass.isAssignableTo(toClass.elementClass); + } else { + return false; + } + return this.superClass ? this.superClass.isAssignableTo(toClass) : false; + } + } + + export class PrimitiveArrayClassInfo extends ArrayClassInfo { + constructor(elementClass: ClassInfo, mangledName: string) { + super(elementClass); + this.mangledName = mangledName; + } + static Z = new PrimitiveArrayClassInfo(PrimitiveClassInfo.Z, "Uint8Array"); + static C = new PrimitiveArrayClassInfo(PrimitiveClassInfo.C, "Uint16Array"); + static F = new PrimitiveArrayClassInfo(PrimitiveClassInfo.F, "Float32Array"); + static D = new PrimitiveArrayClassInfo(PrimitiveClassInfo.D, "Float64Array"); + static B = new PrimitiveArrayClassInfo(PrimitiveClassInfo.B, "Int8Array"); + static S = new PrimitiveArrayClassInfo(PrimitiveClassInfo.S, "Int16Array"); + static I = new PrimitiveArrayClassInfo(PrimitiveClassInfo.I, "Int32Array"); + static J = new PrimitiveArrayClassInfo(PrimitiveClassInfo.J, "Int64Array"); + } } \ No newline at end of file diff --git a/references-jsc.ts b/references-jsc.ts index d5c051b5..2f3d1d85 100644 --- a/references-jsc.ts +++ b/references-jsc.ts @@ -22,13 +22,6 @@ // JIT /// -/// -/// -/// -/// -/// -/// /// -/// /// /// \ No newline at end of file diff --git a/vm/classRegistry.ts b/vm/classRegistry.ts index e7c2d4bf..c34c76d1 100644 --- a/vm/classRegistry.ts +++ b/vm/classRegistry.ts @@ -1,6 +1,7 @@ module J2ME { declare var ZipFile; declare var snarf; + export var classCounter = new Metrics.Counter(true); export class ClassRegistry { /** @@ -153,8 +154,9 @@ module J2ME { loadClassBytes(bytes: ArrayBuffer): ClassInfo { enterTimeline("loadClassBytes"); - var classInfo = new ClassInfo(bytes); + var classInfo = new ClassInfo(new Uint8Array(bytes)); leaveTimeline("loadClassBytes", {className: classInfo.className}); + loadWriter && loadWriter.writeLn("XXX: " + classInfo.className + " -> " + classInfo.superClassName + ";"); this.classes[classInfo.className] = classInfo; return classInfo; } @@ -177,11 +179,12 @@ module J2ME { superClass = superClass.superClass; } } - var classes = classInfo.classes; - classes.forEach(function (c, n) { - classes[n] = self.loadClass(c); - }); - classInfo.complete(); + var classes = classInfo.getClasses(); + // FIXME + //classes.forEach(function (c, n) { + // classes[n] = self.loadClass(c); + //}); + // classInfo.complete(); loadWriter && loadWriter.leave("<"); return classInfo; } @@ -191,19 +194,27 @@ module J2ME { */ loadAllClassFiles() { var jarFiles = this.jarFiles; + var self = this; Object.keys(jarFiles).every(function (path) { if (path.substr(-4) !== ".jar") { return true; } - 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); + self.loadAllClassFilesInJARFile(path); + }); + } + + loadAllClassFilesInJARFile(path: string) { + var jarFiles = this.jarFiles; + var zipFile = jarFiles[path]; + var self = this; + Object.keys(zipFile.directory).every(function (fileName) { + if (fileName.substr(-6) !== '.class') { return true; - }); + } + var className = fileName.substring(0, fileName.length - 6); + var bytes = self.loadFile(className + ".class"); + var classInfo2 = new ClassInfo(new Uint8Array(bytes)); + return true; }); } @@ -222,8 +233,8 @@ module J2ME { } getEntryPoint(classInfo: ClassInfo): MethodInfo { - var methods = classInfo.methods; - for (var i=0; inew Klasses.java.lang.Class(); runtimeKlass.classObject.runtimeKlass = runtimeKlass; - var fields = runtimeKlass.templateKlass.classInfo.fields; + var fields = runtimeKlass.templateKlass.classInfo.getFields(); + if (!fields) { + return; + } for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (field.isStatic) { @@ -1031,7 +1034,7 @@ module J2ME { registerKlass(klass, classInfo); leaveTimeline("registerKlass"); - if (classInfo.isArrayClass) { + if (classInfo instanceof ArrayClassInfo) { klass.isArrayKlass = true; var elementKlass = getKlass(classInfo.elementClass); elementKlass.arrayKlass = klass; @@ -1059,7 +1062,7 @@ module J2ME { return "[Interface Klass " + classInfo.className + "]"; }; setKlassSymbol(mangledName, klass); - } else if (classInfo.isArrayClass) { + } else if (classInfo instanceof ArrayClassInfo) { var elementKlass = getKlass(classInfo.elementClass); // Have we already created one? We need to maintain pointer identity. if (elementKlass.arrayKlass) { @@ -1306,7 +1309,7 @@ module J2ME { */ function linkKlassFields(klass: Klass) { var classInfo = klass.classInfo; - var fields = classInfo.fields; + var fields = classInfo.getFields(); var classBindings = Bindings[klass.classInfo.className]; if (classBindings && classBindings.fields) { for (var i = 0; i < fields.length; i++) { @@ -1338,8 +1341,11 @@ module J2ME { } function linkKlassMethods(klass: Klass) { + var methods = klass.classInfo.getMethods(); + if (!methods) { + return; + } linkWriter && linkWriter.enter("Link Klass Methods: " + klass); - var methods = klass.classInfo.methods; for (var i = 0; i < methods.length; i++) { var methodInfo = methods[i]; if (methodInfo.isAbstract) { @@ -1468,9 +1474,11 @@ module J2ME { release || assert (!klass.interfaces); var interfaces = klass.interfaces = klass.superKlass ? klass.superKlass.interfaces.slice() : []; - var interfaceClassInfos = classInfo.interfaces; - for (var j = 0; j < interfaceClassInfos.length; j++) { - ArrayUtilities.pushUnique(interfaces, getKlass(interfaceClassInfos[j])); + var interfaceClassInfos = classInfo.getInterfaces(); + if (interfaceClassInfos) { + for (var j = 0; j < interfaceClassInfos.length; j++) { + ArrayUtilities.pushUnique(interfaces, getKlass(interfaceClassInfos[j])); + } } } @@ -1844,7 +1852,7 @@ module J2ME { } export function classInitCheck(classInfo: ClassInfo, pc: number) { - if (classInfo.isArrayClass) { + if (classInfo instanceof ArrayClassInfo) { return; } $.ctx.pushClassInitFrame(classInfo);