Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(310)

Unified Diff: pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart

Issue 1198293002: dart2js: Use an abstract Name class for names in the generated JavaScript ast. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: fix tests Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart b/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
index 201a369d717df3cb931a2a8b1f67da996eb09f4c..3303d965cd3840efe105c84af9971ef3205bd475 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
@@ -25,7 +25,7 @@ class NsmEmitter extends CodeEmitterHelper {
// Keep track of the JavaScript names we've already added so we
// do not introduce duplicates (bad for code size).
- Map<String, Selector> addedJsNames
+ Map<jsAst.Name, Selector> addedJsNames
= generator.computeSelectorsForNsmHandlers();
// Set flag used by generateMethod helper below. If we have very few
@@ -33,7 +33,9 @@ class NsmEmitter extends CodeEmitterHelper {
// them at runtime.
bool haveVeryFewNoSuchMemberHandlers =
(addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS);
- for (String jsName in addedJsNames.keys.toList()..sort()) {
+ List<jsAst.Name> names = addedJsNames.keys.toList()
+ ..sort();
+ for (jsAst.Name jsName in names) {
Selector selector = addedJsNames[jsName];
String reflectionName = emitter.getReflectionName(selector, jsName);
@@ -56,7 +58,8 @@ class NsmEmitter extends CodeEmitterHelper {
if (reflectionName != null) {
bool accessible = compiler.world.allFunctions.filter(selector).any(
(Element e) => backend.isAccessibleByReflection(e));
- addProperty('+$reflectionName', js(accessible ? '2' : '0'));
+ addProperty(namer.asName('+$reflectionName'),
+ js(accessible ? '2' : '0'));
}
}
}
@@ -65,23 +68,13 @@ class NsmEmitter extends CodeEmitterHelper {
// Identify the noSuchMethod handlers that are so simple that we can
// generate them programatically.
bool isTrivialNsmHandler(
- int type, List argNames, Selector selector, String internalName) {
+ int type, List argNames, Selector selector, jsAst.Name internalName) {
if (!generateTrivialNsmHandlers) return false;
- // Check for interceptor calling convention.
- if (backend.isInterceptedName(selector.name)) {
- // We can handle the calling convention used by intercepted names in the
- // diff encoding, but we don't use that for non-minified code.
- if (!compiler.enableMinification) return false;
- String shortName = namer.invocationMirrorInternalName(selector);
- if (shortName.length > MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) {
- return false;
- }
- }
// Check for named arguments.
if (argNames.length != 0) return false;
// Check for unexpected name (this doesn't really happen).
- if (internalName.startsWith(namer.getterPrefix[0])) return type == 1;
- if (internalName.startsWith(namer.setterPrefix[0])) return type == 2;
+ if (internalName is GetterName) return type == 1;
+ if (internalName is SetterName) return type == 2;
return type == 0;
}
@@ -127,91 +120,29 @@ class NsmEmitter extends CodeEmitterHelper {
List<jsAst.Statement> buildTrivialNsmHandlers() {
List<jsAst.Statement> statements = <jsAst.Statement>[];
if (trivialNsmHandlers.length == 0) return statements;
- // Sort by calling convention, JS name length and by JS name.
- trivialNsmHandlers.sort((a, b) {
- bool aIsIntercepted = backend.isInterceptedName(a.name);
- bool bIsIntercepted = backend.isInterceptedName(b.name);
- if (aIsIntercepted != bIsIntercepted) return aIsIntercepted ? -1 : 1;
- String aName = namer.invocationMirrorInternalName(a);
- String bName = namer.invocationMirrorInternalName(b);
- if (aName.length != bName.length) return aName.length - bName.length;
- return aName.compareTo(bName);
- });
// Find out how many selectors there are with the special calling
// convention.
- int firstNormalSelector = trivialNsmHandlers.length;
- for (int i = 0; i < trivialNsmHandlers.length; i++) {
- if (!backend.isInterceptedName(trivialNsmHandlers[i].name)) {
- firstNormalSelector = i;
- break;
- }
+ bool hasSpecialCallingConvention(Selector selector) {
+ return backend.isInterceptedName(selector.name);
}
+ Iterable<Selector> specialSelectors = trivialNsmHandlers.where(
+ (Selector s) => backend.isInterceptedName(s.name));
+ Iterable<Selector> ordinarySelectors = trivialNsmHandlers.where(
+ (Selector s) => !backend.isInterceptedName(s.name));
// Get the short names (JS names, perhaps minified).
- Iterable<String> shorts = trivialNsmHandlers.map((selector) =>
- namer.invocationMirrorInternalName(selector));
- var diffEncoding = new StringBuffer();
-
- // Treat string as a number in base 88 with digits in ASCII order from # to
- // z. The short name sorting is based on length, and uses ASCII order for
- // equal length strings so this means that names are ascending. The hash
- // character, #, is never given as input, but we need it because it's the
- // implicit leading zero (otherwise we could not code names with leading
- // dollar signs).
- int fromBase88(String x) {
- int answer = 0;
- for (int i = 0; i < x.length; i++) {
- int c = x.codeUnitAt(i);
- // No support for Unicode minified identifiers in JS.
- assert(c >= $$ && c <= $z);
- answer *= 88;
- answer += c - $HASH;
- }
- return answer;
- }
-
- // Big endian encoding, A = 0, B = 1...
- // A lower case letter terminates the number.
- String toBase26(int x) {
- int c = x;
- var encodingChars = <int>[];
- encodingChars.add($a + (c % 26));
- while (true) {
- c ~/= 26;
- if (c == 0) break;
- encodingChars.add($A + (c % 26));
- }
- return new String.fromCharCodes(encodingChars.reversed.toList());
- }
+ Iterable<jsAst.Name> specialShorts =
+ specialSelectors.map(namer.invocationMirrorInternalName);
+ Iterable<jsAst.Name> ordinaryShorts =
+ ordinarySelectors.map(namer.invocationMirrorInternalName);
bool minify = compiler.enableMinification;
- bool useDiffEncoding = minify && shorts.length > 30;
+ bool useDiffEncoding = minify && trivialNsmHandlers.length > 30;
- int previous = 0;
- int nameCounter = 0;
- for (String short in shorts) {
- // Emit period that resets the diff base to zero when we switch to normal
- // calling convention (this avoids the need to code negative diffs).
- if (useDiffEncoding && nameCounter == firstNormalSelector) {
- diffEncoding.write(".");
- previous = 0;
- }
- if (short.length <= MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING &&
- useDiffEncoding) {
- int base63 = fromBase88(short);
- int diff = base63 - previous;
- previous = base63;
- String base26Diff = toBase26(diff);
- diffEncoding.write(base26Diff);
- } else {
- if (useDiffEncoding || diffEncoding.length != 0) {
- diffEncoding.write(",");
- }
- diffEncoding.write(short);
- }
- nameCounter++;
- }
+ jsAst.Expression diffEncoding = new _DiffEncodedListOfNames(
+ [specialShorts, ordinaryShorts],
+ useDiffEncoding);
// Startup code that loops over the method names and puts handlers on the
// Object class to catch noSuchMethod invocations.
@@ -221,25 +152,24 @@ class NsmEmitter extends CodeEmitterHelper {
if (useDiffEncoding) {
statements.add(js.statement('''{
var objectClassObject = processedClasses.collected[#objectClass],
- shortNames = #diffEncoding.split(","),
- nameNumber = 0,
- diffEncodedString = shortNames[0],
- calculatedShortNames = [0, 1]; // 0, 1 are args for splice.
- // If we are loading a deferred library the object class will not be in
- // the collectedClasses so objectClassObject is undefined, and we skip
- // setting up the names.
-
- if (objectClassObject) {
- if (objectClassObject instanceof Array)
+ nameSequences = #diffEncoding.split("."),
+ shortNames = [];
+ if (objectClassObject instanceof Array)
objectClassObject = objectClassObject[1];
+ for (var j = 0; j < nameSequences.length; ++j) {
+ var sequence = nameSequences[j].split(","),
+ nameNumber = 0;
+ // If we are loading a deferred library the object class will not be
+ // in the collectedClasses so objectClassObject is undefined, and we
+ // skip setting up the names.
+ if (!objectClassObject) break;
+ // Likewise, if the current sequence is empty, we don't process it.
+ if (sequence.length == 0) continue;
+ var diffEncodedString = sequence[0];
for (var i = 0; i < diffEncodedString.length; i++) {
var codes = [],
diff = 0,
digit = diffEncodedString.charCodeAt(i);
- if (digit == ${$PERIOD}) {
- nameNumber = 0;
- digit = diffEncodedString.charCodeAt(++i);
- }
for (; digit <= ${$Z};) {
diff *= 26;
diff += (digit - ${$A});
@@ -253,13 +183,15 @@ class NsmEmitter extends CodeEmitterHelper {
remaining = (remaining / 88) | 0) {
codes.unshift(${$HASH} + remaining % 88);
}
- calculatedShortNames.push(
+ shortNames.push(
String.fromCharCode.apply(String, codes));
}
- shortNames.splice.apply(shortNames, calculatedShortNames);
+ if (sequence.length > 1) {
+ Array.prototype.push.apply(shortNames, sequence.shift());
+ }
}
- }''', {'objectClass': js.string(namer.className(objectClass)),
- 'diffEncoding': js.string('$diffEncoding')}));
+ }''', {'objectClass': js.quoteName(namer.className(objectClass)),
+ 'diffEncoding': diffEncoding}));
} else {
// No useDiffEncoding version.
Iterable<String> longs = trivialNsmHandlers.map((selector) =>
@@ -267,8 +199,8 @@ class NsmEmitter extends CodeEmitterHelper {
statements.add(js.statement(
'var objectClassObject = processedClasses.collected[#objectClass],'
' shortNames = #diffEncoding.split(",")',
- {'objectClass': js.string(namer.className(objectClass)),
- 'diffEncoding': js.string('$diffEncoding')}));
+ {'objectClass': js.quoteName(namer.className(objectClass)),
+ 'diffEncoding': diffEncoding}));
if (!minify) {
statements.add(js.statement('var longNames = #longs.split(",")',
{'longs': js.string(longs.join(','))}));
@@ -279,11 +211,11 @@ class NsmEmitter extends CodeEmitterHelper {
}
dynamic isIntercepted = // jsAst.Expression or bool.
- firstNormalSelector == 0
+ specialSelectors.isEmpty
? false
- : firstNormalSelector == shorts.length
+ : ordinarySelectors.isEmpty
? true
- : js('j < #', js.number(firstNormalSelector));
+ : js('j < #', js.number(specialSelectors.length));
statements.add(js.statement('''
// If we are loading a deferred library the object class will not be in
@@ -341,3 +273,114 @@ class NsmEmitter extends CodeEmitterHelper {
return statements;
}
}
+
+/// When pretty printed, this node computes a diff-encoded string for the list
+/// of given names.
+///
+/// See [buildTrivialNsmHandlers].
+class _DiffEncodedListOfNames extends jsAst.DeferredString
+ implements AstContainer {
+ String _cachedValue;
+ jsAst.ArrayInitializer ast;
+ bool useDiffEncoding;
+
+ _DiffEncodedListOfNames(Iterable<Iterable<jsAst.Name>> names,
+ this.useDiffEncoding) {
+ // Store the names in ArrayInitializer nodes to make them discoverable
+ // by traversals of the ast.
+ ast = new jsAst.ArrayInitializer(
+ names.map((Iterable i) => new jsAst.ArrayInitializer(i.toList()))
+ .toList());
+ }
+
+ void _computeDiffEncodingForList(Iterable<jsAst.Name> names,
+ StringBuffer diffEncoding) {
+ // Treat string as a number in base 88 with digits in ASCII order from # to
+ // z. The short name sorting is based on length, and uses ASCII order for
+ // equal length strings so this means that names are ascending. The hash
+ // character, #, is never given as input, but we need it because it's the
+ // implicit leading zero (otherwise we could not code names with leading
+ // dollar signs).
+ int fromBase88(String x) {
+ int answer = 0;
+ for (int i = 0; i < x.length; i++) {
+ int c = x.codeUnitAt(i);
+ // No support for Unicode minified identifiers in JS.
+ assert(c >= $$ && c <= $z);
+ answer *= 88;
+ answer += c - $HASH;
+ }
+ return answer;
+ }
+
+ // Big endian encoding, A = 0, B = 1...
+ // A lower case letter terminates the number.
+ String toBase26(int x) {
+ int c = x;
+ var encodingChars = <int>[];
+ encodingChars.add($a + (c % 26));
+ while (true) {
+ c ~/= 26;
+ if (c == 0) break;
+ encodingChars.add($A + (c % 26));
+ }
+ return new String.fromCharCodes(encodingChars.reversed.toList());
+ }
+
+ // Sort by length, then lexicographic.
+ int compare(String a, String b) {
+ if (a.length != b.length) return a.length - b.length;
+ return a.compareTo(b);
+ }
+
+ List<String> shorts =
+ names.map((jsAst.Name name) => name.name)
+ .toList()
+ ..sort(compare);
+
+ int previous = 0;
+ for (String short in shorts) {
+ if (useDiffEncoding &&
+ short.length <= NsmEmitter.MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) {
+ int base63 = fromBase88(short);
+ int diff = base63 - previous;
+ previous = base63;
+ String base26Diff = toBase26(diff);
+ diffEncoding.write(base26Diff);
+ } else {
+ if (useDiffEncoding || diffEncoding.length != 0) {
+ diffEncoding.write(',');
+ }
+ diffEncoding.write(short);
+ }
+ }
+ }
+
+ String _computeDiffEncoding() {
+ StringBuffer buffer = new StringBuffer();
+ for (jsAst.ArrayInitializer list in ast.elements) {
+ if (buffer.isNotEmpty) {
+ if (useDiffEncoding) {
+ // Emit period that resets the diff base to zero when we switch to
+ // normal calling convention (this avoids the need to code negative
+ // diffs).
+ buffer.write(".");
+ } else {
+ // Write a separator for the next sequence.
+ buffer.write(",");
+ }
+ }
+ List<jsAst.Name> names = list.elements;
+ _computeDiffEncodingForList(names, buffer);
+ }
+ return '"${buffer.toString()}"';
+ }
+
+ String get value {
+ if (_cachedValue == null) {
+ _cachedValue = _computeDiffEncoding();
+ }
+
+ return _cachedValue;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698