| Index: pkg/compiler/lib/src/js_backend/namer.dart
|
| diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
|
| deleted file mode 100644
|
| index 4eff34a459a1a27cfae6151df1b119eab7803b15..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/js_backend/namer.dart
|
| +++ /dev/null
|
| @@ -1,1396 +0,0 @@
|
| -// Copyright (c) 2011, 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 js_backend;
|
| -
|
| -/**
|
| - * Assigns JavaScript identifiers to Dart variables, class-names and members.
|
| - */
|
| -class Namer implements ClosureNamer {
|
| -
|
| - static const javaScriptKeywords = const <String>[
|
| - // These are current keywords.
|
| - "break", "delete", "function", "return", "typeof", "case", "do", "if",
|
| - "switch", "var", "catch", "else", "in", "this", "void", "continue",
|
| - "false", "instanceof", "throw", "while", "debugger", "finally", "new",
|
| - "true", "with", "default", "for", "null", "try",
|
| -
|
| - // These are future keywords.
|
| - "abstract", "double", "goto", "native", "static", "boolean", "enum",
|
| - "implements", "package", "super", "byte", "export", "import", "private",
|
| - "synchronized", "char", "extends", "int", "protected", "throws",
|
| - "class", "final", "interface", "public", "transient", "const", "float",
|
| - "long", "short", "volatile"
|
| - ];
|
| -
|
| - static const reservedPropertySymbols =
|
| - const <String>["__proto__", "prototype", "constructor", "call"];
|
| -
|
| - // Symbols that we might be using in our JS snippets.
|
| - static const reservedGlobalSymbols = const <String>[
|
| - // Section references are from Ecma-262
|
| - // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)
|
| -
|
| - // 15.1.1 Value Properties of the Global Object
|
| - "NaN", "Infinity", "undefined",
|
| -
|
| - // 15.1.2 Function Properties of the Global Object
|
| - "eval", "parseInt", "parseFloat", "isNaN", "isFinite",
|
| -
|
| - // 15.1.3 URI Handling Function Properties
|
| - "decodeURI", "decodeURIComponent",
|
| - "encodeURI",
|
| - "encodeURIComponent",
|
| -
|
| - // 15.1.4 Constructor Properties of the Global Object
|
| - "Object", "Function", "Array", "String", "Boolean", "Number", "Date",
|
| - "RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
|
| - "SyntaxError", "TypeError", "URIError",
|
| -
|
| - // 15.1.5 Other Properties of the Global Object
|
| - "Math",
|
| -
|
| - // 10.1.6 Activation Object
|
| - "arguments",
|
| -
|
| - // B.2 Additional Properties (non-normative)
|
| - "escape", "unescape",
|
| -
|
| - // Window props (https://developer.mozilla.org/en/DOM/window)
|
| - "applicationCache", "closed", "Components", "content", "controllers",
|
| - "crypto", "defaultStatus", "dialogArguments", "directories",
|
| - "document", "frameElement", "frames", "fullScreen", "globalStorage",
|
| - "history", "innerHeight", "innerWidth", "length",
|
| - "location", "locationbar", "localStorage", "menubar",
|
| - "mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel",
|
| - "name", "navigator", "opener", "outerHeight", "outerWidth",
|
| - "pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11",
|
| - "returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY",
|
| - "self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar",
|
| - "top", "window",
|
| -
|
| - // Window methods (https://developer.mozilla.org/en/DOM/window)
|
| - "alert", "addEventListener", "atob", "back", "blur", "btoa",
|
| - "captureEvents", "clearInterval", "clearTimeout", "close", "confirm",
|
| - "disableExternalCapture", "dispatchEvent", "dump",
|
| - "enableExternalCapture", "escape", "find", "focus", "forward",
|
| - "GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount",
|
| - "getComputedStyle", "getSelection", "home", "maximize", "minimize",
|
| - "moveBy", "moveTo", "open", "openDialog", "postMessage", "print",
|
| - "prompt", "QueryInterface", "releaseEvents", "removeEventListener",
|
| - "resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy",
|
| - "scrollByLines", "scrollByPages", "scrollTo", "setInterval",
|
| - "setResizeable", "setTimeout", "showModalDialog", "sizeToContent",
|
| - "stop", "uuescape", "updateCommands", "XPCNativeWrapper",
|
| - "XPCSafeJSOjbectWrapper",
|
| -
|
| - // Mozilla Window event handlers, same cite
|
| - "onabort", "onbeforeunload", "onchange", "onclick", "onclose",
|
| - "oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange",
|
| - "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown",
|
| - "onmousemove", "onmouseout", "onmouseover", "onmouseup",
|
| - "onmozorientation", "onpaint", "onreset", "onresize", "onscroll",
|
| - "onselect", "onsubmit", "onunload",
|
| -
|
| - // Safari Web Content Guide
|
| - // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf
|
| - // WebKit Window member data, from WebKit DOM Reference
|
| - // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html)
|
| - "ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart",
|
| - "ongesturestart", "ongesturechange", "ongestureend",
|
| -
|
| - // extra window methods
|
| - "uneval",
|
| -
|
| - // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7,
|
| - // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1
|
| - "getPrototypeOf", "let", "yield",
|
| -
|
| - // "future reserved words"
|
| - "abstract", "int", "short", "boolean", "interface", "static", "byte",
|
| - "long", "char", "final", "native", "synchronized", "float", "package",
|
| - "throws", "goto", "private", "transient", "implements", "protected",
|
| - "volatile", "double", "public",
|
| -
|
| - // IE methods
|
| - // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#)
|
| - "attachEvent", "clientInformation", "clipboardData", "createPopup",
|
| - "dialogHeight", "dialogLeft", "dialogTop", "dialogWidth",
|
| - "onafterprint", "onbeforedeactivate", "onbeforeprint",
|
| - "oncontrolselect", "ondeactivate", "onhelp", "onresizeend",
|
| -
|
| - // Common browser-defined identifiers not defined in ECMAScript
|
| - "event", "external", "Debug", "Enumerator", "Global", "Image",
|
| - "ActiveXObject", "VBArray", "Components",
|
| -
|
| - // Functions commonly defined on Object
|
| - "toString", "getClass", "constructor", "prototype", "valueOf",
|
| -
|
| - // Client-side JavaScript identifiers
|
| - "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient",
|
| - "CanvasPattern", "CanvasRenderingContext2D", "CDATASection",
|
| - "CharacterData", "Comment", "CSS2Properties", "CSSRule",
|
| - "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType",
|
| - "DOMException", "DOMImplementation", "DOMParser", "Element", "Event",
|
| - "ExternalInterface", "FlashPlayer", "Form", "Frame", "History",
|
| - "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image",
|
| - "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType",
|
| - "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin",
|
| - "ProcessingInstruction", "Range", "RangeException", "Screen", "Select",
|
| - "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
|
| - "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
|
| - "XPathException", "XPathResult", "XSLTProcessor",
|
| -
|
| - // These keywords trigger the loading of the java-plugin. For the
|
| - // next-generation plugin, this results in starting a new Java process.
|
| - "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
|
| - "JavaArray", "JavaMember",
|
| - ];
|
| -
|
| - static const reservedGlobalObjectNames = const <String>[
|
| - "A",
|
| - "B",
|
| - "C", // Global object for *C*onstants.
|
| - "D",
|
| - "E",
|
| - "F",
|
| - "G",
|
| - "H", // Global object for internal (*H*elper) libraries.
|
| - // I is used for used for the Isolate function.
|
| - "J", // Global object for the interceptor library.
|
| - "K",
|
| - "L",
|
| - "M",
|
| - "N",
|
| - "O",
|
| - "P", // Global object for other *P*latform libraries.
|
| - "Q",
|
| - "R",
|
| - "S",
|
| - "T",
|
| - "U",
|
| - "V",
|
| - "W", // Global object for *W*eb libraries (dart:html).
|
| - "X",
|
| - "Y",
|
| - "Z",
|
| - ];
|
| -
|
| - static const reservedGlobalHelperFunctions = const <String>[
|
| - "init",
|
| - "Isolate",
|
| - ];
|
| -
|
| - static final userGlobalObjects = new List.from(reservedGlobalObjectNames)
|
| - ..remove('C')
|
| - ..remove('H')
|
| - ..remove('J')
|
| - ..remove('P')
|
| - ..remove('W');
|
| -
|
| - Set<String> _jsReserved = null;
|
| - /// Names that cannot be used by members, top level and static
|
| - /// methods.
|
| - Set<String> get jsReserved {
|
| - if (_jsReserved == null) {
|
| - _jsReserved = new Set<String>();
|
| - _jsReserved.addAll(javaScriptKeywords);
|
| - _jsReserved.addAll(reservedPropertySymbols);
|
| - }
|
| - return _jsReserved;
|
| - }
|
| -
|
| - Set<String> _jsVariableReserved = null;
|
| - /// Names that cannot be used by local variables and parameters.
|
| - Set<String> get jsVariableReserved {
|
| - if (_jsVariableReserved == null) {
|
| - _jsVariableReserved = new Set<String>();
|
| - _jsVariableReserved.addAll(javaScriptKeywords);
|
| - _jsVariableReserved.addAll(reservedPropertySymbols);
|
| - _jsVariableReserved.addAll(reservedGlobalSymbols);
|
| - _jsVariableReserved.addAll(reservedGlobalObjectNames);
|
| - // 26 letters in the alphabet, 25 not counting I.
|
| - assert(reservedGlobalObjectNames.length == 25);
|
| - _jsVariableReserved.addAll(reservedGlobalHelperFunctions);
|
| - }
|
| - return _jsVariableReserved;
|
| - }
|
| -
|
| - final String currentIsolate = r'$';
|
| - final String getterPrefix = r'get$';
|
| - final String setterPrefix = r'set$';
|
| - final String metadataField = '@';
|
| - final String callPrefix = 'call';
|
| - final String callCatchAllName = r'call$catchAll';
|
| - final String reflectableField = r'$reflectable';
|
| - final String defaultValuesField = r'$defaultValues';
|
| - final String methodsWithOptionalArgumentsField =
|
| - r'$methodsWithOptionalArguments';
|
| -
|
| - final String classDescriptorProperty = r'^';
|
| -
|
| - // Name of property in a class description for the native dispatch metadata.
|
| - final String nativeSpecProperty = '%';
|
| -
|
| - static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$');
|
| - static final RegExp NON_IDENTIFIER_CHAR = new RegExp(r'[^A-Za-z_0-9$]');
|
| -
|
| - /**
|
| - * Map from top-level or static elements to their unique identifiers provided
|
| - * by [getName].
|
| - *
|
| - * Invariant: Keys must be declaration elements.
|
| - */
|
| - final Compiler compiler;
|
| - final Map<Element, String> globals;
|
| - final Map<String, LibraryElement> shortPrivateNameOwners;
|
| -
|
| - final Set<String> usedGlobalNames;
|
| - final Set<String> usedInstanceNames;
|
| - final Map<String, String> globalNameMap;
|
| - final Map<String, String> suggestedGlobalNames;
|
| - final Map<String, String> instanceNameMap;
|
| - final Map<String, String> suggestedInstanceNames;
|
| -
|
| - final Map<String, String> operatorNameMap;
|
| - final Map<String, int> popularNameCounters;
|
| -
|
| - final Map<ConstantValue, String> constantNames;
|
| - final Map<ConstantValue, String> constantLongNames;
|
| - ConstantCanonicalHasher constantHasher;
|
| -
|
| - // All alphanumeric characters.
|
| - static const String _alphaNumeric =
|
| - 'abcdefghijklmnopqrstuvwxyzABZDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
| -
|
| - Namer(Compiler compiler)
|
| - : compiler = compiler,
|
| - globals = new Map<Element, String>(),
|
| - shortPrivateNameOwners = new Map<String, LibraryElement>(),
|
| - usedGlobalNames = new Set<String>(),
|
| - usedInstanceNames = new Set<String>(),
|
| - instanceNameMap = new Map<String, String>(),
|
| - operatorNameMap = new Map<String, String>(),
|
| - globalNameMap = new Map<String, String>(),
|
| - suggestedGlobalNames = new Map<String, String>(),
|
| - suggestedInstanceNames = new Map<String, String>(),
|
| - popularNameCounters = new Map<String, int>(),
|
| - constantNames = new Map<ConstantValue, String>(),
|
| - constantLongNames = new Map<ConstantValue, String>(),
|
| - constantHasher = new ConstantCanonicalHasher(compiler),
|
| - functionTypeNamer = new FunctionTypeNamer(compiler);
|
| -
|
| - JavaScriptBackend get backend => compiler.backend;
|
| -
|
| - String get isolateName => 'Isolate';
|
| - String get isolatePropertiesName => r'$isolateProperties';
|
| - /**
|
| - * Some closures must contain their name. The name is stored in
|
| - * [STATIC_CLOSURE_NAME_NAME].
|
| - */
|
| - String get STATIC_CLOSURE_NAME_NAME => r'$name';
|
| - String get closureInvocationSelectorName => Compiler.CALL_OPERATOR_NAME;
|
| - bool get shouldMinify => false;
|
| -
|
| - String getNameForJsGetName(Node node, String name) {
|
| - switch (name) {
|
| - case 'GETTER_PREFIX': return getterPrefix;
|
| - case 'SETTER_PREFIX': return setterPrefix;
|
| - case 'CALL_PREFIX': return callPrefix;
|
| - case 'CALL_CATCH_ALL': return callCatchAllName;
|
| - case 'REFLECTABLE': return reflectableField;
|
| - case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty;
|
| - default:
|
| - compiler.reportError(
|
| - node, MessageKind.GENERIC,
|
| - {'text': 'Error: Namer has no name for "$name".'});
|
| - return 'BROKEN';
|
| - }
|
| - }
|
| -
|
| - String constantName(ConstantValue constant) {
|
| - // In the current implementation it doesn't make sense to give names to
|
| - // function constants since the function-implementation itself serves as
|
| - // constant and can be accessed directly.
|
| - assert(!constant.isFunction);
|
| - String result = constantNames[constant];
|
| - if (result == null) {
|
| - String longName = constantLongName(constant);
|
| - result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames,
|
| - ensureSafe: true);
|
| - constantNames[constant] = result;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - // The long name is unminified and may have collisions.
|
| - String constantLongName(ConstantValue constant) {
|
| - String longName = constantLongNames[constant];
|
| - if (longName == null) {
|
| - longName = new ConstantNamingVisitor(compiler, constantHasher)
|
| - .getName(constant);
|
| - constantLongNames[constant] = longName;
|
| - }
|
| - return longName;
|
| - }
|
| -
|
| - String breakLabelName(LabelDefinition label) {
|
| - return '\$${label.labelName}\$${label.target.nestingLevel}';
|
| - }
|
| -
|
| - String implicitBreakLabelName(JumpTarget target) {
|
| - return '\$${target.nestingLevel}';
|
| - }
|
| -
|
| - // We sometimes handle continue targets differently from break targets,
|
| - // so we have special continue-only labels.
|
| - String continueLabelName(LabelDefinition label) {
|
| - return 'c\$${label.labelName}\$${label.target.nestingLevel}';
|
| - }
|
| -
|
| - String implicitContinueLabelName(JumpTarget target) {
|
| - return 'c\$${target.nestingLevel}';
|
| - }
|
| -
|
| - /**
|
| - * If the [name] is not private returns [:name:]. Otherwise
|
| - * mangles the [name] so that each library has a unique name.
|
| - */
|
| - String privateName(LibraryElement library, String name) {
|
| - // Public names are easy.
|
| - String nameString = name;
|
| - if (!isPrivateName(name)) return nameString;
|
| -
|
| - // The first library asking for a short private name wins.
|
| - LibraryElement owner =
|
| - shortPrivateNameOwners.putIfAbsent(nameString, () => library);
|
| -
|
| - if (owner == library && !nameString.contains('\$')) {
|
| - // Since the name doesn't contain $ it doesn't clash with any
|
| - // of the private names that have the library name as the prefix.
|
| - return nameString;
|
| - } else {
|
| - // Make sure to return a private name that starts with _ so it
|
| - // cannot clash with any public names.
|
| - String libraryName = getNameOfLibrary(library);
|
| - return '_$libraryName\$$nameString';
|
| - }
|
| - }
|
| -
|
| - String instanceMethodName(FunctionElement element) {
|
| - // TODO(ahe): Could this be: return invocationName(new
|
| - // Selector.fromElement(element))?
|
| - String elementName = element.name;
|
| - String name = operatorNameToIdentifier(elementName);
|
| - if (name != elementName) return getMappedOperatorName(name);
|
| -
|
| - LibraryElement library = element.library;
|
| - if (element.isGenerativeConstructorBody) {
|
| - name = Elements.reconstructConstructorNameSourceString(element);
|
| - }
|
| - FunctionSignature signature = element.functionSignature;
|
| - // We don't mangle the closure invoking function name because it
|
| - // is generated by string concatenation in applyFunction from
|
| - // js_helper.dart. To keep code size down, we potentially shorten
|
| - // the prefix though.
|
| - String methodName;
|
| - if (name == closureInvocationSelectorName) {
|
| - methodName = '$callPrefix\$${signature.parameterCount}';
|
| - } else {
|
| - methodName = '${privateName(library, name)}\$${signature.parameterCount}';
|
| - }
|
| - if (signature.optionalParametersAreNamed &&
|
| - !signature.optionalParameters.isEmpty) {
|
| - StringBuffer buffer = new StringBuffer();
|
| - signature.orderedOptionalParameters.forEach((Element element) {
|
| - buffer.write('\$${safeName(element.name)}');
|
| - });
|
| - methodName = '$methodName$buffer';
|
| - }
|
| - if (name == closureInvocationSelectorName) return methodName;
|
| - return getMappedInstanceName(methodName);
|
| - }
|
| -
|
| - String publicInstanceMethodNameByArity(String name, int arity) {
|
| - String newName = operatorNameToIdentifier(name);
|
| - if (newName != name) return getMappedOperatorName(newName);
|
| - assert(!isPrivateName(name));
|
| - // We don't mangle the closure invoking function name because it
|
| - // is generated by string concatenation in applyFunction from
|
| - // js_helper.dart. To keep code size down, we potentially shorten
|
| - // the prefix though.
|
| - if (name == closureInvocationSelectorName) return '$callPrefix\$$arity';
|
| -
|
| - return getMappedInstanceName('$name\$$arity');
|
| - }
|
| -
|
| - String invocationName(Selector selector) {
|
| - if (selector.isGetter) {
|
| - String proposedName = privateName(selector.library, selector.name);
|
| - return '$getterPrefix${getMappedInstanceName(proposedName)}';
|
| - } else if (selector.isSetter) {
|
| - String proposedName = privateName(selector.library, selector.name);
|
| - return '$setterPrefix${getMappedInstanceName(proposedName)}';
|
| - } else {
|
| - String name = selector.name;
|
| - if (selector.kind == SelectorKind.OPERATOR
|
| - || selector.kind == SelectorKind.INDEX) {
|
| - name = operatorNameToIdentifier(name);
|
| - assert(name != selector.name);
|
| - return getMappedOperatorName(name);
|
| - }
|
| - assert(name == operatorNameToIdentifier(name));
|
| - StringBuffer buffer = new StringBuffer();
|
| - for (String argumentName in selector.getOrderedNamedArguments()) {
|
| - buffer.write('\$${safeName(argumentName)}');
|
| - }
|
| - String suffix = '\$${selector.argumentCount}$buffer';
|
| - // We don't mangle the closure invoking function name because it
|
| - // is generated by string concatenation in applyFunction from
|
| - // js_helper.dart. We potentially shorten the prefix though.
|
| - if (selector.isClosureCall) {
|
| - return "$callPrefix$suffix";
|
| - } else {
|
| - String proposedName = privateName(selector.library, name);
|
| - return getMappedInstanceName('$proposedName$suffix');
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns the internal name used for an invocation mirror of this selector.
|
| - */
|
| - String invocationMirrorInternalName(Selector selector)
|
| - => invocationName(selector);
|
| -
|
| - /**
|
| - * Returns name of accessor (root to getter and setter) for a static or
|
| - * instance field.
|
| - */
|
| - String fieldAccessorName(Element element) {
|
| - return element.isInstanceMember
|
| - ? instanceFieldAccessorName(element)
|
| - : getNameOfField(element);
|
| - }
|
| -
|
| - /**
|
| - * Returns name of the JavaScript property used to store a static or instance
|
| - * field.
|
| - */
|
| - String fieldPropertyName(Element element) {
|
| - return element.isInstanceMember
|
| - ? instanceFieldPropertyName(element)
|
| - : getNameOfField(element);
|
| - }
|
| -
|
| - /**
|
| - * Returns name of accessor (root to getter and setter) for an instance field.
|
| - */
|
| - String instanceFieldAccessorName(Element element) {
|
| - String proposedName = privateName(element.library, element.name);
|
| - return getMappedInstanceName(proposedName);
|
| - }
|
| -
|
| - String readTypeVariableName(TypeVariableElement element) {
|
| - return '\$tv_${instanceFieldAccessorName(element)}';
|
| - }
|
| -
|
| - /**
|
| - * Returns name of the JavaScript property used to store an instance field.
|
| - */
|
| - String instanceFieldPropertyName(Element element) {
|
| - if (element.hasFixedBackendName) {
|
| - return element.fixedBackendName;
|
| - }
|
| - // If a class is used anywhere as a mixin, we must make the name unique so
|
| - // that it does not accidentally shadow. Also, the mixin name must be
|
| - // constant over all mixins.
|
| - ClassWorld classWorld = compiler.world;
|
| - if (classWorld.isUsedAsMixin(element.enclosingClass) ||
|
| - shadowingAnotherField(element)) {
|
| - // Construct a new name for the element based on the library and class it
|
| - // is in. The name here is not important, we just need to make sure it is
|
| - // unique. If we are minifying, we actually construct the name from the
|
| - // minified version of the class name, but the result is minified once
|
| - // again, so that is not visible in the end result.
|
| - String libraryName = getNameOfLibrary(element.library);
|
| - String className = getNameOfClass(element.enclosingClass);
|
| - String instanceName = privateName(element.library, element.name);
|
| - return getMappedInstanceName('$libraryName\$$className\$$instanceName');
|
| - }
|
| -
|
| - String proposedName = privateName(element.library, element.name);
|
| - return getMappedInstanceName(proposedName);
|
| - }
|
| -
|
| -
|
| - bool shadowingAnotherField(Element element) {
|
| - return element.enclosingClass.hasFieldShadowedBy(element);
|
| - }
|
| -
|
| - String setterName(Element element) {
|
| - // We dynamically create setters from the field-name. The setter name must
|
| - // therefore be derived from the instance field-name.
|
| - LibraryElement library = element.library;
|
| - String name = getMappedInstanceName(privateName(library, element.name));
|
| - return '$setterPrefix$name';
|
| - }
|
| -
|
| - String setterNameFromAccessorName(String name) {
|
| - // We dynamically create setters from the field-name. The setter name must
|
| - // therefore be derived from the instance field-name.
|
| - return '$setterPrefix$name';
|
| - }
|
| -
|
| - String getterNameFromAccessorName(String name) {
|
| - // We dynamically create getters from the field-name. The getter name must
|
| - // therefore be derived from the instance field-name.
|
| - return '$getterPrefix$name';
|
| - }
|
| -
|
| - String getterName(Element element) {
|
| - // We dynamically create getters from the field-name. The getter name must
|
| - // therefore be derived from the instance field-name.
|
| - LibraryElement library = element.library;
|
| - String name = getMappedInstanceName(privateName(library, element.name));
|
| - return '$getterPrefix$name';
|
| - }
|
| -
|
| - String getMappedGlobalName(String proposedName, {bool ensureSafe: true}) {
|
| - var newName = globalNameMap[proposedName];
|
| - if (newName == null) {
|
| - newName = getFreshName(proposedName, usedGlobalNames,
|
| - suggestedGlobalNames, ensureSafe: ensureSafe);
|
| - globalNameMap[proposedName] = newName;
|
| - }
|
| - return newName;
|
| - }
|
| -
|
| - String getMappedInstanceName(String proposedName) {
|
| - var newName = instanceNameMap[proposedName];
|
| - if (newName == null) {
|
| - newName = getFreshName(proposedName, usedInstanceNames,
|
| - suggestedInstanceNames, ensureSafe: true);
|
| - instanceNameMap[proposedName] = newName;
|
| - }
|
| - return newName;
|
| - }
|
| -
|
| - String getMappedOperatorName(String proposedName) {
|
| - var newName = operatorNameMap[proposedName];
|
| - if (newName == null) {
|
| - newName = getFreshName(proposedName, usedInstanceNames,
|
| - suggestedInstanceNames, ensureSafe: false);
|
| - operatorNameMap[proposedName] = newName;
|
| - }
|
| - return newName;
|
| - }
|
| -
|
| - String getFreshName(String proposedName,
|
| - Set<String> usedNames,
|
| - Map<String, String> suggestedNames,
|
| - {bool ensureSafe: true}) {
|
| - var candidate;
|
| - if (ensureSafe) {
|
| - proposedName = safeName(proposedName);
|
| - }
|
| - assert(!jsReserved.contains(proposedName));
|
| - if (!usedNames.contains(proposedName)) {
|
| - candidate = proposedName;
|
| - } else {
|
| - var counter = popularNameCounters[proposedName];
|
| - var i = counter == null ? 0 : counter;
|
| - while (usedNames.contains("$proposedName$i")) {
|
| - i++;
|
| - }
|
| - popularNameCounters[proposedName] = i + 1;
|
| - candidate = "$proposedName$i";
|
| - }
|
| - usedNames.add(candidate);
|
| - return candidate;
|
| - }
|
| -
|
| - String getClosureVariableName(String name, int id) {
|
| - return "${name}_$id";
|
| - }
|
| -
|
| - /**
|
| - * Returns a preferred JS-id for the given top-level or static element.
|
| - * The returned id is guaranteed to be a valid JS-id.
|
| - */
|
| - String _computeGuess(Element element) {
|
| - assert(!element.isInstanceMember);
|
| - String name;
|
| - if (element.isGenerativeConstructor) {
|
| - name = "${element.enclosingClass.name}\$"
|
| - "${element.name}";
|
| - } else if (element.isFactoryConstructor) {
|
| - // TODO(johnniwinther): Change factory name encoding as to not include
|
| - // the class-name twice.
|
| - String className = element.enclosingClass.name;
|
| - name = '${className}_${Elements.reconstructConstructorName(element)}';
|
| - } else if (Elements.isStaticOrTopLevel(element)) {
|
| - if (element.isClassMember) {
|
| - ClassElement enclosingClass = element.enclosingClass;
|
| - name = "${enclosingClass.name}_"
|
| - "${element.name}";
|
| - } else {
|
| - name = element.name.replaceAll('+', '_');
|
| - }
|
| - } else if (element.isLibrary) {
|
| - LibraryElement library = element;
|
| - name = library.getLibraryOrScriptName();
|
| - if (name.contains('.')) {
|
| - // For libraries that have a library tag, we use the last part
|
| - // of the fully qualified name as their base name. For all other
|
| - // libraries, we use the first part of their filename.
|
| - name = library.hasLibraryName()
|
| - ? name.substring(name.lastIndexOf('.') + 1)
|
| - : name.substring(0, name.indexOf('.'));
|
| - }
|
| - // The filename based name can contain all kinds of nasty characters. Make
|
| - // sure it is an identifier.
|
| - if (!IDENTIFIER.hasMatch(name)) {
|
| - name = name.replaceAllMapped(NON_IDENTIFIER_CHAR,
|
| - (match) => match[0].codeUnitAt(0).toRadixString(16));
|
| - if (!IDENTIFIER.hasMatch(name)) { // e.g. starts with digit.
|
| - name = 'lib_$name';
|
| - }
|
| - }
|
| - } else {
|
| - name = element.name;
|
| - }
|
| - return name;
|
| - }
|
| -
|
| - String getInterceptorSuffix(Iterable<ClassElement> classes) {
|
| - String abbreviate(ClassElement cls) {
|
| - if (cls == compiler.objectClass) return "o";
|
| - if (cls == backend.jsStringClass) return "s";
|
| - if (cls == backend.jsArrayClass) return "a";
|
| - if (cls == backend.jsDoubleClass) return "d";
|
| - if (cls == backend.jsIntClass) return "i";
|
| - if (cls == backend.jsNumberClass) return "n";
|
| - if (cls == backend.jsNullClass) return "u";
|
| - if (cls == backend.jsBoolClass) return "b";
|
| - if (cls == backend.jsInterceptorClass) return "I";
|
| - return cls.name;
|
| - }
|
| - List<String> names = classes
|
| - .where((cls) => !Elements.isNativeOrExtendsNative(cls))
|
| - .map(abbreviate)
|
| - .toList();
|
| - // There is one dispatch mechanism for all native classes.
|
| - if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) {
|
| - names.add("x");
|
| - }
|
| - // Sort the names of the classes after abbreviating them to ensure
|
| - // the suffix is stable and predictable for the suggested names.
|
| - names.sort();
|
| - return names.join();
|
| - }
|
| -
|
| - String getInterceptorName(Element element, Iterable<ClassElement> classes) {
|
| - if (classes.contains(backend.jsInterceptorClass)) {
|
| - // If the base Interceptor class is in the set of intercepted classes, we
|
| - // need to go through the generic getInterceptorMethod, since any subclass
|
| - // of the base Interceptor could match.
|
| - return getNameOfInstanceMember(element);
|
| - }
|
| - String suffix = getInterceptorSuffix(classes);
|
| - return getMappedGlobalName("${element.name}\$$suffix");
|
| - }
|
| -
|
| - String getOneShotInterceptorName(Selector selector,
|
| - Iterable<ClassElement> classes) {
|
| - // The one-shot name is a global name derived from the invocation name. To
|
| - // avoid instability we would like the names to be unique and not clash with
|
| - // other global names.
|
| -
|
| - String root = invocationName(selector); // Is already safe.
|
| -
|
| - if (classes.contains(backend.jsInterceptorClass)) {
|
| - // If the base Interceptor class is in the set of intercepted classes,
|
| - // this is the most general specialization which uses the generic
|
| - // getInterceptor method. To keep the name short, we add '$' only to
|
| - // distinguish from global getters or setters; operators and methods can't
|
| - // clash.
|
| - // TODO(sra): Find a way to get the simple name when Object is not in the
|
| - // set of classes for most general variant, e.g. "$lt$n" could be "$lt".
|
| - if (selector.isGetter || selector.isSetter) root = '$root\$';
|
| - return getMappedGlobalName(root, ensureSafe: false);
|
| - } else {
|
| - String suffix = getInterceptorSuffix(classes);
|
| - return getMappedGlobalName("$root\$$suffix", ensureSafe: false);
|
| - }
|
| - }
|
| -
|
| - /// Returns the runtime name for [element]. The result is not safe as an id.
|
| - String getRuntimeTypeName(Element element) {
|
| - if (element == null) return 'dynamic';
|
| - return getNameForRti(element);
|
| - }
|
| -
|
| - /**
|
| - * Returns a preferred JS-id for the given element. The returned id is
|
| - * guaranteed to be a valid JS-id. Globals and static fields are furthermore
|
| - * guaranteed to be unique.
|
| - *
|
| - * For accessing statics consider calling [elementAccess] instead.
|
| - */
|
| - // TODO(ahe): This is an internal method to the Namer (and its subclasses)
|
| - // and should not be call from outside.
|
| - String getNameX(Element element) {
|
| - if (element.isInstanceMember) {
|
| - if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY
|
| - || element.kind == ElementKind.FUNCTION) {
|
| - return instanceMethodName(element);
|
| - } else if (element.kind == ElementKind.GETTER) {
|
| - return getterName(element);
|
| - } else if (element.kind == ElementKind.SETTER) {
|
| - return setterName(element);
|
| - } else if (element.kind == ElementKind.FIELD) {
|
| - compiler.internalError(element,
|
| - 'Use instanceFieldPropertyName or instanceFieldAccessorName.');
|
| - return null;
|
| - } else {
|
| - compiler.internalError(element,
|
| - 'getName for bad kind: ${element.kind}.');
|
| - return null;
|
| - }
|
| - } else {
|
| - // Use declaration element to ensure invariant on [globals].
|
| - element = element.declaration;
|
| - // Dealing with a top-level or static element.
|
| - String cached = globals[element];
|
| - if (cached != null) return cached;
|
| -
|
| - String guess = _computeGuess(element);
|
| - ElementKind kind = element.kind;
|
| - if (kind == ElementKind.VARIABLE ||
|
| - kind == ElementKind.PARAMETER) {
|
| - // The name is not guaranteed to be unique.
|
| - return safeName(guess);
|
| - }
|
| - if (kind == ElementKind.GENERATIVE_CONSTRUCTOR ||
|
| - kind == ElementKind.FUNCTION ||
|
| - kind == ElementKind.CLASS ||
|
| - kind == ElementKind.FIELD ||
|
| - kind == ElementKind.GETTER ||
|
| - kind == ElementKind.SETTER ||
|
| - kind == ElementKind.TYPEDEF ||
|
| - kind == ElementKind.LIBRARY) {
|
| - bool fixedName = false;
|
| - if (Elements.isInstanceField(element)) {
|
| - fixedName = element.hasFixedBackendName;
|
| - }
|
| - String result = fixedName
|
| - ? guess
|
| - : getFreshName(guess, usedGlobalNames, suggestedGlobalNames,
|
| - ensureSafe: true);
|
| - globals[element] = result;
|
| - return result;
|
| - }
|
| - compiler.internalError(element,
|
| - 'getName for unknown kind: ${element.kind}.');
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - String getNameForRti(Element element) => getNameX(element);
|
| -
|
| - String getNameOfLibrary(LibraryElement library) => getNameX(library);
|
| -
|
| - String getNameOfClass(ClassElement cls) => getNameX(cls);
|
| -
|
| - String getNameOfField(VariableElement field) => getNameX(field);
|
| -
|
| - // TODO(ahe): Remove this method. Use get getNameOfMember instead.
|
| - String getNameOfInstanceMember(Element member) => getNameX(member);
|
| -
|
| - String getNameOfMember(Element member) => getNameX(member);
|
| -
|
| - String getNameOfGlobalField(VariableElement field) => getNameX(field);
|
| -
|
| - /// Returns true if [element] is stored on current isolate ('$'). We intend
|
| - /// to store only mutable static state in [currentIsolate], constants are
|
| - /// stored in 'C', and functions, accessors, classes, etc. are stored in one
|
| - /// of the other objects in [reservedGlobalObjectNames].
|
| - bool isPropertyOfCurrentIsolate(Element element) {
|
| - // TODO(ahe): Make sure this method's documentation is always true and
|
| - // remove the word "intend".
|
| - return
|
| - // TODO(ahe): Re-write these tests to be positive (so it only returns
|
| - // true for static/top-level mutable fields). Right now, a number of
|
| - // other elements, such as bound closures also live in [currentIsolate].
|
| - !element.isAccessor &&
|
| - !element.isClass &&
|
| - !element.isTypedef &&
|
| - !element.isConstructor &&
|
| - !element.isFunction &&
|
| - !element.isLibrary;
|
| - }
|
| -
|
| - /// Returns [currentIsolate] or one of [reservedGlobalObjectNames].
|
| - String globalObjectFor(Element element) {
|
| - if (isPropertyOfCurrentIsolate(element)) return currentIsolate;
|
| - LibraryElement library = element.library;
|
| - if (library == backend.interceptorsLibrary) return 'J';
|
| - if (library.isInternalLibrary) return 'H';
|
| - if (library.isPlatformLibrary) {
|
| - if ('${library.canonicalUri}' == 'dart:html') return 'W';
|
| - return 'P';
|
| - }
|
| - return userGlobalObjects[
|
| - library.getLibraryOrScriptName().hashCode % userGlobalObjects.length];
|
| - }
|
| -
|
| - jsAst.PropertyAccess elementAccess(Element element) {
|
| - String name = getNameX(element);
|
| - return new jsAst.PropertyAccess.field(
|
| - new jsAst.VariableUse(globalObjectFor(element)),
|
| - name);
|
| - }
|
| -
|
| - String getLazyInitializerName(Element element) {
|
| - assert(Elements.isStaticOrTopLevelField(element));
|
| - return getMappedGlobalName("$getterPrefix${getNameX(element)}");
|
| - }
|
| -
|
| - String getStaticClosureName(Element element) {
|
| - assert(Elements.isStaticOrTopLevelFunction(element));
|
| - return getMappedGlobalName("${getNameX(element)}\$closure");
|
| - }
|
| -
|
| - jsAst.Expression isolateLazyInitializerAccess(Element element) {
|
| - return js('#.#',
|
| - [globalObjectFor(element), getLazyInitializerName(element)]);
|
| - }
|
| -
|
| - jsAst.Expression isolateStaticClosureAccess(Element element) {
|
| - return js('#.#()',
|
| - [globalObjectFor(element), getStaticClosureName(element)]);
|
| - }
|
| -
|
| - // This name is used as part of the name of a TypeConstant
|
| - String uniqueNameForTypeConstantElement(Element element) {
|
| - // TODO(sra): If we replace the period with an identifier character,
|
| - // TypeConstants will have better names in unminified code.
|
| - return "${globalObjectFor(element)}.${getNameX(element)}";
|
| - }
|
| -
|
| - String globalObjectForConstant(ConstantValue constant) => 'C';
|
| -
|
| - String operatorIsPrefix() => r'$is';
|
| -
|
| - String operatorAsPrefix() => r'$as';
|
| -
|
| - String operatorSignature() => r'$signature';
|
| -
|
| - String typedefTag() => r'typedef';
|
| -
|
| - String functionTypeTag() => r'func';
|
| -
|
| - String functionTypeVoidReturnTag() => r'void';
|
| -
|
| - String functionTypeReturnTypeTag() => r'ret';
|
| -
|
| - String functionTypeRequiredParametersTag() => r'args';
|
| -
|
| - String functionTypeOptionalParametersTag() => r'opt';
|
| -
|
| - String functionTypeNamedParametersTag() => r'named';
|
| -
|
| - Map<FunctionType,String> functionTypeNameMap =
|
| - new Map<FunctionType,String>();
|
| - final FunctionTypeNamer functionTypeNamer;
|
| -
|
| - String getFunctionTypeName(FunctionType functionType) {
|
| - return functionTypeNameMap.putIfAbsent(functionType, () {
|
| - String proposedName = functionTypeNamer.computeName(functionType);
|
| - String freshName = getFreshName(proposedName, usedInstanceNames,
|
| - suggestedInstanceNames, ensureSafe: true);
|
| - return freshName;
|
| - });
|
| - }
|
| -
|
| - String operatorIsType(DartType type) {
|
| - if (type.isFunctionType) {
|
| - // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
|
| - return '${operatorIsPrefix()}_${getFunctionTypeName(type)}';
|
| - }
|
| - return operatorIs(type.element);
|
| - }
|
| -
|
| - String operatorIs(Element element) {
|
| - // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
|
| - return '${operatorIsPrefix()}${getRuntimeTypeName(element)}';
|
| - }
|
| -
|
| - /*
|
| - * Returns a name that does not clash with reserved JS keywords,
|
| - * and also ensures it won't clash with other identifiers.
|
| - */
|
| - String _safeName(String name, Set<String> reserved) {
|
| - if (reserved.contains(name) || name.startsWith(r'$')) {
|
| - name = '\$$name';
|
| - }
|
| - assert(!reserved.contains(name));
|
| - return name;
|
| - }
|
| -
|
| - String substitutionName(Element element) {
|
| - // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
|
| - // string concatenation in the implementation), and may prevent
|
| - // segmentation of '$'.
|
| - return '${operatorAsPrefix()}${getNameForRti(element)}';
|
| - }
|
| -
|
| - String safeName(String name) => _safeName(name, jsReserved);
|
| - String safeVariableName(String name) => _safeName(name, jsVariableReserved);
|
| -
|
| - String operatorNameToIdentifier(String name) {
|
| - if (name == null) return null;
|
| - if (name == '==') {
|
| - return r'$eq';
|
| - } else if (name == '~') {
|
| - return r'$not';
|
| - } else if (name == '[]') {
|
| - return r'$index';
|
| - } else if (name == '[]=') {
|
| - return r'$indexSet';
|
| - } else if (name == '*') {
|
| - return r'$mul';
|
| - } else if (name == '/') {
|
| - return r'$div';
|
| - } else if (name == '%') {
|
| - return r'$mod';
|
| - } else if (name == '~/') {
|
| - return r'$tdiv';
|
| - } else if (name == '+') {
|
| - return r'$add';
|
| - } else if (name == '<<') {
|
| - return r'$shl';
|
| - } else if (name == '>>') {
|
| - return r'$shr';
|
| - } else if (name == '>=') {
|
| - return r'$ge';
|
| - } else if (name == '>') {
|
| - return r'$gt';
|
| - } else if (name == '<=') {
|
| - return r'$le';
|
| - } else if (name == '<') {
|
| - return r'$lt';
|
| - } else if (name == '&') {
|
| - return r'$and';
|
| - } else if (name == '^') {
|
| - return r'$xor';
|
| - } else if (name == '|') {
|
| - return r'$or';
|
| - } else if (name == '-') {
|
| - return r'$sub';
|
| - } else if (name == 'unary-') {
|
| - return r'$negate';
|
| - } else {
|
| - return name;
|
| - }
|
| - }
|
| -
|
| - void forgetElement(Element element) {
|
| - String globalName = globals[element];
|
| - invariant(element, globalName != null, message: 'No global name.');
|
| - usedGlobalNames.remove(globalName);
|
| - globals.remove(element);
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Generator of names for [ConstantValue] values.
|
| - *
|
| - * The names are stable under perturbations of the source. The name is either a
|
| - * short sequence of words, if this can be found from the constant, or a type
|
| - * followed by a hash tag.
|
| - *
|
| - * List_imX // A List, with hash tag.
|
| - * C_Sentinel // const Sentinel(), "C_" added to avoid clash
|
| - * // with class name.
|
| - * JSInt_methods // an interceptor.
|
| - * Duration_16000 // const Duration(milliseconds: 16)
|
| - * EventKeyProvider_keyup // const EventKeyProvider('keyup')
|
| - *
|
| - */
|
| -class ConstantNamingVisitor implements ConstantValueVisitor {
|
| -
|
| - static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$');
|
| - static const MAX_FRAGMENTS = 5;
|
| - static const MAX_EXTRA_LENGTH = 30;
|
| - static const DEFAULT_TAG_LENGTH = 3;
|
| -
|
| - final Compiler compiler;
|
| - final ConstantCanonicalHasher hasher;
|
| -
|
| - String root = null; // First word, usually a type name.
|
| - bool failed = false; // Failed to generate something pretty.
|
| - List<String> fragments = <String>[];
|
| - int length = 0;
|
| -
|
| - ConstantNamingVisitor(this.compiler, this.hasher);
|
| -
|
| - String getName(ConstantValue constant) {
|
| - _visit(constant);
|
| - if (root == null) return 'CONSTANT';
|
| - if (failed) return '${root}_${getHashTag(constant, DEFAULT_TAG_LENGTH)}';
|
| - if (fragments.length == 1) return 'C_${root}';
|
| - return fragments.join('_');
|
| - }
|
| -
|
| - String getHashTag(ConstantValue constant, int width) =>
|
| - hashWord(hasher.getHash(constant), width);
|
| -
|
| - String hashWord(int hash, int length) {
|
| - hash &= 0x1fffffff;
|
| - StringBuffer sb = new StringBuffer();
|
| - for (int i = 0; i < length; i++) {
|
| - int digit = hash % 62;
|
| - sb.write('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
| - [digit]);
|
| - hash ~/= 62;
|
| - if (hash == 0) break;
|
| - }
|
| - return sb.toString();
|
| - }
|
| -
|
| - void addRoot(String fragment) {
|
| - if (root == null && fragments.isEmpty) {
|
| - root = fragment;
|
| - }
|
| - add(fragment);
|
| - }
|
| -
|
| - void add(String fragment) {
|
| - assert(fragment.length > 0);
|
| - fragments.add(fragment);
|
| - length += fragment.length;
|
| - if (fragments.length > MAX_FRAGMENTS) failed = true;
|
| - if (root != null && length > root.length + 1 + MAX_EXTRA_LENGTH) {
|
| - failed = true;
|
| - }
|
| - }
|
| -
|
| - void addIdentifier(String fragment) {
|
| - if (fragment.length <= MAX_EXTRA_LENGTH && IDENTIFIER.hasMatch(fragment)) {
|
| - add(fragment);
|
| - } else {
|
| - failed = true;
|
| - }
|
| - }
|
| -
|
| - _visit(ConstantValue constant) {
|
| - return constant.accept(this);
|
| - }
|
| -
|
| - visitFunction(FunctionConstantValue constant) {
|
| - add(constant.element.name);
|
| - }
|
| -
|
| - visitNull(NullConstantValue constant) {
|
| - add('null');
|
| - }
|
| -
|
| - visitInt(IntConstantValue constant) {
|
| - // No `addRoot` since IntConstants are always inlined.
|
| - if (constant.primitiveValue < 0) {
|
| - add('m${-constant.primitiveValue}');
|
| - } else {
|
| - add('${constant.primitiveValue}');
|
| - }
|
| - }
|
| -
|
| - visitDouble(DoubleConstantValue constant) {
|
| - failed = true;
|
| - }
|
| -
|
| - visitTrue(TrueConstantValue constant) {
|
| - add('true');
|
| - }
|
| -
|
| - visitFalse(FalseConstantValue constant) {
|
| - add('false');
|
| - }
|
| -
|
| - visitString(StringConstantValue constant) {
|
| - // No `addRoot` since string constants are always inlined.
|
| - addIdentifier(constant.primitiveValue.slowToString());
|
| - }
|
| -
|
| - visitList(ListConstantValue constant) {
|
| - // TODO(9476): Incorporate type parameters into name.
|
| - addRoot('List');
|
| - int length = constant.length;
|
| - if (constant.length == 0) {
|
| - add('empty');
|
| - } else if (length >= MAX_FRAGMENTS) {
|
| - failed = true;
|
| - } else {
|
| - for (int i = 0; i < length; i++) {
|
| - _visit(constant.entries[i]);
|
| - if (failed) break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitMap(JavaScriptMapConstant constant) {
|
| - // TODO(9476): Incorporate type parameters into name.
|
| - addRoot('Map');
|
| - if (constant.length == 0) {
|
| - add('empty');
|
| - } else {
|
| - // Using some bits from the keys hash tag groups the names Maps with the
|
| - // same structure.
|
| - add(getHashTag(constant.keyList, 2) + getHashTag(constant, 3));
|
| - }
|
| - }
|
| -
|
| - visitConstructed(ConstructedConstantValue constant) {
|
| - addRoot(constant.type.element.name);
|
| - for (int i = 0; i < constant.fields.length; i++) {
|
| - _visit(constant.fields[i]);
|
| - if (failed) return;
|
| - }
|
| - }
|
| -
|
| - visitType(TypeConstantValue constant) {
|
| - addRoot('Type');
|
| - DartType type = constant.representedType;
|
| - JavaScriptBackend backend = compiler.backend;
|
| - String name = backend.rti.getTypeRepresentationForTypeConstant(type);
|
| - addIdentifier(name);
|
| - }
|
| -
|
| - visitInterceptor(InterceptorConstantValue constant) {
|
| - addRoot(constant.dispatchedType.element.name);
|
| - add('methods');
|
| - }
|
| -
|
| - visitDummy(DummyConstantValue constant) {
|
| - add('dummy_receiver');
|
| - }
|
| -
|
| - visitDeferred(DeferredConstantValue constant) {
|
| - addRoot('Deferred');
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Generates canonical hash values for [ConstantValue]s.
|
| - *
|
| - * Unfortunately, [Constant.hashCode] is not stable under minor perturbations,
|
| - * so it can't be used for generating names. This hasher keeps consistency
|
| - * between runs by basing hash values of the names of elements, rather than
|
| - * their hashCodes.
|
| - */
|
| -class ConstantCanonicalHasher implements ConstantValueVisitor<int> {
|
| -
|
| - static const _MASK = 0x1fffffff;
|
| - static const _UINT32_LIMIT = 4 * 1024 * 1024 * 1024;
|
| -
|
| -
|
| - final Compiler compiler;
|
| - final Map<ConstantValue, int> hashes = new Map<ConstantValue, int>();
|
| -
|
| - ConstantCanonicalHasher(this.compiler);
|
| -
|
| - int getHash(ConstantValue constant) => _visit(constant);
|
| -
|
| - int _visit(ConstantValue constant) {
|
| - int hash = hashes[constant];
|
| - if (hash == null) {
|
| - hash = _finish(constant.accept(this));
|
| - hashes[constant] = hash;
|
| - }
|
| - return hash;
|
| - }
|
| -
|
| - int visitNull(NullConstantValue constant) => 1;
|
| - int visitTrue(TrueConstantValue constant) => 2;
|
| - int visitFalse(FalseConstantValue constant) => 3;
|
| -
|
| - int visitFunction(FunctionConstantValue constant) {
|
| - return _hashString(1, constant.element.name);
|
| - }
|
| -
|
| - int visitInt(IntConstantValue constant) => _hashInt(constant.primitiveValue);
|
| -
|
| - int visitDouble(DoubleConstantValue constant) {
|
| - return _hashDouble(constant.primitiveValue);
|
| - }
|
| -
|
| - int visitString(StringConstantValue constant) {
|
| - return _hashString(2, constant.primitiveValue.slowToString());
|
| - }
|
| -
|
| - int visitList(ListConstantValue constant) {
|
| - return _hashList(constant.length, constant.entries);
|
| - }
|
| -
|
| - int visitMap(MapConstantValue constant) {
|
| - int hash = _hashList(constant.length, constant.keys);
|
| - return _hashList(hash, constant.values);
|
| - }
|
| -
|
| - int visitConstructed(ConstructedConstantValue constant) {
|
| - int hash = _hashString(3, constant.type.element.name);
|
| - for (int i = 0; i < constant.fields.length; i++) {
|
| - hash = _combine(hash, _visit(constant.fields[i]));
|
| - }
|
| - return hash;
|
| - }
|
| -
|
| - int visitType(TypeConstantValue constant) {
|
| - DartType type = constant.representedType;
|
| - JavaScriptBackend backend = compiler.backend;
|
| - String name = backend.rti.getTypeRepresentationForTypeConstant(type);
|
| - return _hashString(4, name);
|
| - }
|
| -
|
| - visitInterceptor(InterceptorConstantValue constant) {
|
| - String typeName = constant.dispatchedType.element.name;
|
| - return _hashString(5, typeName);
|
| - }
|
| -
|
| - visitDummy(DummyConstantValue constant) {
|
| - compiler.internalError(NO_LOCATION_SPANNABLE,
|
| - 'DummyReceiverConstant should never be named and never be subconstant');
|
| - }
|
| -
|
| - visitDeferred(DeferredConstantValue constant) {
|
| - int hash = constant.prefix.hashCode;
|
| - return _combine(hash, constant.referenced.accept(this));
|
| - }
|
| -
|
| - int _hashString(int hash, String s) {
|
| - int length = s.length;
|
| - hash = _combine(hash, length);
|
| - // Increasing stride is O(log N) on large strings which are unlikely to have
|
| - // many collisions.
|
| - for (int i = 0; i < length; i += 1 + (i >> 2)) {
|
| - hash = _combine(hash, s.codeUnitAt(i));
|
| - }
|
| - return hash;
|
| - }
|
| -
|
| - int _hashList(int hash, List<ConstantValue> constants) {
|
| - for (ConstantValue constant in constants) {
|
| - hash = _combine(hash, _visit(constant));
|
| - }
|
| - return hash;
|
| - }
|
| -
|
| - static int _hashInt(int value) {
|
| - if (value.abs() < _UINT32_LIMIT) return _MASK & value;
|
| - return _hashDouble(value.toDouble());
|
| - }
|
| -
|
| - static int _hashDouble(double value) {
|
| - double magnitude = value.abs();
|
| - int sign = value < 0 ? 1 : 0;
|
| - if (magnitude < _UINT32_LIMIT) { // 2^32
|
| - int intValue = value.toInt();
|
| - // Integer valued doubles in 32-bit range hash to the same values as ints.
|
| - int hash = _hashInt(intValue);
|
| - if (value == intValue) return hash;
|
| - hash = _combine(hash, sign);
|
| - int fraction = ((magnitude - intValue.abs()) * (_MASK + 1)).toInt();
|
| - hash = _combine(hash, fraction);
|
| - return hash;
|
| - } else if (value.isInfinite) {
|
| - return _combine(6, sign);
|
| - } else if (value.isNaN) {
|
| - return 7;
|
| - } else {
|
| - int hash = 0;
|
| - while (magnitude >= _UINT32_LIMIT) {
|
| - magnitude = magnitude / _UINT32_LIMIT;
|
| - hash++;
|
| - }
|
| - hash = _combine(hash, sign);
|
| - return _combine(hash, _hashDouble(magnitude));
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * [_combine] and [_finish] are parts of the [Jenkins hash function][1],
|
| - * modified by using masking to keep values in SMI range.
|
| - *
|
| - * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function
|
| - */
|
| - static int _combine(int hash, int value) {
|
| - hash = _MASK & (hash + value);
|
| - hash = _MASK & (hash + (((_MASK >> 10) & hash) << 10));
|
| - hash = hash ^ (hash >> 6);
|
| - return hash;
|
| - }
|
| -
|
| - static int _finish(int hash) {
|
| - hash = _MASK & (hash + (((_MASK >> 3) & hash) << 3));
|
| - hash = hash & (hash >> 11);
|
| - return _MASK & (hash + (((_MASK >> 15) & hash) << 15));
|
| - }
|
| -}
|
| -
|
| -class FunctionTypeNamer extends DartTypeVisitor {
|
| - final Compiler compiler;
|
| - StringBuffer sb;
|
| -
|
| - FunctionTypeNamer(this.compiler);
|
| -
|
| - JavaScriptBackend get backend => compiler.backend;
|
| -
|
| - String computeName(DartType type) {
|
| - sb = new StringBuffer();
|
| - visit(type);
|
| - return sb.toString();
|
| - }
|
| -
|
| - visit(DartType type) {
|
| - type.accept(this, null);
|
| - }
|
| -
|
| - visitType(DartType type, _) {
|
| - sb.write(type.name);
|
| - }
|
| -
|
| - visitFunctionType(FunctionType type, _) {
|
| - if (backend.rti.isSimpleFunctionType(type)) {
|
| - sb.write('args${type.parameterTypes.length}');
|
| - return;
|
| - }
|
| - visit(type.returnType);
|
| - sb.write('_');
|
| - for (DartType parameter in type.parameterTypes) {
|
| - sb.write('_');
|
| - visit(parameter);
|
| - }
|
| - bool first = false;
|
| - for (DartType parameter in type.optionalParameterTypes) {
|
| - if (!first) {
|
| - sb.write('_');
|
| - }
|
| - sb.write('_');
|
| - visit(parameter);
|
| - first = true;
|
| - }
|
| - if (!type.namedParameterTypes.isEmpty) {
|
| - first = false;
|
| - for (DartType parameter in type.namedParameterTypes) {
|
| - if (!first) {
|
| - sb.write('_');
|
| - }
|
| - sb.write('_');
|
| - visit(parameter);
|
| - first = true;
|
| - }
|
| - }
|
| - }
|
| -}
|
|
|