Clean up name mangling and use a better string hashing function.

This commit is contained in:
Michael Bebenita 2015-01-22 01:43:49 -08:00
Родитель bd748126d7
Коммит d0d6c9e472
5 изменённых файлов: 111 добавлений и 73 удалений

Просмотреть файл

@ -287,6 +287,7 @@ module J2ME {
static createFromObject(object) { static createFromObject(object) {
var classInfo = Object.create(ClassInfo.prototype, object); var classInfo = Object.create(ClassInfo.prototype, object);
classInfo.resolved_constant_pool = new Array(classInfo.constant_pool.length); classInfo.resolved_constant_pool = new Array(classInfo.constant_pool.length);
classInfo.mangledName = mangleClass(classInfo);
return classInfo; return classInfo;
} }
@ -389,16 +390,6 @@ module J2ME {
} }
private _mangleFields() { private _mangleFields() {
if (false) {
// Safe mangling that includes className, fieldName and signature.
var fields = this.fields;
for (var j = 0; j < fields.length; j++) {
var fieldInfo = fields[j];
fieldInfo.mangledName = "$" + escapeString(fieldInfo.classInfo.className + "_" + fieldInfo.name + "_" + fieldInfo.signature);
}
return;
}
// Keep track of how many times a field name was used and resolve conflicts by // Keep track of how many times a field name was used and resolve conflicts by
// prefixing filed names with numbers. // prefixing filed names with numbers.
var classInfo: ClassInfo; var classInfo: ClassInfo;
@ -546,11 +537,11 @@ module J2ME {
} }
export class ArrayClassInfo extends ClassInfo { export class ArrayClassInfo extends ClassInfo {
constructor(className: string, elementClass?) { constructor(className: string, elementClass: ClassInfo, mangledName?: string) {
false && super(null); false && super(null);
this.className = className; this.className = className;
// TODO this may need to change for compiled code. // TODO this may need to change for compiled code.
this.mangledName = className; this.mangledName = mangledName;
this.superClass = CLASSES.java_lang_Object; this.superClass = CLASSES.java_lang_Object;
this.superClassName = "java/lang/Object"; this.superClassName = "java/lang/Object";
this.access_flags = 0; this.access_flags = 0;
@ -589,22 +580,22 @@ module J2ME {
PrimitiveClassInfo.prototype.interfaces = []; PrimitiveClassInfo.prototype.interfaces = [];
export class PrimitiveArrayClassInfo extends ArrayClassInfo { export class PrimitiveArrayClassInfo extends ArrayClassInfo {
constructor(className: string, elementClass?) { constructor(className: string, elementClass: ClassInfo, mangledName: string) {
super(className, elementClass); super(className, elementClass, mangledName);
} }
get superClass() { get superClass() {
return CLASSES.java_lang_Object; return CLASSES.java_lang_Object;
} }
static Z = new PrimitiveArrayClassInfo("[Z", PrimitiveClassInfo.Z); static Z = new PrimitiveArrayClassInfo("[Z", PrimitiveClassInfo.Z, "Uint8Array");
static C = new PrimitiveArrayClassInfo("[C", PrimitiveClassInfo.C); static C = new PrimitiveArrayClassInfo("[C", PrimitiveClassInfo.C, "Uint16Array");
static F = new PrimitiveArrayClassInfo("[F", PrimitiveClassInfo.F); static F = new PrimitiveArrayClassInfo("[F", PrimitiveClassInfo.F, "Float32Array");
static D = new PrimitiveArrayClassInfo("[D", PrimitiveClassInfo.D); static D = new PrimitiveArrayClassInfo("[D", PrimitiveClassInfo.D, "Float64Array");
static B = new PrimitiveArrayClassInfo("[B", PrimitiveClassInfo.B); static B = new PrimitiveArrayClassInfo("[B", PrimitiveClassInfo.B, "Int8Array");
static S = new PrimitiveArrayClassInfo("[S", PrimitiveClassInfo.S); static S = new PrimitiveArrayClassInfo("[S", PrimitiveClassInfo.S, "Int16Array");
static I = new PrimitiveArrayClassInfo("[I", PrimitiveClassInfo.I); static I = new PrimitiveArrayClassInfo("[I", PrimitiveClassInfo.I, "Int32Array");
static J = new PrimitiveArrayClassInfo("[J", PrimitiveClassInfo.J); static J = new PrimitiveArrayClassInfo("[J", PrimitiveClassInfo.J, "Int64Array");
} }
PrimitiveClassInfo.prototype.fields = []; PrimitiveClassInfo.prototype.fields = [];

Просмотреть файл

@ -169,6 +169,17 @@ module J2ME {
return "Long.fromInt(" + v + ")"; return "Long.fromInt(" + v + ")";
} }
function classConstant(classInfo: ClassInfo): string {
if (classInfo.mangledName) {
return classInfo.mangledName;
}
if (classInfo.isArrayClass) {
return "$AK(" + classConstant(classInfo.elementClass) + ")";
}
release || assert(classInfo.mangledName);
return classInfo.mangledName;
}
export class BaselineCompiler { export class BaselineCompiler {
sp: number; sp: number;
pc: number; pc: number;
@ -308,7 +319,7 @@ module J2ME {
if (classInfo.isInterface) { if (classInfo.isInterface) {
check = "$IOI"; check = "$IOI";
} }
check += "(" + this.peek(Kind.Reference) + ", " + mangleClass(classInfo) + ")"; check += "(" + this.peek(Kind.Reference) + ", " + classConstant(classInfo) + ")";
check = " && " + check; check = " && " + check;
} }
this.emitter.enter("if (pc >= " + handler.start_pc + " && pc < " + handler.end_pc + check + ") {"); this.emitter.enter("if (pc >= " + handler.start_pc + " && pc < " + handler.end_pc + check + ") {");
@ -591,11 +602,11 @@ module J2ME {
} }
runtimeClass(classInfo: ClassInfo) { runtimeClass(classInfo: ClassInfo) {
return "$." + mangleClass(classInfo); return "$." + classConstant(classInfo);
} }
runtimeClassObject(classInfo: ClassInfo) { runtimeClassObject(classInfo: ClassInfo) {
return "$." + mangleClass(classInfo) + ".classObject"; return "$." + classConstant(classInfo) + ".classObject";
} }
emitClassInitializationCheck(classInfo: ClassInfo) { emitClassInitializationCheck(classInfo: ClassInfo) {
@ -650,12 +661,12 @@ module J2ME {
object = this.pop(Kind.Reference); object = this.pop(Kind.Reference);
if (opcode === Bytecodes.INVOKESPECIAL) { if (opcode === Bytecodes.INVOKESPECIAL) {
args.unshift(object); args.unshift(object);
call = mangleClassAndMethod(methodInfo) + ".call(" + args.join(", ") + ")"; call = methodInfo.mangledClassAndMethodName + ".call(" + args.join(", ") + ")";
} else { } else {
call = object + "." + mangleMethod(methodInfo) + "(" + args.join(", ") + ")"; call = object + "." + methodInfo.mangledName + "(" + args.join(", ") + ")";
} }
} else { } else {
call = mangleClassAndMethod(methodInfo) + "(" + args.join(", ") + ")"; call = methodInfo.mangledClassAndMethodName + "(" + args.join(", ") + ")";
} }
if (methodInfo.implKey in inlineMethods) { if (methodInfo.implKey in inlineMethods) {
emitDebugInfoComments && this.emitter.writeLn("// Inlining: " + methodInfo.implKey); emitDebugInfoComments && this.emitter.writeLn("// Inlining: " + methodInfo.implKey);
@ -733,7 +744,7 @@ module J2ME {
emitNewInstance(cpi: number) { emitNewInstance(cpi: number) {
var classInfo = this.lookupClass(cpi); var classInfo = this.lookupClass(cpi);
this.emitClassInitializationCheck(classInfo); this.emitClassInitializationCheck(classInfo);
this.emitPush(Kind.Reference, "new " + mangleClass(classInfo)+ "()"); this.emitPush(Kind.Reference, "new " + classConstant(classInfo)+ "()");
} }
emitNewTypeArray(typeCode: number) { emitNewTypeArray(typeCode: number) {
@ -749,7 +760,7 @@ module J2ME {
if (classInfo.isInterface) { if (classInfo.isInterface) {
call = "$CCI"; call = "$CCI";
} }
this.emitter.writeLn(call + "(" + object + ", " + mangleClass(classInfo) + ");"); this.emitter.writeLn(call + "(" + object + ", " + classConstant(classInfo) + ");");
} }
emitInstanceOf(cpi: number) { emitInstanceOf(cpi: number) {
@ -759,7 +770,7 @@ module J2ME {
if (classInfo.isInterface) { if (classInfo.isInterface) {
call = "$IOI"; call = "$IOI";
} }
this.emitPush(Kind.Int, call + "(" + object + ", " + mangleClass(classInfo) + ") | 0"); this.emitPush(Kind.Int, call + "(" + object + ", " + classConstant(classInfo) + ") | 0");
} }
emitArrayLength() { emitArrayLength() {
@ -770,7 +781,7 @@ module J2ME {
var classInfo = this.lookupClass(cpi); var classInfo = this.lookupClass(cpi);
this.emitClassInitializationCheck(classInfo); this.emitClassInitializationCheck(classInfo);
var length = this.pop(Kind.Int); var length = this.pop(Kind.Int);
this.emitPush(Kind.Reference, "$NA(" + mangleClass(classInfo) + ", " + length + ")"); this.emitPush(Kind.Reference, "$NA(" + classConstant(classInfo) + ", " + length + ")");
} }
emitUnwind(pc: number, nextPC: number) { emitUnwind(pc: number, nextPC: number) {

Просмотреть файл

@ -142,7 +142,7 @@ module J2ME {
export function emitKlass(emitter: Emitter, classInfo: ClassInfo) { export function emitKlass(emitter: Emitter, classInfo: ClassInfo) {
var writer = emitter.writer; var writer = emitter.writer;
var mangledClassName = mangleClass(classInfo); var mangledClassName = classInfo.mangledName;
if (emitter.closure) { if (emitter.closure) {
writer.writeLn("/** @constructor */"); writer.writeLn("/** @constructor */");
} }
@ -227,7 +227,7 @@ module J2ME {
}); });
} }
var mangledClassName = mangleClass(classInfo); var mangledClassName = classInfo.mangledName;
emitter.writer.writeLn(mangledClassName + ".classSymbols = [" + referencedClasses.map(classInfo => { emitter.writer.writeLn(mangledClassName + ".classSymbols = [" + referencedClasses.map(classInfo => {
return quote(classInfo.className); return quote(classInfo.className);
@ -240,7 +240,7 @@ module J2ME {
methodFilter: (methodInfo: MethodInfo) => boolean, methodFilter: (methodInfo: MethodInfo) => boolean,
ctx: Context): CompiledMethodInfo [] { ctx: Context): CompiledMethodInfo [] {
var writer = emitter.writer; var writer = emitter.writer;
var mangledClassName = mangleClass(classInfo); var mangledClassName = classInfo.mangledName;
if (!isIdentifierName(mangledClassName)) { if (!isIdentifierName(mangledClassName)) {
mangledClassName = quote(mangledClassName); mangledClassName = quote(mangledClassName);
} }
@ -276,7 +276,7 @@ module J2ME {
if (!methodFilter(method)) { if (!methodFilter(method)) {
continue; continue;
} }
var mangledMethodName = mangleMethod(method); var mangledMethodName = method.mangledName;
if (!isIdentifierName(mangledMethodName)) { if (!isIdentifierName(mangledMethodName)) {
mangledMethodName = quote(mangledMethodName); mangledMethodName = quote(mangledMethodName);
} }
@ -285,7 +285,7 @@ module J2ME {
continue; continue;
} }
try { try {
var mangledClassAndMethodName = mangleClassAndMethod(method); var mangledClassAndMethodName = method.mangledClassAndMethodName;
if (emitter.debugInfo) { if (emitter.debugInfo) {
writer.writeLn("// " + method.implKey + " (" + mangledClassAndMethodName + ") " + method.getSourceLocationForPC(0)); writer.writeLn("// " + method.implKey + " (" + mangledClassAndMethodName + ") " + method.getSourceLocationForPC(0));
} }

Просмотреть файл

@ -226,6 +226,8 @@ module J2ME {
declare var util; declare var util;
import assert = J2ME.Debug.assert; import assert = J2ME.Debug.assert;
import concat3 = StringUtilities.concat3;
import concat5 = StringUtilities.concat5;
export enum RuntimeStatus { export enum RuntimeStatus {
New = 1, New = 1,
@ -240,7 +242,10 @@ module J2ME {
Compiled Compiled
} }
var hashMap = Object.create(null);
var hashArray = new Int32Array(1024); var hashArray = new Int32Array(1024);
function hashString(s: string) { function hashString(s: string) {
if (hashArray.length < s.length) { if (hashArray.length < s.length) {
hashArray = new Int32Array((hashArray.length * 2 / 3) | 0); hashArray = new Int32Array((hashArray.length * 2 / 3) | 0);
@ -249,10 +254,18 @@ module J2ME {
for (var i = 0; i < s.length; i++) { for (var i = 0; i < s.length; i++) {
data[i] = s.charCodeAt(i); data[i] = s.charCodeAt(i);
} }
return HashUtilities.hashBytesTo32BitsAdler(data, 0, s.length); var hash = HashUtilities.hashBytesTo32BitsMurmur(data, 0, s.length);
}
if (!release) { // Check to see that no collisions have ever happened.
if (hashMap[hash] && hashMap[hash] !== s) {
assert(false, "This is very bad.")
}
hashMap[hash] = s;
}
var friendlyMangledNames = true; var friendlyMangledNames = true;
return hash;
}
function isIdentifierChar(c: number): boolean { function isIdentifierChar(c: number): boolean {
@ -318,58 +331,55 @@ module J2ME {
var stringHashes = Object.create(null); var stringHashes = Object.create(null);
var stringHashCount = 0; var stringHashCount = 0;
function hashStringStrong(s): string {
// Hash with Murmur hash.
var result = StringUtilities.variableLengthEncodeInt32(hashString(s));
// Also use the length for some more precision.
result += StringUtilities.toEncoding(s.length & 0x3f);
return result;
}
export function hashStringToString(s: string) { export function hashStringToString(s: string) {
if (stringHashCount > 1024) { if (stringHashCount > 1024) {
return StringUtilities.variableLengthEncodeInt32(hashString(s)); return hashStringStrong(s);
} }
var c = stringHashes[s]; var c = stringHashes[s];
if (c) { if (c) {
return c; return c;
} }
c = stringHashes[s] = StringUtilities.variableLengthEncodeInt32(hashString(s)); c = stringHashes[s] = hashStringStrong(s);
stringHashCount ++; stringHashCount ++;
return c; return c;
} }
export function mangleClassAndMethod(methodInfo: MethodInfo) { export function mangleClassAndMethod(methodInfo: MethodInfo) {
var name = methodInfo.classInfo.className + "_" + methodInfo.name + "_" + hashStringToString(methodInfo.signature); var name = concat5(methodInfo.classInfo.className, "_", methodInfo.name, "_", hashStringToString(methodInfo.signature));
if (friendlyMangledNames) { if (friendlyMangledNames) {
return escapeString(name); return escapeString(name);
} }
var hash = hashString(name); return hashStringToString(name);
return StringUtilities.variableLengthEncodeInt32(hash);
} }
export function mangleMethod(methodInfo: MethodInfo) { export function mangleMethod(methodInfo: MethodInfo) {
var name = methodInfo.name + "_" + hashStringToString(methodInfo.signature); var name = concat3(methodInfo.name, "_", hashStringToString(methodInfo.signature));
if (friendlyMangledNames) { if (friendlyMangledNames) {
return escapeString(name); return escapeString(name);
} }
var hash = hashString(name); return "$" + hashStringToString(name);
return StringUtilities.variableLengthEncodeInt32(hash); }
export function mangleClassName(name: string): string {
if (friendlyMangledNames) {
return "$" + escapeString(name);
}
return "$" + hashStringToString(name);
} }
export function mangleClass(classInfo: ClassInfo) { export function mangleClass(classInfo: ClassInfo) {
if (classInfo instanceof PrimitiveArrayClassInfo) { if (classInfo.mangledName) {
switch (classInfo) { return classInfo.mangledName;
case PrimitiveArrayClassInfo.Z: return "Uint8Array";
case PrimitiveArrayClassInfo.C: return "Uint16Array";
case PrimitiveArrayClassInfo.F: return "Float32Array";
case PrimitiveArrayClassInfo.D: return "Float64Array";
case PrimitiveArrayClassInfo.B: return "Int8Array";
case PrimitiveArrayClassInfo.S: return "Int16Array";
case PrimitiveArrayClassInfo.I: return "Int32Array";
case PrimitiveArrayClassInfo.J: return "Int64Array";
}
} else if (classInfo.isArrayClass) {
return "$AK(" + mangleClass(classInfo.elementClass) + ")";
} else {
if (friendlyMangledNames) {
return "$" + escapeString(classInfo.className);
}
var hash = hashString(classInfo.className);
return "$" + StringUtilities.variableLengthEncodeInt32(hash);
} }
return mangleClassName(classInfo.className);
} }
/** /**
@ -892,7 +902,7 @@ module J2ME {
export function registerKlassSymbol(className: string) { export function registerKlassSymbol(className: string) {
// TODO: This needs to be kept in sync to how mangleClass works. // TODO: This needs to be kept in sync to how mangleClass works.
var mangledName = "$" + escapeString(className); var mangledName = mangleClassName(className);
if (RuntimeTemplate.prototype.hasOwnProperty(mangledName)) { if (RuntimeTemplate.prototype.hasOwnProperty(mangledName)) {
return; return;
} }
@ -1025,7 +1035,7 @@ module J2ME {
function makeKlassConstructor(classInfo: ClassInfo): Klass { function makeKlassConstructor(classInfo: ClassInfo): Klass {
var klass: Klass; var klass: Klass;
var mangledName = mangleClass(classInfo); var mangledName = classInfo.mangledName;
if (classInfo.isInterface) { if (classInfo.isInterface) {
klass = <Klass><any>function () { klass = <Klass><any>function () {
Debug.unexpected("Should never be instantiated.") Debug.unexpected("Should never be instantiated.")
@ -1085,7 +1095,7 @@ module J2ME {
return; return;
} }
enterTimeline("linkKlass", {classInfo: classInfo}); enterTimeline("linkKlass", {classInfo: classInfo});
var mangledName = mangleClass(classInfo); var mangledName = classInfo.mangledName;
var klass; var klass;
classInfo.klass = klass = getKlass(classInfo); classInfo.klass = klass = getKlass(classInfo);
classInfo.klass.classInfo = classInfo; classInfo.klass.classInfo = classInfo;
@ -1235,9 +1245,7 @@ module J2ME {
} }
function findCompiledMethod(klass: Klass, methodInfo: MethodInfo): Function { function findCompiledMethod(klass: Klass, methodInfo: MethodInfo): Function {
var name = methodInfo.mangledClassAndMethodName; return jsGlobal[methodInfo.mangledClassAndMethodName];
var method = jsGlobal[name];
return method;
} }
/** /**
@ -1446,7 +1454,7 @@ module J2ME {
return; return;
} }
var mangledClassAndMethodName = mangleClassAndMethod(methodInfo); var mangledClassAndMethodName = methodInfo.mangledClassAndMethodName;
compiledCount ++; compiledCount ++;

Просмотреть файл

@ -965,6 +965,34 @@ module J2ME {
} }
export module HashUtilities { export module HashUtilities {
// https://github.com/garycourt/murmurhash-js
export function hashBytesTo32BitsMurmur(data: Uint8Array, offset: number, length: number) {
var l = length, h = 0x12345678 ^ l, i = offset, k;
while (l >= 4) {
k =
(data[i]) |
(data[++i] << 8) |
(data[++i] << 16) |
(data[++i] << 24);
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
k ^= k >>> 24;
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
l -= 4;
++i;
}
switch (l) {
case 3: h ^= data[i + 2] << 16;
case 2: h ^= data[i + 1] << 8;
case 1: h ^= data[i];
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
}
h ^= h >>> 13;
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
h ^= h >>> 15;
return h >>> 0;
}
export function hashBytesTo32BitsAdler(data: Uint8Array, offset: number, length: number): number { export function hashBytesTo32BitsAdler(data: Uint8Array, offset: number, length: number): number {
var a = 1; var a = 1;
var b = 0; var b = 0;