| 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
|
| deleted file mode 100644
|
| index 5d7df35210e3da68bf1c1c7cc827ea5f54809393..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
|
| +++ /dev/null
|
| @@ -1,391 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -part of dart2js.js_emitter;
|
| -
|
| -class NsmEmitter extends CodeEmitterHelper {
|
| - final List<Selector> trivialNsmHandlers = <Selector>[];
|
| -
|
| - /// If this is true then we can generate the noSuchMethod handlers at startup
|
| - /// time, instead of them being emitted as part of the Object class.
|
| - bool get generateTrivialNsmHandlers => true;
|
| -
|
| - // If we need fewer than this many noSuchMethod handlers we can save space by
|
| - // just emitting them in JS, rather than emitting the JS needed to generate
|
| - // them at run time.
|
| - static const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10;
|
| -
|
| - static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4;
|
| -
|
| - void emitNoSuchMethodHandlers(AddPropertyFunction addProperty) {
|
| -
|
| - ClassStubGenerator generator =
|
| - new ClassStubGenerator(compiler, namer, backend);
|
| -
|
| - // Keep track of the JavaScript names we've already added so we
|
| - // do not introduce duplicates (bad for code size).
|
| - Map<jsAst.Name, Selector> addedJsNames
|
| - = generator.computeSelectorsForNsmHandlers();
|
| -
|
| - // Set flag used by generateMethod helper below. If we have very few
|
| - // handlers we use addProperty for them all, rather than try to generate
|
| - // them at runtime.
|
| - bool haveVeryFewNoSuchMemberHandlers =
|
| - (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS);
|
| - List<jsAst.Name> names = addedJsNames.keys.toList()
|
| - ..sort();
|
| - for (jsAst.Name jsName in names) {
|
| - Selector selector = addedJsNames[jsName];
|
| - String reflectionName = emitter.getReflectionName(selector, jsName);
|
| -
|
| - if (reflectionName != null) {
|
| - emitter.mangledFieldNames[jsName] = reflectionName;
|
| - }
|
| -
|
| - List<jsAst.Expression> argNames =
|
| - selector.callStructure.getOrderedNamedArguments().map((String name) =>
|
| - js.string(name)).toList();
|
| - int type = selector.invocationMirrorKind;
|
| - if (!haveVeryFewNoSuchMemberHandlers &&
|
| - isTrivialNsmHandler(type, argNames, selector, jsName) &&
|
| - reflectionName == null) {
|
| - trivialNsmHandlers.add(selector);
|
| - } else {
|
| - StubMethod method =
|
| - generator.generateStubForNoSuchMethod(jsName, selector);
|
| - addProperty(method.name, method.code);
|
| - if (reflectionName != null) {
|
| - bool accessible =
|
| - compiler.world.allFunctions.filter(selector, null).any(
|
| - (Element e) => backend.isAccessibleByReflection(e));
|
| - addProperty(namer.asName('+$reflectionName'),
|
| - js(accessible ? '2' : '0'));
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Identify the noSuchMethod handlers that are so simple that we can
|
| - // generate them programatically.
|
| - bool isTrivialNsmHandler(
|
| - int type, List argNames, Selector selector, jsAst.Name internalName) {
|
| - if (!generateTrivialNsmHandlers) return false;
|
| - // Check for named arguments.
|
| - if (argNames.length != 0) return false;
|
| - // Check for unexpected name (this doesn't really happen).
|
| - if (internalName is GetterName) return type == 1;
|
| - if (internalName is SetterName) return type == 2;
|
| - return type == 0;
|
| - }
|
| -
|
| - /**
|
| - * Adds (at runtime) the handlers to the Object class which catch calls to
|
| - * methods that the object does not have. The handlers create an invocation
|
| - * mirror object.
|
| - *
|
| - * The current version only gives you the minified name when minifying (when
|
| - * not minifying this method is not called).
|
| - *
|
| - * In order to generate the noSuchMethod handlers we only need the minified
|
| - * name of the method. We test the first character of the minified name to
|
| - * determine if it is a getter or a setter, and we use the arguments array at
|
| - * runtime to get the number of arguments and their values. If the method
|
| - * involves named arguments etc. then we don't handle it here, but emit the
|
| - * handler method directly on the Object class.
|
| - *
|
| - * The minified names are mostly 1-4 character names, which we emit in sorted
|
| - * order (primary key is length, secondary ordering is lexicographic). This
|
| - * gives an order like ... dD dI dX da ...
|
| - *
|
| - * Gzip is good with repeated text, but it can't diff-encode, so we do that
|
| - * for it. We encode the minified names in a comma-separated string, but all
|
| - * the 1-4 character names are encoded before the first comma as a series of
|
| - * base 26 numbers. The last digit of each number is lower case, the others
|
| - * are upper case, so 1 is "b" and 26 is "Ba".
|
| - *
|
| - * We think of the minified names as base 88 numbers using the ASCII
|
| - * characters from # to z. The base 26 numbers each encode the delta from
|
| - * the previous minified name to the next. So if there is a minified name
|
| - * called Df and the next is Dh, then they are 2971 and 2973 when thought of
|
| - * as base 88 numbers. The difference is 2, which is "c" in lower-case-
|
| - * terminated base 26.
|
| - *
|
| - * The reason we don't encode long minified names with this method is that
|
| - * decoding the base 88 numbers would overflow JavaScript's puny integers.
|
| - *
|
| - * There are some selectors that have a special calling convention (because
|
| - * they are called with the receiver as the first argument). They need a
|
| - * slightly different noSuchMethod handler, so we handle these first.
|
| - */
|
| - List<jsAst.Statement> buildTrivialNsmHandlers() {
|
| - List<jsAst.Statement> statements = <jsAst.Statement>[];
|
| - if (trivialNsmHandlers.length == 0) return statements;
|
| -
|
| - bool minify = compiler.enableMinification;
|
| - bool useDiffEncoding = minify && trivialNsmHandlers.length > 30;
|
| -
|
| - // Find out how many selectors there are with the special calling
|
| - // convention.
|
| - Iterable<Selector> interceptedSelectors = 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<jsAst.Name> interceptedShorts =
|
| - interceptedSelectors.map(namer.invocationMirrorInternalName);
|
| - Iterable<jsAst.Name> ordinaryShorts =
|
| - ordinarySelectors.map(namer.invocationMirrorInternalName);
|
| -
|
| - jsAst.Expression sortedShorts;
|
| - Iterable<String> sortedLongs;
|
| - if (useDiffEncoding) {
|
| - assert(minify);
|
| - sortedShorts = new _DiffEncodedListOfNames(
|
| - [interceptedShorts, ordinaryShorts]);
|
| - } else {
|
| - Iterable<Selector> sorted =
|
| - [interceptedSelectors, ordinarySelectors].expand((e) => (e));
|
| - sortedShorts = js.concatenateStrings(
|
| - js.joinLiterals(
|
| - sorted.map(namer.invocationMirrorInternalName),
|
| - js.stringPart(",")),
|
| - addQuotes: true);
|
| -
|
| - if (!minify) {
|
| - sortedLongs = sorted.map((selector) =>
|
| - selector.invocationMirrorMemberName);
|
| - }
|
| - }
|
| - // Startup code that loops over the method names and puts handlers on the
|
| - // Object class to catch noSuchMethod invocations.
|
| - ClassElement objectClass = compiler.objectClass;
|
| - jsAst.Expression createInvocationMirror = backend.emitter
|
| - .staticFunctionAccess(backend.getCreateInvocationMirror());
|
| - if (useDiffEncoding) {
|
| - statements.add(js.statement('''{
|
| - var objectClassObject = processedClasses.collected[#objectClass],
|
| - 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);
|
| - for (; digit <= ${$Z};) {
|
| - diff *= 26;
|
| - diff += (digit - ${$A});
|
| - digit = diffEncodedString.charCodeAt(++i);
|
| - }
|
| - diff *= 26;
|
| - diff += (digit - ${$a});
|
| - nameNumber += diff;
|
| - for (var remaining = nameNumber;
|
| - remaining > 0;
|
| - remaining = (remaining / 88) | 0) {
|
| - codes.unshift(${$HASH} + remaining % 88);
|
| - }
|
| - shortNames.push(
|
| - String.fromCharCode.apply(String, codes));
|
| - }
|
| - if (sequence.length > 1) {
|
| - Array.prototype.push.apply(shortNames, sequence.shift());
|
| - }
|
| - }
|
| - }''', {'objectClass': js.quoteName(namer.className(objectClass)),
|
| - 'diffEncoding': sortedShorts}));
|
| - } else {
|
| - // No useDiffEncoding version.
|
| - statements.add(js.statement(
|
| - 'var objectClassObject = processedClasses.collected[#objectClass],'
|
| - ' shortNames = #diffEncoding.split(",")',
|
| - {'objectClass': js.quoteName(namer.className(objectClass)),
|
| - 'diffEncoding': sortedShorts}));
|
| - if (!minify) {
|
| - statements.add(js.statement('var longNames = #longs.split(",")',
|
| - {'longs': js.string(sortedLongs.join(','))}));
|
| - }
|
| - statements.add(js.statement(
|
| - 'if (objectClassObject instanceof Array)'
|
| - ' objectClassObject = objectClassObject[1];'));
|
| - }
|
| -
|
| - dynamic isIntercepted = // jsAst.Expression or bool.
|
| - interceptedSelectors.isEmpty
|
| - ? false
|
| - : ordinarySelectors.isEmpty
|
| - ? true
|
| - : js('j < #', js.number(interceptedSelectors.length));
|
| -
|
| - statements.add(js.statement('''
|
| - // 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) {
|
| - for (var j = 0; j < shortNames.length; j++) {
|
| - var type = 0;
|
| - var shortName = shortNames[j];
|
| - if (shortName[0] == "${namer.getterPrefix[0]}") type = 1;
|
| - if (shortName[0] == "${namer.setterPrefix[0]}") type = 2;
|
| - // Generate call to:
|
| - //
|
| - // createInvocationMirror(String name, internalName, type,
|
| - // arguments, argumentNames)
|
| - //
|
| -
|
| - // This 'if' is either a static choice or dynamic choice depending on
|
| - // [isIntercepted].
|
| - if (#isIntercepted) {
|
| - objectClassObject[shortName] =
|
| - (function(name, shortName, type) {
|
| - return function(receiver) {
|
| - return this.#noSuchMethodName(
|
| - receiver,
|
| - #createInvocationMirror(name, shortName, type,
|
| - // Create proper Array with all arguments except first
|
| - // (receiver).
|
| - Array.prototype.slice.call(arguments, 1),
|
| - []));
|
| - }
|
| - })(#names[j], shortName, type);
|
| - } else {
|
| - objectClassObject[shortName] =
|
| - (function(name, shortName, type) {
|
| - return function() {
|
| - return this.#noSuchMethodName(
|
| - // Object.noSuchMethodName ignores the explicit receiver
|
| - // argument. We could pass anything in place of [this].
|
| - this,
|
| - #createInvocationMirror(name, shortName, type,
|
| - // Create proper Array with all arguments.
|
| - Array.prototype.slice.call(arguments, 0),
|
| - []));
|
| - }
|
| - })(#names[j], shortName, type);
|
| - }
|
| - }
|
| - }''', {
|
| - 'noSuchMethodName': namer.noSuchMethodName,
|
| - 'createInvocationMirror': createInvocationMirror,
|
| - 'names': minify ? 'shortNames' : 'longNames',
|
| - 'isIntercepted': isIntercepted}));
|
| -
|
| - 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 jsAst.AstContainer {
|
| - String _cachedValue;
|
| - List<jsAst.ArrayInitializer> ast;
|
| -
|
| - Iterable<jsAst.Node> get containedNodes => ast;
|
| -
|
| - _DiffEncodedListOfNames(Iterable<Iterable<jsAst.Name>> names) {
|
| - // Store the names in ArrayInitializer nodes to make them discoverable
|
| - // by traversals of the ast.
|
| - ast = 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 (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 (diffEncoding.length != 0) {
|
| - diffEncoding.write(',');
|
| - }
|
| - diffEncoding.write(short);
|
| - }
|
| - }
|
| - }
|
| -
|
| - String _computeDiffEncoding() {
|
| - StringBuffer buffer = new StringBuffer();
|
| - for (jsAst.ArrayInitializer list in ast) {
|
| - if (buffer.isNotEmpty) {
|
| - // 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(".");
|
| - }
|
| - List<jsAst.Name> names = list.elements;
|
| - _computeDiffEncodingForList(names, buffer);
|
| - }
|
| - return '"${buffer.toString()}"';
|
| - }
|
| -
|
| - String get value {
|
| - if (_cachedValue == null) {
|
| - _cachedValue = _computeDiffEncoding();
|
| - }
|
| -
|
| - return _cachedValue;
|
| - }
|
| -}
|
|
|