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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/namer.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of js_backend;
6
7 /**
8 * Assigns JavaScript identifiers to Dart variables, class-names and members.
9 */
10 class Namer implements ClosureNamer {
11
12 static const javaScriptKeywords = const <String>[
13 // These are current keywords.
14 "break", "delete", "function", "return", "typeof", "case", "do", "if",
15 "switch", "var", "catch", "else", "in", "this", "void", "continue",
16 "false", "instanceof", "throw", "while", "debugger", "finally", "new",
17 "true", "with", "default", "for", "null", "try",
18
19 // These are future keywords.
20 "abstract", "double", "goto", "native", "static", "boolean", "enum",
21 "implements", "package", "super", "byte", "export", "import", "private",
22 "synchronized", "char", "extends", "int", "protected", "throws",
23 "class", "final", "interface", "public", "transient", "const", "float",
24 "long", "short", "volatile"
25 ];
26
27 static const reservedPropertySymbols =
28 const <String>["__proto__", "prototype", "constructor", "call"];
29
30 // Symbols that we might be using in our JS snippets.
31 static const reservedGlobalSymbols = const <String>[
32 // Section references are from Ecma-262
33 // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pd f)
34
35 // 15.1.1 Value Properties of the Global Object
36 "NaN", "Infinity", "undefined",
37
38 // 15.1.2 Function Properties of the Global Object
39 "eval", "parseInt", "parseFloat", "isNaN", "isFinite",
40
41 // 15.1.3 URI Handling Function Properties
42 "decodeURI", "decodeURIComponent",
43 "encodeURI",
44 "encodeURIComponent",
45
46 // 15.1.4 Constructor Properties of the Global Object
47 "Object", "Function", "Array", "String", "Boolean", "Number", "Date",
48 "RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
49 "SyntaxError", "TypeError", "URIError",
50
51 // 15.1.5 Other Properties of the Global Object
52 "Math",
53
54 // 10.1.6 Activation Object
55 "arguments",
56
57 // B.2 Additional Properties (non-normative)
58 "escape", "unescape",
59
60 // Window props (https://developer.mozilla.org/en/DOM/window)
61 "applicationCache", "closed", "Components", "content", "controllers",
62 "crypto", "defaultStatus", "dialogArguments", "directories",
63 "document", "frameElement", "frames", "fullScreen", "globalStorage",
64 "history", "innerHeight", "innerWidth", "length",
65 "location", "locationbar", "localStorage", "menubar",
66 "mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel",
67 "name", "navigator", "opener", "outerHeight", "outerWidth",
68 "pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11",
69 "returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY",
70 "self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar",
71 "top", "window",
72
73 // Window methods (https://developer.mozilla.org/en/DOM/window)
74 "alert", "addEventListener", "atob", "back", "blur", "btoa",
75 "captureEvents", "clearInterval", "clearTimeout", "close", "confirm",
76 "disableExternalCapture", "dispatchEvent", "dump",
77 "enableExternalCapture", "escape", "find", "focus", "forward",
78 "GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount",
79 "getComputedStyle", "getSelection", "home", "maximize", "minimize",
80 "moveBy", "moveTo", "open", "openDialog", "postMessage", "print",
81 "prompt", "QueryInterface", "releaseEvents", "removeEventListener",
82 "resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy",
83 "scrollByLines", "scrollByPages", "scrollTo", "setInterval",
84 "setResizeable", "setTimeout", "showModalDialog", "sizeToContent",
85 "stop", "uuescape", "updateCommands", "XPCNativeWrapper",
86 "XPCSafeJSOjbectWrapper",
87
88 // Mozilla Window event handlers, same cite
89 "onabort", "onbeforeunload", "onchange", "onclick", "onclose",
90 "oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange",
91 "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown",
92 "onmousemove", "onmouseout", "onmouseover", "onmouseup",
93 "onmozorientation", "onpaint", "onreset", "onresize", "onscroll",
94 "onselect", "onsubmit", "onunload",
95
96 // Safari Web Content Guide
97 // http://developer.apple.com/library/safari/#documentation/AppleApplication s/Reference/SafariWebContent/SafariWebContent.pdf
98 // WebKit Window member data, from WebKit DOM Reference
99 // (http://developer.apple.com/safari/library/documentation/AppleApplication s/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html)
100 "ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart",
101 "ongesturestart", "ongesturechange", "ongestureend",
102
103 // extra window methods
104 "uneval",
105
106 // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7,
107 // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1
108 "getPrototypeOf", "let", "yield",
109
110 // "future reserved words"
111 "abstract", "int", "short", "boolean", "interface", "static", "byte",
112 "long", "char", "final", "native", "synchronized", "float", "package",
113 "throws", "goto", "private", "transient", "implements", "protected",
114 "volatile", "double", "public",
115
116 // IE methods
117 // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#)
118 "attachEvent", "clientInformation", "clipboardData", "createPopup",
119 "dialogHeight", "dialogLeft", "dialogTop", "dialogWidth",
120 "onafterprint", "onbeforedeactivate", "onbeforeprint",
121 "oncontrolselect", "ondeactivate", "onhelp", "onresizeend",
122
123 // Common browser-defined identifiers not defined in ECMAScript
124 "event", "external", "Debug", "Enumerator", "Global", "Image",
125 "ActiveXObject", "VBArray", "Components",
126
127 // Functions commonly defined on Object
128 "toString", "getClass", "constructor", "prototype", "valueOf",
129
130 // Client-side JavaScript identifiers
131 "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient",
132 "CanvasPattern", "CanvasRenderingContext2D", "CDATASection",
133 "CharacterData", "Comment", "CSS2Properties", "CSSRule",
134 "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType",
135 "DOMException", "DOMImplementation", "DOMParser", "Element", "Event",
136 "ExternalInterface", "FlashPlayer", "Form", "Frame", "History",
137 "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image",
138 "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType",
139 "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin",
140 "ProcessingInstruction", "Range", "RangeException", "Screen", "Select",
141 "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
142 "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
143 "XPathException", "XPathResult", "XSLTProcessor",
144
145 // These keywords trigger the loading of the java-plugin. For the
146 // next-generation plugin, this results in starting a new Java process.
147 "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
148 "JavaArray", "JavaMember",
149 ];
150
151 static const reservedGlobalObjectNames = const <String>[
152 "A",
153 "B",
154 "C", // Global object for *C*onstants.
155 "D",
156 "E",
157 "F",
158 "G",
159 "H", // Global object for internal (*H*elper) libraries.
160 // I is used for used for the Isolate function.
161 "J", // Global object for the interceptor library.
162 "K",
163 "L",
164 "M",
165 "N",
166 "O",
167 "P", // Global object for other *P*latform libraries.
168 "Q",
169 "R",
170 "S",
171 "T",
172 "U",
173 "V",
174 "W", // Global object for *W*eb libraries (dart:html).
175 "X",
176 "Y",
177 "Z",
178 ];
179
180 static const reservedGlobalHelperFunctions = const <String>[
181 "init",
182 "Isolate",
183 ];
184
185 static final userGlobalObjects = new List.from(reservedGlobalObjectNames)
186 ..remove('C')
187 ..remove('H')
188 ..remove('J')
189 ..remove('P')
190 ..remove('W');
191
192 Set<String> _jsReserved = null;
193 /// Names that cannot be used by members, top level and static
194 /// methods.
195 Set<String> get jsReserved {
196 if (_jsReserved == null) {
197 _jsReserved = new Set<String>();
198 _jsReserved.addAll(javaScriptKeywords);
199 _jsReserved.addAll(reservedPropertySymbols);
200 }
201 return _jsReserved;
202 }
203
204 Set<String> _jsVariableReserved = null;
205 /// Names that cannot be used by local variables and parameters.
206 Set<String> get jsVariableReserved {
207 if (_jsVariableReserved == null) {
208 _jsVariableReserved = new Set<String>();
209 _jsVariableReserved.addAll(javaScriptKeywords);
210 _jsVariableReserved.addAll(reservedPropertySymbols);
211 _jsVariableReserved.addAll(reservedGlobalSymbols);
212 _jsVariableReserved.addAll(reservedGlobalObjectNames);
213 // 26 letters in the alphabet, 25 not counting I.
214 assert(reservedGlobalObjectNames.length == 25);
215 _jsVariableReserved.addAll(reservedGlobalHelperFunctions);
216 }
217 return _jsVariableReserved;
218 }
219
220 final String currentIsolate = r'$';
221 final String getterPrefix = r'get$';
222 final String setterPrefix = r'set$';
223 final String metadataField = '@';
224 final String callPrefix = 'call';
225 final String callCatchAllName = r'call$catchAll';
226 final String reflectableField = r'$reflectable';
227 final String defaultValuesField = r'$defaultValues';
228 final String methodsWithOptionalArgumentsField =
229 r'$methodsWithOptionalArguments';
230
231 final String classDescriptorProperty = r'^';
232
233 // Name of property in a class description for the native dispatch metadata.
234 final String nativeSpecProperty = '%';
235
236 static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$');
237 static final RegExp NON_IDENTIFIER_CHAR = new RegExp(r'[^A-Za-z_0-9$]');
238
239 /**
240 * Map from top-level or static elements to their unique identifiers provided
241 * by [getName].
242 *
243 * Invariant: Keys must be declaration elements.
244 */
245 final Compiler compiler;
246 final Map<Element, String> globals;
247 final Map<String, LibraryElement> shortPrivateNameOwners;
248
249 final Set<String> usedGlobalNames;
250 final Set<String> usedInstanceNames;
251 final Map<String, String> globalNameMap;
252 final Map<String, String> suggestedGlobalNames;
253 final Map<String, String> instanceNameMap;
254 final Map<String, String> suggestedInstanceNames;
255
256 final Map<String, String> operatorNameMap;
257 final Map<String, int> popularNameCounters;
258
259 final Map<ConstantValue, String> constantNames;
260 final Map<ConstantValue, String> constantLongNames;
261 ConstantCanonicalHasher constantHasher;
262
263 // All alphanumeric characters.
264 static const String _alphaNumeric =
265 'abcdefghijklmnopqrstuvwxyzABZDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
266
267 Namer(Compiler compiler)
268 : compiler = compiler,
269 globals = new Map<Element, String>(),
270 shortPrivateNameOwners = new Map<String, LibraryElement>(),
271 usedGlobalNames = new Set<String>(),
272 usedInstanceNames = new Set<String>(),
273 instanceNameMap = new Map<String, String>(),
274 operatorNameMap = new Map<String, String>(),
275 globalNameMap = new Map<String, String>(),
276 suggestedGlobalNames = new Map<String, String>(),
277 suggestedInstanceNames = new Map<String, String>(),
278 popularNameCounters = new Map<String, int>(),
279 constantNames = new Map<ConstantValue, String>(),
280 constantLongNames = new Map<ConstantValue, String>(),
281 constantHasher = new ConstantCanonicalHasher(compiler),
282 functionTypeNamer = new FunctionTypeNamer(compiler);
283
284 JavaScriptBackend get backend => compiler.backend;
285
286 String get isolateName => 'Isolate';
287 String get isolatePropertiesName => r'$isolateProperties';
288 /**
289 * Some closures must contain their name. The name is stored in
290 * [STATIC_CLOSURE_NAME_NAME].
291 */
292 String get STATIC_CLOSURE_NAME_NAME => r'$name';
293 String get closureInvocationSelectorName => Compiler.CALL_OPERATOR_NAME;
294 bool get shouldMinify => false;
295
296 String getNameForJsGetName(Node node, String name) {
297 switch (name) {
298 case 'GETTER_PREFIX': return getterPrefix;
299 case 'SETTER_PREFIX': return setterPrefix;
300 case 'CALL_PREFIX': return callPrefix;
301 case 'CALL_CATCH_ALL': return callCatchAllName;
302 case 'REFLECTABLE': return reflectableField;
303 case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty;
304 default:
305 compiler.reportError(
306 node, MessageKind.GENERIC,
307 {'text': 'Error: Namer has no name for "$name".'});
308 return 'BROKEN';
309 }
310 }
311
312 String constantName(ConstantValue constant) {
313 // In the current implementation it doesn't make sense to give names to
314 // function constants since the function-implementation itself serves as
315 // constant and can be accessed directly.
316 assert(!constant.isFunction);
317 String result = constantNames[constant];
318 if (result == null) {
319 String longName = constantLongName(constant);
320 result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames,
321 ensureSafe: true);
322 constantNames[constant] = result;
323 }
324 return result;
325 }
326
327 // The long name is unminified and may have collisions.
328 String constantLongName(ConstantValue constant) {
329 String longName = constantLongNames[constant];
330 if (longName == null) {
331 longName = new ConstantNamingVisitor(compiler, constantHasher)
332 .getName(constant);
333 constantLongNames[constant] = longName;
334 }
335 return longName;
336 }
337
338 String breakLabelName(LabelDefinition label) {
339 return '\$${label.labelName}\$${label.target.nestingLevel}';
340 }
341
342 String implicitBreakLabelName(JumpTarget target) {
343 return '\$${target.nestingLevel}';
344 }
345
346 // We sometimes handle continue targets differently from break targets,
347 // so we have special continue-only labels.
348 String continueLabelName(LabelDefinition label) {
349 return 'c\$${label.labelName}\$${label.target.nestingLevel}';
350 }
351
352 String implicitContinueLabelName(JumpTarget target) {
353 return 'c\$${target.nestingLevel}';
354 }
355
356 /**
357 * If the [name] is not private returns [:name:]. Otherwise
358 * mangles the [name] so that each library has a unique name.
359 */
360 String privateName(LibraryElement library, String name) {
361 // Public names are easy.
362 String nameString = name;
363 if (!isPrivateName(name)) return nameString;
364
365 // The first library asking for a short private name wins.
366 LibraryElement owner =
367 shortPrivateNameOwners.putIfAbsent(nameString, () => library);
368
369 if (owner == library && !nameString.contains('\$')) {
370 // Since the name doesn't contain $ it doesn't clash with any
371 // of the private names that have the library name as the prefix.
372 return nameString;
373 } else {
374 // Make sure to return a private name that starts with _ so it
375 // cannot clash with any public names.
376 String libraryName = getNameOfLibrary(library);
377 return '_$libraryName\$$nameString';
378 }
379 }
380
381 String instanceMethodName(FunctionElement element) {
382 // TODO(ahe): Could this be: return invocationName(new
383 // Selector.fromElement(element))?
384 String elementName = element.name;
385 String name = operatorNameToIdentifier(elementName);
386 if (name != elementName) return getMappedOperatorName(name);
387
388 LibraryElement library = element.library;
389 if (element.isGenerativeConstructorBody) {
390 name = Elements.reconstructConstructorNameSourceString(element);
391 }
392 FunctionSignature signature = element.functionSignature;
393 // We don't mangle the closure invoking function name because it
394 // is generated by string concatenation in applyFunction from
395 // js_helper.dart. To keep code size down, we potentially shorten
396 // the prefix though.
397 String methodName;
398 if (name == closureInvocationSelectorName) {
399 methodName = '$callPrefix\$${signature.parameterCount}';
400 } else {
401 methodName = '${privateName(library, name)}\$${signature.parameterCount}';
402 }
403 if (signature.optionalParametersAreNamed &&
404 !signature.optionalParameters.isEmpty) {
405 StringBuffer buffer = new StringBuffer();
406 signature.orderedOptionalParameters.forEach((Element element) {
407 buffer.write('\$${safeName(element.name)}');
408 });
409 methodName = '$methodName$buffer';
410 }
411 if (name == closureInvocationSelectorName) return methodName;
412 return getMappedInstanceName(methodName);
413 }
414
415 String publicInstanceMethodNameByArity(String name, int arity) {
416 String newName = operatorNameToIdentifier(name);
417 if (newName != name) return getMappedOperatorName(newName);
418 assert(!isPrivateName(name));
419 // We don't mangle the closure invoking function name because it
420 // is generated by string concatenation in applyFunction from
421 // js_helper.dart. To keep code size down, we potentially shorten
422 // the prefix though.
423 if (name == closureInvocationSelectorName) return '$callPrefix\$$arity';
424
425 return getMappedInstanceName('$name\$$arity');
426 }
427
428 String invocationName(Selector selector) {
429 if (selector.isGetter) {
430 String proposedName = privateName(selector.library, selector.name);
431 return '$getterPrefix${getMappedInstanceName(proposedName)}';
432 } else if (selector.isSetter) {
433 String proposedName = privateName(selector.library, selector.name);
434 return '$setterPrefix${getMappedInstanceName(proposedName)}';
435 } else {
436 String name = selector.name;
437 if (selector.kind == SelectorKind.OPERATOR
438 || selector.kind == SelectorKind.INDEX) {
439 name = operatorNameToIdentifier(name);
440 assert(name != selector.name);
441 return getMappedOperatorName(name);
442 }
443 assert(name == operatorNameToIdentifier(name));
444 StringBuffer buffer = new StringBuffer();
445 for (String argumentName in selector.getOrderedNamedArguments()) {
446 buffer.write('\$${safeName(argumentName)}');
447 }
448 String suffix = '\$${selector.argumentCount}$buffer';
449 // We don't mangle the closure invoking function name because it
450 // is generated by string concatenation in applyFunction from
451 // js_helper.dart. We potentially shorten the prefix though.
452 if (selector.isClosureCall) {
453 return "$callPrefix$suffix";
454 } else {
455 String proposedName = privateName(selector.library, name);
456 return getMappedInstanceName('$proposedName$suffix');
457 }
458 }
459 }
460
461 /**
462 * Returns the internal name used for an invocation mirror of this selector.
463 */
464 String invocationMirrorInternalName(Selector selector)
465 => invocationName(selector);
466
467 /**
468 * Returns name of accessor (root to getter and setter) for a static or
469 * instance field.
470 */
471 String fieldAccessorName(Element element) {
472 return element.isInstanceMember
473 ? instanceFieldAccessorName(element)
474 : getNameOfField(element);
475 }
476
477 /**
478 * Returns name of the JavaScript property used to store a static or instance
479 * field.
480 */
481 String fieldPropertyName(Element element) {
482 return element.isInstanceMember
483 ? instanceFieldPropertyName(element)
484 : getNameOfField(element);
485 }
486
487 /**
488 * Returns name of accessor (root to getter and setter) for an instance field.
489 */
490 String instanceFieldAccessorName(Element element) {
491 String proposedName = privateName(element.library, element.name);
492 return getMappedInstanceName(proposedName);
493 }
494
495 String readTypeVariableName(TypeVariableElement element) {
496 return '\$tv_${instanceFieldAccessorName(element)}';
497 }
498
499 /**
500 * Returns name of the JavaScript property used to store an instance field.
501 */
502 String instanceFieldPropertyName(Element element) {
503 if (element.hasFixedBackendName) {
504 return element.fixedBackendName;
505 }
506 // If a class is used anywhere as a mixin, we must make the name unique so
507 // that it does not accidentally shadow. Also, the mixin name must be
508 // constant over all mixins.
509 ClassWorld classWorld = compiler.world;
510 if (classWorld.isUsedAsMixin(element.enclosingClass) ||
511 shadowingAnotherField(element)) {
512 // Construct a new name for the element based on the library and class it
513 // is in. The name here is not important, we just need to make sure it is
514 // unique. If we are minifying, we actually construct the name from the
515 // minified version of the class name, but the result is minified once
516 // again, so that is not visible in the end result.
517 String libraryName = getNameOfLibrary(element.library);
518 String className = getNameOfClass(element.enclosingClass);
519 String instanceName = privateName(element.library, element.name);
520 return getMappedInstanceName('$libraryName\$$className\$$instanceName');
521 }
522
523 String proposedName = privateName(element.library, element.name);
524 return getMappedInstanceName(proposedName);
525 }
526
527
528 bool shadowingAnotherField(Element element) {
529 return element.enclosingClass.hasFieldShadowedBy(element);
530 }
531
532 String setterName(Element element) {
533 // We dynamically create setters from the field-name. The setter name must
534 // therefore be derived from the instance field-name.
535 LibraryElement library = element.library;
536 String name = getMappedInstanceName(privateName(library, element.name));
537 return '$setterPrefix$name';
538 }
539
540 String setterNameFromAccessorName(String name) {
541 // We dynamically create setters from the field-name. The setter name must
542 // therefore be derived from the instance field-name.
543 return '$setterPrefix$name';
544 }
545
546 String getterNameFromAccessorName(String name) {
547 // We dynamically create getters from the field-name. The getter name must
548 // therefore be derived from the instance field-name.
549 return '$getterPrefix$name';
550 }
551
552 String getterName(Element element) {
553 // We dynamically create getters from the field-name. The getter name must
554 // therefore be derived from the instance field-name.
555 LibraryElement library = element.library;
556 String name = getMappedInstanceName(privateName(library, element.name));
557 return '$getterPrefix$name';
558 }
559
560 String getMappedGlobalName(String proposedName, {bool ensureSafe: true}) {
561 var newName = globalNameMap[proposedName];
562 if (newName == null) {
563 newName = getFreshName(proposedName, usedGlobalNames,
564 suggestedGlobalNames, ensureSafe: ensureSafe);
565 globalNameMap[proposedName] = newName;
566 }
567 return newName;
568 }
569
570 String getMappedInstanceName(String proposedName) {
571 var newName = instanceNameMap[proposedName];
572 if (newName == null) {
573 newName = getFreshName(proposedName, usedInstanceNames,
574 suggestedInstanceNames, ensureSafe: true);
575 instanceNameMap[proposedName] = newName;
576 }
577 return newName;
578 }
579
580 String getMappedOperatorName(String proposedName) {
581 var newName = operatorNameMap[proposedName];
582 if (newName == null) {
583 newName = getFreshName(proposedName, usedInstanceNames,
584 suggestedInstanceNames, ensureSafe: false);
585 operatorNameMap[proposedName] = newName;
586 }
587 return newName;
588 }
589
590 String getFreshName(String proposedName,
591 Set<String> usedNames,
592 Map<String, String> suggestedNames,
593 {bool ensureSafe: true}) {
594 var candidate;
595 if (ensureSafe) {
596 proposedName = safeName(proposedName);
597 }
598 assert(!jsReserved.contains(proposedName));
599 if (!usedNames.contains(proposedName)) {
600 candidate = proposedName;
601 } else {
602 var counter = popularNameCounters[proposedName];
603 var i = counter == null ? 0 : counter;
604 while (usedNames.contains("$proposedName$i")) {
605 i++;
606 }
607 popularNameCounters[proposedName] = i + 1;
608 candidate = "$proposedName$i";
609 }
610 usedNames.add(candidate);
611 return candidate;
612 }
613
614 String getClosureVariableName(String name, int id) {
615 return "${name}_$id";
616 }
617
618 /**
619 * Returns a preferred JS-id for the given top-level or static element.
620 * The returned id is guaranteed to be a valid JS-id.
621 */
622 String _computeGuess(Element element) {
623 assert(!element.isInstanceMember);
624 String name;
625 if (element.isGenerativeConstructor) {
626 name = "${element.enclosingClass.name}\$"
627 "${element.name}";
628 } else if (element.isFactoryConstructor) {
629 // TODO(johnniwinther): Change factory name encoding as to not include
630 // the class-name twice.
631 String className = element.enclosingClass.name;
632 name = '${className}_${Elements.reconstructConstructorName(element)}';
633 } else if (Elements.isStaticOrTopLevel(element)) {
634 if (element.isClassMember) {
635 ClassElement enclosingClass = element.enclosingClass;
636 name = "${enclosingClass.name}_"
637 "${element.name}";
638 } else {
639 name = element.name.replaceAll('+', '_');
640 }
641 } else if (element.isLibrary) {
642 LibraryElement library = element;
643 name = library.getLibraryOrScriptName();
644 if (name.contains('.')) {
645 // For libraries that have a library tag, we use the last part
646 // of the fully qualified name as their base name. For all other
647 // libraries, we use the first part of their filename.
648 name = library.hasLibraryName()
649 ? name.substring(name.lastIndexOf('.') + 1)
650 : name.substring(0, name.indexOf('.'));
651 }
652 // The filename based name can contain all kinds of nasty characters. Make
653 // sure it is an identifier.
654 if (!IDENTIFIER.hasMatch(name)) {
655 name = name.replaceAllMapped(NON_IDENTIFIER_CHAR,
656 (match) => match[0].codeUnitAt(0).toRadixString(16));
657 if (!IDENTIFIER.hasMatch(name)) { // e.g. starts with digit.
658 name = 'lib_$name';
659 }
660 }
661 } else {
662 name = element.name;
663 }
664 return name;
665 }
666
667 String getInterceptorSuffix(Iterable<ClassElement> classes) {
668 String abbreviate(ClassElement cls) {
669 if (cls == compiler.objectClass) return "o";
670 if (cls == backend.jsStringClass) return "s";
671 if (cls == backend.jsArrayClass) return "a";
672 if (cls == backend.jsDoubleClass) return "d";
673 if (cls == backend.jsIntClass) return "i";
674 if (cls == backend.jsNumberClass) return "n";
675 if (cls == backend.jsNullClass) return "u";
676 if (cls == backend.jsBoolClass) return "b";
677 if (cls == backend.jsInterceptorClass) return "I";
678 return cls.name;
679 }
680 List<String> names = classes
681 .where((cls) => !Elements.isNativeOrExtendsNative(cls))
682 .map(abbreviate)
683 .toList();
684 // There is one dispatch mechanism for all native classes.
685 if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) {
686 names.add("x");
687 }
688 // Sort the names of the classes after abbreviating them to ensure
689 // the suffix is stable and predictable for the suggested names.
690 names.sort();
691 return names.join();
692 }
693
694 String getInterceptorName(Element element, Iterable<ClassElement> classes) {
695 if (classes.contains(backend.jsInterceptorClass)) {
696 // If the base Interceptor class is in the set of intercepted classes, we
697 // need to go through the generic getInterceptorMethod, since any subclass
698 // of the base Interceptor could match.
699 return getNameOfInstanceMember(element);
700 }
701 String suffix = getInterceptorSuffix(classes);
702 return getMappedGlobalName("${element.name}\$$suffix");
703 }
704
705 String getOneShotInterceptorName(Selector selector,
706 Iterable<ClassElement> classes) {
707 // The one-shot name is a global name derived from the invocation name. To
708 // avoid instability we would like the names to be unique and not clash with
709 // other global names.
710
711 String root = invocationName(selector); // Is already safe.
712
713 if (classes.contains(backend.jsInterceptorClass)) {
714 // If the base Interceptor class is in the set of intercepted classes,
715 // this is the most general specialization which uses the generic
716 // getInterceptor method. To keep the name short, we add '$' only to
717 // distinguish from global getters or setters; operators and methods can't
718 // clash.
719 // TODO(sra): Find a way to get the simple name when Object is not in the
720 // set of classes for most general variant, e.g. "$lt$n" could be "$lt".
721 if (selector.isGetter || selector.isSetter) root = '$root\$';
722 return getMappedGlobalName(root, ensureSafe: false);
723 } else {
724 String suffix = getInterceptorSuffix(classes);
725 return getMappedGlobalName("$root\$$suffix", ensureSafe: false);
726 }
727 }
728
729 /// Returns the runtime name for [element]. The result is not safe as an id.
730 String getRuntimeTypeName(Element element) {
731 if (element == null) return 'dynamic';
732 return getNameForRti(element);
733 }
734
735 /**
736 * Returns a preferred JS-id for the given element. The returned id is
737 * guaranteed to be a valid JS-id. Globals and static fields are furthermore
738 * guaranteed to be unique.
739 *
740 * For accessing statics consider calling [elementAccess] instead.
741 */
742 // TODO(ahe): This is an internal method to the Namer (and its subclasses)
743 // and should not be call from outside.
744 String getNameX(Element element) {
745 if (element.isInstanceMember) {
746 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY
747 || element.kind == ElementKind.FUNCTION) {
748 return instanceMethodName(element);
749 } else if (element.kind == ElementKind.GETTER) {
750 return getterName(element);
751 } else if (element.kind == ElementKind.SETTER) {
752 return setterName(element);
753 } else if (element.kind == ElementKind.FIELD) {
754 compiler.internalError(element,
755 'Use instanceFieldPropertyName or instanceFieldAccessorName.');
756 return null;
757 } else {
758 compiler.internalError(element,
759 'getName for bad kind: ${element.kind}.');
760 return null;
761 }
762 } else {
763 // Use declaration element to ensure invariant on [globals].
764 element = element.declaration;
765 // Dealing with a top-level or static element.
766 String cached = globals[element];
767 if (cached != null) return cached;
768
769 String guess = _computeGuess(element);
770 ElementKind kind = element.kind;
771 if (kind == ElementKind.VARIABLE ||
772 kind == ElementKind.PARAMETER) {
773 // The name is not guaranteed to be unique.
774 return safeName(guess);
775 }
776 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR ||
777 kind == ElementKind.FUNCTION ||
778 kind == ElementKind.CLASS ||
779 kind == ElementKind.FIELD ||
780 kind == ElementKind.GETTER ||
781 kind == ElementKind.SETTER ||
782 kind == ElementKind.TYPEDEF ||
783 kind == ElementKind.LIBRARY) {
784 bool fixedName = false;
785 if (Elements.isInstanceField(element)) {
786 fixedName = element.hasFixedBackendName;
787 }
788 String result = fixedName
789 ? guess
790 : getFreshName(guess, usedGlobalNames, suggestedGlobalNames,
791 ensureSafe: true);
792 globals[element] = result;
793 return result;
794 }
795 compiler.internalError(element,
796 'getName for unknown kind: ${element.kind}.');
797 return null;
798 }
799 }
800
801 String getNameForRti(Element element) => getNameX(element);
802
803 String getNameOfLibrary(LibraryElement library) => getNameX(library);
804
805 String getNameOfClass(ClassElement cls) => getNameX(cls);
806
807 String getNameOfField(VariableElement field) => getNameX(field);
808
809 // TODO(ahe): Remove this method. Use get getNameOfMember instead.
810 String getNameOfInstanceMember(Element member) => getNameX(member);
811
812 String getNameOfMember(Element member) => getNameX(member);
813
814 String getNameOfGlobalField(VariableElement field) => getNameX(field);
815
816 /// Returns true if [element] is stored on current isolate ('$'). We intend
817 /// to store only mutable static state in [currentIsolate], constants are
818 /// stored in 'C', and functions, accessors, classes, etc. are stored in one
819 /// of the other objects in [reservedGlobalObjectNames].
820 bool isPropertyOfCurrentIsolate(Element element) {
821 // TODO(ahe): Make sure this method's documentation is always true and
822 // remove the word "intend".
823 return
824 // TODO(ahe): Re-write these tests to be positive (so it only returns
825 // true for static/top-level mutable fields). Right now, a number of
826 // other elements, such as bound closures also live in [currentIsolate].
827 !element.isAccessor &&
828 !element.isClass &&
829 !element.isTypedef &&
830 !element.isConstructor &&
831 !element.isFunction &&
832 !element.isLibrary;
833 }
834
835 /// Returns [currentIsolate] or one of [reservedGlobalObjectNames].
836 String globalObjectFor(Element element) {
837 if (isPropertyOfCurrentIsolate(element)) return currentIsolate;
838 LibraryElement library = element.library;
839 if (library == backend.interceptorsLibrary) return 'J';
840 if (library.isInternalLibrary) return 'H';
841 if (library.isPlatformLibrary) {
842 if ('${library.canonicalUri}' == 'dart:html') return 'W';
843 return 'P';
844 }
845 return userGlobalObjects[
846 library.getLibraryOrScriptName().hashCode % userGlobalObjects.length];
847 }
848
849 jsAst.PropertyAccess elementAccess(Element element) {
850 String name = getNameX(element);
851 return new jsAst.PropertyAccess.field(
852 new jsAst.VariableUse(globalObjectFor(element)),
853 name);
854 }
855
856 String getLazyInitializerName(Element element) {
857 assert(Elements.isStaticOrTopLevelField(element));
858 return getMappedGlobalName("$getterPrefix${getNameX(element)}");
859 }
860
861 String getStaticClosureName(Element element) {
862 assert(Elements.isStaticOrTopLevelFunction(element));
863 return getMappedGlobalName("${getNameX(element)}\$closure");
864 }
865
866 jsAst.Expression isolateLazyInitializerAccess(Element element) {
867 return js('#.#',
868 [globalObjectFor(element), getLazyInitializerName(element)]);
869 }
870
871 jsAst.Expression isolateStaticClosureAccess(Element element) {
872 return js('#.#()',
873 [globalObjectFor(element), getStaticClosureName(element)]);
874 }
875
876 // This name is used as part of the name of a TypeConstant
877 String uniqueNameForTypeConstantElement(Element element) {
878 // TODO(sra): If we replace the period with an identifier character,
879 // TypeConstants will have better names in unminified code.
880 return "${globalObjectFor(element)}.${getNameX(element)}";
881 }
882
883 String globalObjectForConstant(ConstantValue constant) => 'C';
884
885 String operatorIsPrefix() => r'$is';
886
887 String operatorAsPrefix() => r'$as';
888
889 String operatorSignature() => r'$signature';
890
891 String typedefTag() => r'typedef';
892
893 String functionTypeTag() => r'func';
894
895 String functionTypeVoidReturnTag() => r'void';
896
897 String functionTypeReturnTypeTag() => r'ret';
898
899 String functionTypeRequiredParametersTag() => r'args';
900
901 String functionTypeOptionalParametersTag() => r'opt';
902
903 String functionTypeNamedParametersTag() => r'named';
904
905 Map<FunctionType,String> functionTypeNameMap =
906 new Map<FunctionType,String>();
907 final FunctionTypeNamer functionTypeNamer;
908
909 String getFunctionTypeName(FunctionType functionType) {
910 return functionTypeNameMap.putIfAbsent(functionType, () {
911 String proposedName = functionTypeNamer.computeName(functionType);
912 String freshName = getFreshName(proposedName, usedInstanceNames,
913 suggestedInstanceNames, ensureSafe: true);
914 return freshName;
915 });
916 }
917
918 String operatorIsType(DartType type) {
919 if (type.isFunctionType) {
920 // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
921 return '${operatorIsPrefix()}_${getFunctionTypeName(type)}';
922 }
923 return operatorIs(type.element);
924 }
925
926 String operatorIs(Element element) {
927 // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
928 return '${operatorIsPrefix()}${getRuntimeTypeName(element)}';
929 }
930
931 /*
932 * Returns a name that does not clash with reserved JS keywords,
933 * and also ensures it won't clash with other identifiers.
934 */
935 String _safeName(String name, Set<String> reserved) {
936 if (reserved.contains(name) || name.startsWith(r'$')) {
937 name = '\$$name';
938 }
939 assert(!reserved.contains(name));
940 return name;
941 }
942
943 String substitutionName(Element element) {
944 // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
945 // string concatenation in the implementation), and may prevent
946 // segmentation of '$'.
947 return '${operatorAsPrefix()}${getNameForRti(element)}';
948 }
949
950 String safeName(String name) => _safeName(name, jsReserved);
951 String safeVariableName(String name) => _safeName(name, jsVariableReserved);
952
953 String operatorNameToIdentifier(String name) {
954 if (name == null) return null;
955 if (name == '==') {
956 return r'$eq';
957 } else if (name == '~') {
958 return r'$not';
959 } else if (name == '[]') {
960 return r'$index';
961 } else if (name == '[]=') {
962 return r'$indexSet';
963 } else if (name == '*') {
964 return r'$mul';
965 } else if (name == '/') {
966 return r'$div';
967 } else if (name == '%') {
968 return r'$mod';
969 } else if (name == '~/') {
970 return r'$tdiv';
971 } else if (name == '+') {
972 return r'$add';
973 } else if (name == '<<') {
974 return r'$shl';
975 } else if (name == '>>') {
976 return r'$shr';
977 } else if (name == '>=') {
978 return r'$ge';
979 } else if (name == '>') {
980 return r'$gt';
981 } else if (name == '<=') {
982 return r'$le';
983 } else if (name == '<') {
984 return r'$lt';
985 } else if (name == '&') {
986 return r'$and';
987 } else if (name == '^') {
988 return r'$xor';
989 } else if (name == '|') {
990 return r'$or';
991 } else if (name == '-') {
992 return r'$sub';
993 } else if (name == 'unary-') {
994 return r'$negate';
995 } else {
996 return name;
997 }
998 }
999
1000 void forgetElement(Element element) {
1001 String globalName = globals[element];
1002 invariant(element, globalName != null, message: 'No global name.');
1003 usedGlobalNames.remove(globalName);
1004 globals.remove(element);
1005 }
1006 }
1007
1008 /**
1009 * Generator of names for [ConstantValue] values.
1010 *
1011 * The names are stable under perturbations of the source. The name is either a
1012 * short sequence of words, if this can be found from the constant, or a type
1013 * followed by a hash tag.
1014 *
1015 * List_imX // A List, with hash tag.
1016 * C_Sentinel // const Sentinel(), "C_" added to avoid clash
1017 * // with class name.
1018 * JSInt_methods // an interceptor.
1019 * Duration_16000 // const Duration(milliseconds: 16)
1020 * EventKeyProvider_keyup // const EventKeyProvider('keyup')
1021 *
1022 */
1023 class ConstantNamingVisitor implements ConstantValueVisitor {
1024
1025 static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$');
1026 static const MAX_FRAGMENTS = 5;
1027 static const MAX_EXTRA_LENGTH = 30;
1028 static const DEFAULT_TAG_LENGTH = 3;
1029
1030 final Compiler compiler;
1031 final ConstantCanonicalHasher hasher;
1032
1033 String root = null; // First word, usually a type name.
1034 bool failed = false; // Failed to generate something pretty.
1035 List<String> fragments = <String>[];
1036 int length = 0;
1037
1038 ConstantNamingVisitor(this.compiler, this.hasher);
1039
1040 String getName(ConstantValue constant) {
1041 _visit(constant);
1042 if (root == null) return 'CONSTANT';
1043 if (failed) return '${root}_${getHashTag(constant, DEFAULT_TAG_LENGTH)}';
1044 if (fragments.length == 1) return 'C_${root}';
1045 return fragments.join('_');
1046 }
1047
1048 String getHashTag(ConstantValue constant, int width) =>
1049 hashWord(hasher.getHash(constant), width);
1050
1051 String hashWord(int hash, int length) {
1052 hash &= 0x1fffffff;
1053 StringBuffer sb = new StringBuffer();
1054 for (int i = 0; i < length; i++) {
1055 int digit = hash % 62;
1056 sb.write('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
1057 [digit]);
1058 hash ~/= 62;
1059 if (hash == 0) break;
1060 }
1061 return sb.toString();
1062 }
1063
1064 void addRoot(String fragment) {
1065 if (root == null && fragments.isEmpty) {
1066 root = fragment;
1067 }
1068 add(fragment);
1069 }
1070
1071 void add(String fragment) {
1072 assert(fragment.length > 0);
1073 fragments.add(fragment);
1074 length += fragment.length;
1075 if (fragments.length > MAX_FRAGMENTS) failed = true;
1076 if (root != null && length > root.length + 1 + MAX_EXTRA_LENGTH) {
1077 failed = true;
1078 }
1079 }
1080
1081 void addIdentifier(String fragment) {
1082 if (fragment.length <= MAX_EXTRA_LENGTH && IDENTIFIER.hasMatch(fragment)) {
1083 add(fragment);
1084 } else {
1085 failed = true;
1086 }
1087 }
1088
1089 _visit(ConstantValue constant) {
1090 return constant.accept(this);
1091 }
1092
1093 visitFunction(FunctionConstantValue constant) {
1094 add(constant.element.name);
1095 }
1096
1097 visitNull(NullConstantValue constant) {
1098 add('null');
1099 }
1100
1101 visitInt(IntConstantValue constant) {
1102 // No `addRoot` since IntConstants are always inlined.
1103 if (constant.primitiveValue < 0) {
1104 add('m${-constant.primitiveValue}');
1105 } else {
1106 add('${constant.primitiveValue}');
1107 }
1108 }
1109
1110 visitDouble(DoubleConstantValue constant) {
1111 failed = true;
1112 }
1113
1114 visitTrue(TrueConstantValue constant) {
1115 add('true');
1116 }
1117
1118 visitFalse(FalseConstantValue constant) {
1119 add('false');
1120 }
1121
1122 visitString(StringConstantValue constant) {
1123 // No `addRoot` since string constants are always inlined.
1124 addIdentifier(constant.primitiveValue.slowToString());
1125 }
1126
1127 visitList(ListConstantValue constant) {
1128 // TODO(9476): Incorporate type parameters into name.
1129 addRoot('List');
1130 int length = constant.length;
1131 if (constant.length == 0) {
1132 add('empty');
1133 } else if (length >= MAX_FRAGMENTS) {
1134 failed = true;
1135 } else {
1136 for (int i = 0; i < length; i++) {
1137 _visit(constant.entries[i]);
1138 if (failed) break;
1139 }
1140 }
1141 }
1142
1143 visitMap(JavaScriptMapConstant constant) {
1144 // TODO(9476): Incorporate type parameters into name.
1145 addRoot('Map');
1146 if (constant.length == 0) {
1147 add('empty');
1148 } else {
1149 // Using some bits from the keys hash tag groups the names Maps with the
1150 // same structure.
1151 add(getHashTag(constant.keyList, 2) + getHashTag(constant, 3));
1152 }
1153 }
1154
1155 visitConstructed(ConstructedConstantValue constant) {
1156 addRoot(constant.type.element.name);
1157 for (int i = 0; i < constant.fields.length; i++) {
1158 _visit(constant.fields[i]);
1159 if (failed) return;
1160 }
1161 }
1162
1163 visitType(TypeConstantValue constant) {
1164 addRoot('Type');
1165 DartType type = constant.representedType;
1166 JavaScriptBackend backend = compiler.backend;
1167 String name = backend.rti.getTypeRepresentationForTypeConstant(type);
1168 addIdentifier(name);
1169 }
1170
1171 visitInterceptor(InterceptorConstantValue constant) {
1172 addRoot(constant.dispatchedType.element.name);
1173 add('methods');
1174 }
1175
1176 visitDummy(DummyConstantValue constant) {
1177 add('dummy_receiver');
1178 }
1179
1180 visitDeferred(DeferredConstantValue constant) {
1181 addRoot('Deferred');
1182 }
1183 }
1184
1185 /**
1186 * Generates canonical hash values for [ConstantValue]s.
1187 *
1188 * Unfortunately, [Constant.hashCode] is not stable under minor perturbations,
1189 * so it can't be used for generating names. This hasher keeps consistency
1190 * between runs by basing hash values of the names of elements, rather than
1191 * their hashCodes.
1192 */
1193 class ConstantCanonicalHasher implements ConstantValueVisitor<int> {
1194
1195 static const _MASK = 0x1fffffff;
1196 static const _UINT32_LIMIT = 4 * 1024 * 1024 * 1024;
1197
1198
1199 final Compiler compiler;
1200 final Map<ConstantValue, int> hashes = new Map<ConstantValue, int>();
1201
1202 ConstantCanonicalHasher(this.compiler);
1203
1204 int getHash(ConstantValue constant) => _visit(constant);
1205
1206 int _visit(ConstantValue constant) {
1207 int hash = hashes[constant];
1208 if (hash == null) {
1209 hash = _finish(constant.accept(this));
1210 hashes[constant] = hash;
1211 }
1212 return hash;
1213 }
1214
1215 int visitNull(NullConstantValue constant) => 1;
1216 int visitTrue(TrueConstantValue constant) => 2;
1217 int visitFalse(FalseConstantValue constant) => 3;
1218
1219 int visitFunction(FunctionConstantValue constant) {
1220 return _hashString(1, constant.element.name);
1221 }
1222
1223 int visitInt(IntConstantValue constant) => _hashInt(constant.primitiveValue);
1224
1225 int visitDouble(DoubleConstantValue constant) {
1226 return _hashDouble(constant.primitiveValue);
1227 }
1228
1229 int visitString(StringConstantValue constant) {
1230 return _hashString(2, constant.primitiveValue.slowToString());
1231 }
1232
1233 int visitList(ListConstantValue constant) {
1234 return _hashList(constant.length, constant.entries);
1235 }
1236
1237 int visitMap(MapConstantValue constant) {
1238 int hash = _hashList(constant.length, constant.keys);
1239 return _hashList(hash, constant.values);
1240 }
1241
1242 int visitConstructed(ConstructedConstantValue constant) {
1243 int hash = _hashString(3, constant.type.element.name);
1244 for (int i = 0; i < constant.fields.length; i++) {
1245 hash = _combine(hash, _visit(constant.fields[i]));
1246 }
1247 return hash;
1248 }
1249
1250 int visitType(TypeConstantValue constant) {
1251 DartType type = constant.representedType;
1252 JavaScriptBackend backend = compiler.backend;
1253 String name = backend.rti.getTypeRepresentationForTypeConstant(type);
1254 return _hashString(4, name);
1255 }
1256
1257 visitInterceptor(InterceptorConstantValue constant) {
1258 String typeName = constant.dispatchedType.element.name;
1259 return _hashString(5, typeName);
1260 }
1261
1262 visitDummy(DummyConstantValue constant) {
1263 compiler.internalError(NO_LOCATION_SPANNABLE,
1264 'DummyReceiverConstant should never be named and never be subconstant');
1265 }
1266
1267 visitDeferred(DeferredConstantValue constant) {
1268 int hash = constant.prefix.hashCode;
1269 return _combine(hash, constant.referenced.accept(this));
1270 }
1271
1272 int _hashString(int hash, String s) {
1273 int length = s.length;
1274 hash = _combine(hash, length);
1275 // Increasing stride is O(log N) on large strings which are unlikely to have
1276 // many collisions.
1277 for (int i = 0; i < length; i += 1 + (i >> 2)) {
1278 hash = _combine(hash, s.codeUnitAt(i));
1279 }
1280 return hash;
1281 }
1282
1283 int _hashList(int hash, List<ConstantValue> constants) {
1284 for (ConstantValue constant in constants) {
1285 hash = _combine(hash, _visit(constant));
1286 }
1287 return hash;
1288 }
1289
1290 static int _hashInt(int value) {
1291 if (value.abs() < _UINT32_LIMIT) return _MASK & value;
1292 return _hashDouble(value.toDouble());
1293 }
1294
1295 static int _hashDouble(double value) {
1296 double magnitude = value.abs();
1297 int sign = value < 0 ? 1 : 0;
1298 if (magnitude < _UINT32_LIMIT) { // 2^32
1299 int intValue = value.toInt();
1300 // Integer valued doubles in 32-bit range hash to the same values as ints.
1301 int hash = _hashInt(intValue);
1302 if (value == intValue) return hash;
1303 hash = _combine(hash, sign);
1304 int fraction = ((magnitude - intValue.abs()) * (_MASK + 1)).toInt();
1305 hash = _combine(hash, fraction);
1306 return hash;
1307 } else if (value.isInfinite) {
1308 return _combine(6, sign);
1309 } else if (value.isNaN) {
1310 return 7;
1311 } else {
1312 int hash = 0;
1313 while (magnitude >= _UINT32_LIMIT) {
1314 magnitude = magnitude / _UINT32_LIMIT;
1315 hash++;
1316 }
1317 hash = _combine(hash, sign);
1318 return _combine(hash, _hashDouble(magnitude));
1319 }
1320 }
1321
1322 /**
1323 * [_combine] and [_finish] are parts of the [Jenkins hash function][1],
1324 * modified by using masking to keep values in SMI range.
1325 *
1326 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function
1327 */
1328 static int _combine(int hash, int value) {
1329 hash = _MASK & (hash + value);
1330 hash = _MASK & (hash + (((_MASK >> 10) & hash) << 10));
1331 hash = hash ^ (hash >> 6);
1332 return hash;
1333 }
1334
1335 static int _finish(int hash) {
1336 hash = _MASK & (hash + (((_MASK >> 3) & hash) << 3));
1337 hash = hash & (hash >> 11);
1338 return _MASK & (hash + (((_MASK >> 15) & hash) << 15));
1339 }
1340 }
1341
1342 class FunctionTypeNamer extends DartTypeVisitor {
1343 final Compiler compiler;
1344 StringBuffer sb;
1345
1346 FunctionTypeNamer(this.compiler);
1347
1348 JavaScriptBackend get backend => compiler.backend;
1349
1350 String computeName(DartType type) {
1351 sb = new StringBuffer();
1352 visit(type);
1353 return sb.toString();
1354 }
1355
1356 visit(DartType type) {
1357 type.accept(this, null);
1358 }
1359
1360 visitType(DartType type, _) {
1361 sb.write(type.name);
1362 }
1363
1364 visitFunctionType(FunctionType type, _) {
1365 if (backend.rti.isSimpleFunctionType(type)) {
1366 sb.write('args${type.parameterTypes.length}');
1367 return;
1368 }
1369 visit(type.returnType);
1370 sb.write('_');
1371 for (DartType parameter in type.parameterTypes) {
1372 sb.write('_');
1373 visit(parameter);
1374 }
1375 bool first = false;
1376 for (DartType parameter in type.optionalParameterTypes) {
1377 if (!first) {
1378 sb.write('_');
1379 }
1380 sb.write('_');
1381 visit(parameter);
1382 first = true;
1383 }
1384 if (!type.namedParameterTypes.isEmpty) {
1385 first = false;
1386 for (DartType parameter in type.namedParameterTypes) {
1387 if (!first) {
1388 sb.write('_');
1389 }
1390 sb.write('_');
1391 visit(parameter);
1392 first = true;
1393 }
1394 }
1395 }
1396 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698