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

Side by Side Diff: pkg/compiler/lib/src/js_backend/namer.dart

Issue 917083003: Revert "dart2js: Refactoring, documentation, and a few bugfixes in Namer class." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of js_backend; 5 part of js_backend;
6 6
7 /** 7 /**
8 * Assigns JavaScript identifiers to Dart variables, class-names and members. 8 * Assigns JavaScript identifiers to Dart variables, class-names and members.
9 *
10 * Names are generated through three stages:
11 *
12 * 1. Original names and proposed names
13 * 2. Disambiguated names (also known as "mangled names")
14 * 3. Annotated names
15 *
16 * Original names are names taken directly from the input.
17 *
18 * Proposed names are either original names or synthesized names for input
19 * elements that do not have original names.
20 *
21 * Disambiguated names are derived from the above, but are mangled to ensure
22 * uniqueness within some namespace (e.g. as fields on the same JS object).
23 * In [MinifyNamer], disambiguated names are also minified.
24 *
25 * Annotated names are names generated from a disambiguated name. Annnotated
26 * names must be computable at runtime by prefixing/suffixing constant strings
27 * onto the disambiguated name.
28 *
29 * For example, some entity called `x` might be associated with these names:
30 *
31 * Original name: `x`
32 *
33 * Disambiguated name: `x1` (if something else was called `x`)
34 *
35 * Annotated names: `x1` (field name)
36 * `get$x1` (getter name)
37 * `set$x1` (setter name)
38 *
39 * The [Namer] can choose the disambiguated names, and to some degree the
40 * prefix/suffix constants used to construct annotated names. It cannot choose
41 * annotated names with total freedom, for example, it cannot choose that the
42 * getter for `x1` should be called `getX` -- the annotated names are always
43 * built by concatenation.
44 *
45 * Disambiguated names must be chosen such that none of the annotated names can
46 * clash with each other. This may happen even if the disambiguated names are
47 * distinct, for example, suppose a field `x` and `get$x` exists in the input:
48 *
49 * Original names: `x` and `get$x`
50 *
51 * Disambiguated names: `x` and `get$x` (the two names a different)
52 *
53 * Annotated names: `x` (field for `x`)
54 * `get$x` (getter for `x`)
55 * `get$x` (field for `get$x`)
56 * `get$get$x` (getter for `get$x`)
57 *
58 * The getter for `x` clashes with the field name for `get$x`, so the
59 * disambiguated names are invalid.
60 *
61 * Additionally, disambiguated names must be chosen such that all annotated
62 * names are valid JavaScript identifiers and do not coincide with a native
63 * JavaScript property such as `__proto__`.
64 *
65 * The following annotated names are generated for instance members, where
66 * <NAME> denotes the disambiguated name.
67 *
68 * 0. The disambiguated name can itself be seen as an annotated name.
69 *
70 * 1. Multiple annotated names exist for the `call` method, encoding arity and
71 * named parameters with the pattern:
72 *
73 * call$<N>$namedParam1...$namedParam<M>
74 *
75 * where <N> is the number of parameters (required and optional) and <M> is
76 * the number of named parameters, and namedParam<n> are the names of the
77 * named parameters in alphabetical order.
78 *
79 * Note that the same convention is used for the *proposed name* of other
80 * methods. Thus, for ordinary methods, the suffix becomes embedded in the
81 * disambiguated name (and can be minified), whereas for the 'call' method,
82 * the suffix is an annotation that must be computable at runtime
83 * (and thus cannot be minified).
84 *
85 * Note that the ordering of named parameters is not encapsulated in the
86 * [Namer], and is hardcoded into other components, such as [Element] and
87 * [Selector].
88 *
89 * 2. The getter/setter for a field:
90 *
91 * get$<NAME>
92 * set$<NAME>
93 *
94 * (The [getterPrefix] and [setterPrefix] are different in [MinifyNamer]).
95 *
96 * 3. The `is` and operator uses the following names:
97 *
98 * $is<NAME>
99 * $as<NAME>
100 *
101 * For local variables, the [Namer] only provides *proposed names*. These names
102 * must be disambiguated elsewhere.
103 */ 9 */
104 class Namer implements ClosureNamer { 10 class Namer implements ClosureNamer {
105 11
106 static const List<String> javaScriptKeywords = const <String>[ 12 static const javaScriptKeywords = const <String>[
107 // These are current keywords. 13 // These are current keywords.
108 "break", "delete", "function", "return", "typeof", "case", "do", "if", 14 "break", "delete", "function", "return", "typeof", "case", "do", "if",
109 "switch", "var", "catch", "else", "in", "this", "void", "continue", 15 "switch", "var", "catch", "else", "in", "this", "void", "continue",
110 "false", "instanceof", "throw", "while", "debugger", "finally", "new", 16 "false", "instanceof", "throw", "while", "debugger", "finally", "new",
111 "true", "with", "default", "for", "null", "try", 17 "true", "with", "default", "for", "null", "try",
112 18
113 // These are future keywords. 19 // These are future keywords.
114 "abstract", "double", "goto", "native", "static", "boolean", "enum", 20 "abstract", "double", "goto", "native", "static", "boolean", "enum",
115 "implements", "package", "super", "byte", "export", "import", "private", 21 "implements", "package", "super", "byte", "export", "import", "private",
116 "synchronized", "char", "extends", "int", "protected", "throws", 22 "synchronized", "char", "extends", "int", "protected", "throws",
117 "class", "final", "interface", "public", "transient", "const", "float", 23 "class", "final", "interface", "public", "transient", "const", "float",
118 "long", "short", "volatile" 24 "long", "short", "volatile"
119 ]; 25 ];
120 26
121 static const List<String> reservedPropertySymbols = 27 static const reservedPropertySymbols =
122 const <String>["__proto__", "prototype", "constructor", "call"]; 28 const <String>["__proto__", "prototype", "constructor", "call"];
123 29
124 // Symbols that we might be using in our JS snippets. 30 // Symbols that we might be using in our JS snippets.
125 static const List<String> reservedGlobalSymbols = const <String>[ 31 static const reservedGlobalSymbols = const <String>[
126 // Section references are from Ecma-262 32 // Section references are from Ecma-262
127 // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pd f) 33 // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pd f)
128 34
129 // 15.1.1 Value Properties of the Global Object 35 // 15.1.1 Value Properties of the Global Object
130 "NaN", "Infinity", "undefined", 36 "NaN", "Infinity", "undefined",
131 37
132 // 15.1.2 Function Properties of the Global Object 38 // 15.1.2 Function Properties of the Global Object
133 "eval", "parseInt", "parseFloat", "isNaN", "isFinite", 39 "eval", "parseInt", "parseFloat", "isNaN", "isFinite",
134 40
135 // 15.1.3 URI Handling Function Properties 41 // 15.1.3 URI Handling Function Properties
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea", 141 "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
236 "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer", 142 "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
237 "XPathException", "XPathResult", "XSLTProcessor", 143 "XPathException", "XPathResult", "XSLTProcessor",
238 144
239 // These keywords trigger the loading of the java-plugin. For the 145 // These keywords trigger the loading of the java-plugin. For the
240 // next-generation plugin, this results in starting a new Java process. 146 // next-generation plugin, this results in starting a new Java process.
241 "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass", 147 "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
242 "JavaArray", "JavaMember", 148 "JavaArray", "JavaMember",
243 ]; 149 ];
244 150
245 static const List<String> reservedGlobalObjectNames = const <String>[ 151 static const reservedGlobalObjectNames = const <String>[
246 "A", 152 "A",
247 "B", 153 "B",
248 "C", // Global object for *C*onstants. 154 "C", // Global object for *C*onstants.
249 "D", 155 "D",
250 "E", 156 "E",
251 "F", 157 "F",
252 "G", 158 "G",
253 "H", // Global object for internal (*H*elper) libraries. 159 "H", // Global object for internal (*H*elper) libraries.
254 // I is used for used for the Isolate function. 160 // I is used for used for the Isolate function.
255 "J", // Global object for the interceptor library. 161 "J", // Global object for the interceptor library.
256 "K", 162 "K",
257 "L", 163 "L",
258 "M", 164 "M",
259 "N", 165 "N",
260 "O", 166 "O",
261 "P", // Global object for other *P*latform libraries. 167 "P", // Global object for other *P*latform libraries.
262 "Q", 168 "Q",
263 "R", 169 "R",
264 "S", 170 "S",
265 "T", 171 "T",
266 "U", 172 "U",
267 "V", 173 "V",
268 "W", // Global object for *W*eb libraries (dart:html). 174 "W", // Global object for *W*eb libraries (dart:html).
269 "X", 175 "X",
270 "Y", 176 "Y",
271 "Z", 177 "Z",
272 ]; 178 ];
273 179
274 static const List<String> reservedGlobalHelperFunctions = const <String>[ 180 static const reservedGlobalHelperFunctions = const <String>[
275 "init", 181 "init",
276 "Isolate", 182 "Isolate",
277 ]; 183 ];
278 184
279 static final List<String> userGlobalObjects = 185 static final userGlobalObjects = new List.from(reservedGlobalObjectNames)
280 new List.from(reservedGlobalObjectNames)
281 ..remove('C') 186 ..remove('C')
282 ..remove('H') 187 ..remove('H')
283 ..remove('J') 188 ..remove('J')
284 ..remove('P') 189 ..remove('P')
285 ..remove('W'); 190 ..remove('W');
286 191
287 Set<String> _jsReserved = null; 192 Set<String> _jsReserved = null;
288 /// Names that cannot be used by members, top level and static 193 /// Names that cannot be used by members, top level and static
289 /// methods. 194 /// methods.
290 Set<String> get jsReserved { 195 Set<String> get jsReserved {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 final String reflectableField = r'$reflectable'; 227 final String reflectableField = r'$reflectable';
323 final String reflectionInfoField = r'$reflectionInfo'; 228 final String reflectionInfoField = r'$reflectionInfo';
324 final String reflectionNameField = r'$reflectionName'; 229 final String reflectionNameField = r'$reflectionName';
325 final String metadataIndexField = r'$metadataIndex'; 230 final String metadataIndexField = r'$metadataIndex';
326 final String defaultValuesField = r'$defaultValues'; 231 final String defaultValuesField = r'$defaultValues';
327 final String methodsWithOptionalArgumentsField = 232 final String methodsWithOptionalArgumentsField =
328 r'$methodsWithOptionalArguments'; 233 r'$methodsWithOptionalArguments';
329 234
330 final String classDescriptorProperty = r'^'; 235 final String classDescriptorProperty = r'^';
331 236
332 /// The non-minifying namer's [callPrefix] with a dollar after it.
333 static const String _callPrefixDollar = r'call$';
334
335 // Name of property in a class description for the native dispatch metadata. 237 // Name of property in a class description for the native dispatch metadata.
336 final String nativeSpecProperty = '%'; 238 final String nativeSpecProperty = '%';
337 239
338 static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$'); 240 static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$');
339 static final RegExp NON_IDENTIFIER_CHAR = new RegExp(r'[^A-Za-z_0-9$]'); 241 static final RegExp NON_IDENTIFIER_CHAR = new RegExp(r'[^A-Za-z_0-9$]');
340 242
243 /**
244 * Map from top-level or static elements to their unique identifiers provided
245 * by [getName].
246 *
247 * Invariant: Keys must be declaration elements.
248 */
341 final Compiler compiler; 249 final Compiler compiler;
250 final Map<Element, String> globals;
251 final Map<String, LibraryElement> shortPrivateNameOwners;
342 252
343 /// Used disambiguated names in the global namespace, issued by 253 final Set<String> usedGlobalNames;
344 /// [_disambiguateGlobal], and [_disambiguateInternalGlobal]. 254 final Set<String> usedInstanceNames;
345 /// 255 final Map<String, String> globalNameMap;
346 /// Although global names are distributed across a number of global objects, 256 final Map<String, String> suggestedGlobalNames;
347 /// (see [globalObjectFor]), we currently use a single namespace for all these 257 final Map<String, String> instanceNameMap;
348 /// names. 258 final Map<String, String> suggestedInstanceNames;
349 final Set<String> usedGlobalNames = new Set<String>();
350 final Map<Element, String> userGlobals = <Element, String>{};
351 final Map<String, String> internalGlobals = <String, String>{};
352 259
353 /// Used disambiguated names in the instance namespace, issued by 260 final Map<String, String> operatorNameMap;
354 /// [_disambiguateMember], [_disambiguateInternalMember], 261 final Map<String, int> popularNameCounters;
355 /// [_disambiguateOperator], and [reservePublicMemberName].
356 final Set<String> usedInstanceNames = new Set<String>();
357 final Map<String, String> userInstanceMembers = <String, String>{};
358 final Map<Element, String> internalInstanceMembers = <Element, String>{};
359 final Map<String, String> userInstanceOperators = <String, String>{};
360 262
361 final Map<String, int> popularNameCounters = <String, int>{}; 263 final Map<ConstantValue, String> constantNames;
362 264 final Map<ConstantValue, String> constantLongNames;
363 final Map<ConstantValue, String> constantNames = <ConstantValue, String>{};
364 final Map<ConstantValue, String> constantLongNames =
365 <ConstantValue, String>{};
366 ConstantCanonicalHasher constantHasher; 265 ConstantCanonicalHasher constantHasher;
367 266
368 /// Maps private names to a library that may use that name without prefixing
369 /// itself. Used for building proposed names.
370 final Map<String, LibraryElement> shortPrivateNameOwners =
371 <String, LibraryElement>{};
372
373 /// Maps proposed names to *suggested* disambiguated names.
374 ///
375 /// Suggested names are hints to the [MinifyNamer], suggesting that a specific
376 /// names be given to the first item with the given proposed name.
377 ///
378 /// This is currently used in [MinifyNamer] to assign very short minified
379 /// names to things that tend to be used very often.
380 final Map<String, String> suggestedGlobalNames = <String, String>{};
381 final Map<String, String> suggestedInstanceNames = <String, String>{};
382
383 // All alphanumeric characters. 267 // All alphanumeric characters.
384 static const String _alphaNumeric = 268 static const String _alphaNumeric =
385 'abcdefghijklmnopqrstuvwxyzABZDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 269 'abcdefghijklmnopqrstuvwxyzABZDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
386 270
387 Namer(Compiler compiler) 271 Namer(Compiler compiler)
388 : compiler = compiler, 272 : compiler = compiler,
273 globals = new Map<Element, String>(),
274 shortPrivateNameOwners = new Map<String, LibraryElement>(),
275 usedGlobalNames = new Set<String>(),
276 usedInstanceNames = new Set<String>(),
277 instanceNameMap = new Map<String, String>(),
278 operatorNameMap = new Map<String, String>(),
279 globalNameMap = new Map<String, String>(),
280 suggestedGlobalNames = new Map<String, String>(),
281 suggestedInstanceNames = new Map<String, String>(),
282 popularNameCounters = new Map<String, int>(),
283 constantNames = new Map<ConstantValue, String>(),
284 constantLongNames = new Map<ConstantValue, String>(),
389 constantHasher = new ConstantCanonicalHasher(compiler), 285 constantHasher = new ConstantCanonicalHasher(compiler),
390 functionTypeNamer = new FunctionTypeNamer(compiler); 286 functionTypeNamer = new FunctionTypeNamer(compiler);
391 287
392 JavaScriptBackend get backend => compiler.backend; 288 JavaScriptBackend get backend => compiler.backend;
393 289
394 String get isolateName => 'Isolate'; 290 String get isolateName => 'Isolate';
395 String get isolatePropertiesName => r'$isolateProperties'; 291 String get isolatePropertiesName => r'$isolateProperties';
396 String get noSuchMethodName => publicInstanceMethodNameByArity( 292 String get noSuchMethodName => publicInstanceMethodNameByArity(
397 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); 293 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
398 /** 294 /**
(...skipping 13 matching lines...) Expand all
412 case 'REFLECTABLE': return reflectableField; 308 case 'REFLECTABLE': return reflectableField;
413 case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty; 309 case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty;
414 default: 310 default:
415 compiler.reportError( 311 compiler.reportError(
416 node, MessageKind.GENERIC, 312 node, MessageKind.GENERIC,
417 {'text': 'Error: Namer has no name for "$name".'}); 313 {'text': 'Error: Namer has no name for "$name".'});
418 return 'BROKEN'; 314 return 'BROKEN';
419 } 315 }
420 } 316 }
421 317
422 /// Disambiguated name for [constant].
423 ///
424 /// Unique within the global-member namespace.
425 String constantName(ConstantValue constant) { 318 String constantName(ConstantValue constant) {
426 // In the current implementation it doesn't make sense to give names to 319 // In the current implementation it doesn't make sense to give names to
427 // function constants since the function-implementation itself serves as 320 // function constants since the function-implementation itself serves as
428 // constant and can be accessed directly. 321 // constant and can be accessed directly.
429 assert(!constant.isFunction); 322 assert(!constant.isFunction);
430 String result = constantNames[constant]; 323 String result = constantNames[constant];
431 if (result == null) { 324 if (result == null) {
432 String longName = constantLongName(constant); 325 String longName = constantLongName(constant);
433 result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames); 326 result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames,
327 ensureSafe: true);
434 constantNames[constant] = result; 328 constantNames[constant] = result;
435 } 329 }
436 return result; 330 return result;
437 } 331 }
438 332
439 /// Proposed name for [constant]. 333 // The long name is unminified and may have collisions.
440 String constantLongName(ConstantValue constant) { 334 String constantLongName(ConstantValue constant) {
441 String longName = constantLongNames[constant]; 335 String longName = constantLongNames[constant];
442 if (longName == null) { 336 if (longName == null) {
443 longName = new ConstantNamingVisitor(compiler, constantHasher) 337 longName = new ConstantNamingVisitor(compiler, constantHasher)
444 .getName(constant); 338 .getName(constant);
445 constantLongNames[constant] = longName; 339 constantLongNames[constant] = longName;
446 } 340 }
447 return longName; 341 return longName;
448 } 342 }
449 343
450 String breakLabelName(LabelDefinition label) { 344 String breakLabelName(LabelDefinition label) {
451 return '\$${label.labelName}\$${label.target.nestingLevel}'; 345 return '\$${label.labelName}\$${label.target.nestingLevel}';
452 } 346 }
453 347
454 String implicitBreakLabelName(JumpTarget target) { 348 String implicitBreakLabelName(JumpTarget target) {
455 return '\$${target.nestingLevel}'; 349 return '\$${target.nestingLevel}';
456 } 350 }
457 351
458 // We sometimes handle continue targets differently from break targets, 352 // We sometimes handle continue targets differently from break targets,
459 // so we have special continue-only labels. 353 // so we have special continue-only labels.
460 String continueLabelName(LabelDefinition label) { 354 String continueLabelName(LabelDefinition label) {
461 return 'c\$${label.labelName}\$${label.target.nestingLevel}'; 355 return 'c\$${label.labelName}\$${label.target.nestingLevel}';
462 } 356 }
463 357
464 String implicitContinueLabelName(JumpTarget target) { 358 String implicitContinueLabelName(JumpTarget target) {
465 return 'c\$${target.nestingLevel}'; 359 return 'c\$${target.nestingLevel}';
466 } 360 }
467 361
468 /** 362 /**
469 * If the [originalName] is not private returns [originalName]. Otherwise 363 * If the [name] is not private returns [:name:]. Otherwise
470 * mangles the [originalName] so that each library has its own distinguished 364 * mangles the [name] so that each library has a unique name.
471 * version of the name.
472 *
473 * Although the name is not guaranteed to be unique within any namespace,
474 * clashes are very unlikely in practice. Therefore, it can be used in cases
475 * where uniqueness is nice but not a strict requirement.
476 *
477 * The resulting name is a *proposed name* and is never minified.
478 */ 365 */
479 String privateName(LibraryElement library, String originalName) { 366 String privateName(LibraryElement library, String name) {
480 // Public names are easy. 367 // Public names are easy.
481 if (!isPrivateName(originalName)) return originalName; 368 String nameString = name;
369 if (!isPrivateName(name)) return nameString;
482 370
483 // The first library asking for a short private name wins. 371 // The first library asking for a short private name wins.
484 LibraryElement owner = 372 LibraryElement owner =
485 shortPrivateNameOwners.putIfAbsent(originalName, () => library); 373 shortPrivateNameOwners.putIfAbsent(nameString, () => library);
486 374
487 if (owner == library) { 375 if (owner == library && !nameString.contains('\$')) {
488 return originalName; 376 // Since the name doesn't contain $ it doesn't clash with any
377 // of the private names that have the library name as the prefix.
378 return nameString;
489 } else { 379 } else {
490 // Make sure to return a private name that starts with _ so it 380 // Make sure to return a private name that starts with _ so it
491 // cannot clash with any public names. 381 // cannot clash with any public names.
492 // The name is still not guaranteed to be unique, since both the library 382 String libraryName = getNameOfLibrary(library);
493 // name and originalName could contain $ symbols. 383 return '_$libraryName\$$nameString';
494 String libraryName = _disambiguateGlobal(library);
495 return '_$libraryName\$${originalName}';
496 } 384 }
497 } 385 }
498 386
499 String _proposeNameForConstructorBody(ConstructorBodyElement method) { 387 String instanceMethodName(FunctionElement element) {
500 String name = Elements.reconstructConstructorNameSourceString(method); 388 // TODO(ahe): Could this be: return invocationName(new
501 // We include the method suffix on constructor bodies. It has no purpose, 389 // Selector.fromElement(element))?
502 // but this way it produces the same names as previous versions of the 390 String elementName = element.name;
503 // Namer class did. 391 String name = operatorNameToIdentifier(elementName);
504 List<String> suffix = callSuffixForSignature(method.functionSignature); 392 if (name != elementName) return getMappedOperatorName(name);
505 return '$name\$${suffix.join(r'$')}'; 393
394 LibraryElement library = element.library;
395 if (element.isGenerativeConstructorBody) {
396 name = Elements.reconstructConstructorNameSourceString(element);
397 }
398 FunctionSignature signature = element.functionSignature;
399 // We don't mangle the closure invoking function name because it
400 // is generated by string concatenation in applyFunction from
401 // js_helper.dart. To keep code size down, we potentially shorten
402 // the prefix though.
403 String methodName;
404 if (name == closureInvocationSelectorName) {
405 methodName = '$callPrefix\$${signature.parameterCount}';
406 } else {
407 methodName = '${privateName(library, name)}\$${signature.parameterCount}';
408 }
409 if (signature.optionalParametersAreNamed &&
410 !signature.optionalParameters.isEmpty) {
411 StringBuffer buffer = new StringBuffer();
412 signature.orderedOptionalParameters.forEach((Element element) {
413 buffer.write('\$${safeName(element.name)}');
414 });
415 methodName = '$methodName$buffer';
416 }
417 if (name == closureInvocationSelectorName) return methodName;
418 return getMappedInstanceName(methodName);
506 } 419 }
507 420
508 /// Annotated name for [method] encoding arity and named parameters. 421 String publicInstanceMethodNameByArity(String name, int arity) {
509 String instanceMethodName(FunctionElement method) { 422 String newName = operatorNameToIdentifier(name);
510 if (method.isGenerativeConstructorBody) { 423 if (newName != name) return getMappedOperatorName(newName);
511 return _disambiguateInternalMember(method, 424 assert(!isPrivateName(name));
512 () => _proposeNameForConstructorBody(method)); 425 // We don't mangle the closure invoking function name because it
513 } 426 // is generated by string concatenation in applyFunction from
514 return invocationName(new Selector.fromElement(method)); 427 // js_helper.dart. To keep code size down, we potentially shorten
428 // the prefix though.
429 if (name == closureInvocationSelectorName) return '$callPrefix\$$arity';
430
431 return getMappedInstanceName('$name\$$arity');
515 } 432 }
516 433
517 /// Annotated name for a public method with the given [originalName] 434 String invocationName(Selector selector) {
518 /// and [arity] and no named parameters. 435 if (selector.isGetter) {
519 String publicInstanceMethodNameByArity(String originalName, int arity) { 436 String proposedName = privateName(selector.library, selector.name);
520 return invocationName(new Selector.call(originalName, null, arity)); 437 return '$getterPrefix${getMappedInstanceName(proposedName)}';
521 } 438 } else if (selector.isSetter) {
522 439 String proposedName = privateName(selector.library, selector.name);
523 /// Returns the annotated name for a variant of `call` that takes [arity] 440 return '$setterPrefix${getMappedInstanceName(proposedName)}';
524 /// positional parameters and no named parameters. 441 } else {
525 /// The result has the form: 442 String name = selector.name;
526 /// 443 if (selector.kind == SelectorKind.OPERATOR
527 /// call$<N>$namedParam1...$namedParam<M> 444 || selector.kind == SelectorKind.INDEX) {
528 /// 445 name = operatorNameToIdentifier(name);
529 /// This name cannot be minified because it is generated by string 446 assert(name != selector.name);
530 /// concatenation at runtime, by applyFunction in js_helper.dart. 447 return getMappedOperatorName(name);
531 String deriveCallMethodName(List<String> suffix) {
532 // TODO(asgerf): Avoid clashes when named parameters contain $ symbols.
533 return '$callPrefix\$${suffix.join(r'$')}';
534 }
535
536 /// The suffix list for the pattern:
537 ///
538 /// $<N>$namedParam1...$namedParam<M>
539 ///
540 /// This is used for the annotated names of `call`, and for the proposed name
541 /// for other instance methods.
542 List<String> callSuffixForSelector(Selector selector) {
543 List<String> suffixes = ['${selector.argumentCount}'];
544 suffixes.addAll(selector.getOrderedNamedArguments());
545 return suffixes;
546 }
547
548 /// The suffix list for the pattern:
549 ///
550 /// $<N>$namedParam1...$namedParam<M>
551 ///
552 /// This is used for the annotated names of `call`, and for the proposed name
553 /// for other instance methods.
554 List<String> callSuffixForSignature(FunctionSignature sig) {
555 List<String> suffixes = ['${sig.parameterCount}'];
556 if (sig.optionalParametersAreNamed) {
557 for (FormalElement param in sig.orderedOptionalParameters) {
558 suffixes.add(param.name);
559 } 448 }
560 } 449 assert(name == operatorNameToIdentifier(name));
561 return suffixes; 450 StringBuffer buffer = new StringBuffer();
562 } 451 for (String argumentName in selector.getOrderedNamedArguments()) {
563 452 buffer.write('\$${safeName(argumentName)}');
564 /// Annotated name for the member being invoked by [selector]. 453 }
565 String invocationName(Selector selector) { 454 String suffix = '\$${selector.argumentCount}$buffer';
566 LibraryElement library = selector.library; 455 // We don't mangle the closure invoking function name because it
567 switch (selector.kind) { 456 // is generated by string concatenation in applyFunction from
568 case SelectorKind.GETTER: 457 // js_helper.dart. We potentially shorten the prefix though.
569 String disambiguatedName = _disambiguateMember(library, selector.name); 458 if (selector.isClosureCall) {
570 return deriveGetterName(disambiguatedName); 459 return "$callPrefix$suffix";
571 460 } else {
572 case SelectorKind.SETTER: 461 String proposedName = privateName(selector.library, name);
573 String disambiguatedName = _disambiguateMember(library, selector.name); 462 return getMappedInstanceName('$proposedName$suffix');
574 return deriveSetterName(disambiguatedName); 463 }
575
576 case SelectorKind.OPERATOR:
577 case SelectorKind.INDEX:
578 String operatorIdentifier = operatorNameToIdentifier(selector.name);
579 String disambiguatedName = _disambiguateOperator(operatorIdentifier);
580 return disambiguatedName; // Operators are not annotated.
581
582 case SelectorKind.CALL:
583 List<String> suffix = callSuffixForSelector(selector);
584 if (selector.name == Compiler.CALL_OPERATOR_NAME) {
585 // Derive the annotated name for this variant of 'call'.
586 return deriveCallMethodName(suffix);
587 }
588 String disambiguatedName =
589 _disambiguateMember(library, selector.name, suffix);
590 return disambiguatedName; // Methods other than call are not annotated.
591
592 default:
593 compiler.internalError(compiler.currentElement,
594 'Unexpected selector kind: ${selector.kind}');
595 return null;
596 } 464 }
597 } 465 }
598 466
599 /** 467 /**
600 * Returns the internal name used for an invocation mirror of this selector. 468 * Returns the internal name used for an invocation mirror of this selector.
601 */ 469 */
602 String invocationMirrorInternalName(Selector selector) 470 String invocationMirrorInternalName(Selector selector)
603 => invocationName(selector); 471 => invocationName(selector);
604 472
605 /** 473 /**
606 * Returns the disambiguated name for the given field, used for constructing 474 * Returns name of accessor (root to getter and setter) for a static or
607 * the getter and setter names. 475 * instance field.
608 */ 476 */
609 String fieldAccessorName(Element element) { 477 String fieldAccessorName(Element element) {
610 return element.isInstanceMember 478 return element.isInstanceMember
611 ? _disambiguateMember(element.library, element.name) 479 ? instanceFieldAccessorName(element)
612 : _disambiguateGlobal(element); 480 : getNameOfField(element);
613 } 481 }
614 482
615 /** 483 /**
616 * Returns name of the JavaScript property used to store a static or instance 484 * Returns name of the JavaScript property used to store a static or instance
617 * field. 485 * field.
618 */ 486 */
619 String fieldPropertyName(Element element) { 487 String fieldPropertyName(Element element) {
620 return element.isInstanceMember 488 return element.isInstanceMember
621 ? instanceFieldPropertyName(element) 489 ? instanceFieldPropertyName(element)
622 : _disambiguateGlobal(element); 490 : getNameOfField(element);
623 } 491 }
624 492
625 /** 493 /**
626 * Returns name of the JavaScript property used to store the 494 * Returns name of accessor (root to getter and setter) for an instance field.
627 * `readTypeVariable` function for the given type variable.
628 */ 495 */
629 String nameForReadTypeVariable(TypeVariableElement element) { 496 String instanceFieldAccessorName(Element element) {
630 return _disambiguateInternalMember(element, () => element.name); 497 String proposedName = privateName(element.library, element.name);
498 return getMappedInstanceName(proposedName);
499 }
500
501 String readTypeVariableName(TypeVariableElement element) {
502 return '\$tv_${instanceFieldAccessorName(element)}';
631 } 503 }
632 504
633 /** 505 /**
634 * Returns a JavaScript property name used to store [element] on one 506 * Returns name of the JavaScript property used to store an instance field.
635 * of the global objects.
636 *
637 * Should be used together with [globalObjectFor], which denotes the object
638 * on which the returned property name should be used.
639 */ 507 */
640 String globalPropertyName(Element element) { 508 String instanceFieldPropertyName(Element element) {
641 return _disambiguateGlobal(element); 509 if (element.hasFixedBackendName) {
510 return element.fixedBackendName;
511 }
512 // If a class is used anywhere as a mixin, we must make the name unique so
513 // that it does not accidentally shadow. Also, the mixin name must be
514 // constant over all mixins.
515 ClassWorld classWorld = compiler.world;
516 if (classWorld.isUsedAsMixin(element.enclosingClass) ||
517 shadowingAnotherField(element)) {
518 // Construct a new name for the element based on the library and class it
519 // is in. The name here is not important, we just need to make sure it is
520 // unique. If we are minifying, we actually construct the name from the
521 // minified version of the class name, but the result is minified once
522 // again, so that is not visible in the end result.
523 String libraryName = getNameOfLibrary(element.library);
524 String className = getNameOfClass(element.enclosingClass);
525 String instanceName = privateName(element.library, element.name);
526 return getMappedInstanceName('$libraryName\$$className\$$instanceName');
527 }
528
529 String proposedName = privateName(element.library, element.name);
530 return getMappedInstanceName(proposedName);
642 } 531 }
643 532
644 /**
645 * Returns the JavaScript property name used to store an instance field.
646 */
647 String instanceFieldPropertyName(Element element) {
648 ClassElement enclosingClass = element.enclosingClass;
649 533
650 if (element.hasFixedBackendName) { 534 bool shadowingAnotherField(Element element) {
651 // Box fields and certain native fields must be given a specific name.
652 assert(element is BoxFieldElement || enclosingClass.isNative);
653
654 // This name must not contain '$'. We rely on this to avoid clashes.
655 assert(!element.fixedBackendName.contains(r'$'));
656
657 return element.fixedBackendName;
658 }
659
660 // If the name of the field might clash with another field,
661 // use a mangled field name to avoid potential clashes.
662 // Note that if the class extends a native class, that native class might
663 // have fields with fixed backend names, so we assume the worst and always
664 // mangle the field names of classes extending native classes.
665 // Methods on such classes are stored on the interceptor, not the instance,
666 // so only fields have the potential to clash with a native property name.
667 ClassWorld classWorld = compiler.world;
668 if (classWorld.isUsedAsMixin(enclosingClass) ||
669 _isShadowingSuperField(element) ||
670 _isUserClassExtendingNative(enclosingClass)) {
671 String proposeName() => '${enclosingClass.name}_${element.name}';
672 return _disambiguateInternalMember(element, proposeName);
673 }
674
675 // No superclass uses the disambiguated name as a property name, so we can
676 // use it for this field. This generates nicer field names since otherwise
677 // the field name would have to be mangled.
678 return _disambiguateMember(element.library, element.name);
679 }
680
681 bool _isShadowingSuperField(Element element) {
682 return element.enclosingClass.hasFieldShadowedBy(element); 535 return element.enclosingClass.hasFieldShadowedBy(element);
683 } 536 }
684 537
685 /// True if [class_] is a non-native class that inherits from a native class. 538 String setterName(Element element) {
686 bool _isUserClassExtendingNative(ClassElement class_) { 539 // We dynamically create setters from the field-name. The setter name must
687 return !class_.isNative && 540 // therefore be derived from the instance field-name.
688 Elements.isNativeOrExtendsNative(class_.superclass); 541 LibraryElement library = element.library;
542 String name = getMappedInstanceName(privateName(library, element.name));
543 return '$setterPrefix$name';
689 } 544 }
690 545
691 /// Annotated name for the setter of [element]. 546 String setterNameFromAccessorName(String name) {
692 String setterForElement(Element element) {
693 // We dynamically create setters from the field-name. The setter name must 547 // We dynamically create setters from the field-name. The setter name must
694 // therefore be derived from the instance field-name. 548 // therefore be derived from the instance field-name.
695 String name = _disambiguateMember(element.library, element.name); 549 return '$setterPrefix$name';
696 return deriveSetterName(name);
697 } 550 }
698 551
699 /// Annotated name for the setter of any member with [disambiguatedName]. 552 String getterNameFromAccessorName(String name) {
700 String deriveSetterName(String disambiguatedName) { 553 // We dynamically create getters from the field-name. The getter name must
701 // We dynamically create setters from the field-name. The setter name must
702 // therefore be derived from the instance field-name. 554 // therefore be derived from the instance field-name.
703 return '$setterPrefix$disambiguatedName'; 555 return '$getterPrefix$name';
704 } 556 }
705 557
706 /// Annotated name for the setter of any member with [disambiguatedName]. 558 String getterName(Element element) {
707 String deriveGetterName(String disambiguatedName) {
708 // We dynamically create getters from the field-name. The getter name must 559 // We dynamically create getters from the field-name. The getter name must
709 // therefore be derived from the instance field-name. 560 // therefore be derived from the instance field-name.
710 return '$getterPrefix$disambiguatedName'; 561 LibraryElement library = element.library;
562 String name = getMappedInstanceName(privateName(library, element.name));
563 return '$getterPrefix$name';
711 } 564 }
712 565
713 /// Annotated name for the getter of [element]. 566 String getMappedGlobalName(String proposedName, {bool ensureSafe: true}) {
714 String getterForElement(Element element) { 567 var newName = globalNameMap[proposedName];
715 // We dynamically create getters from the field-name. The getter name must
716 // therefore be derived from the instance field-name.
717 String name = _disambiguateMember(element.library, element.name);
718 return deriveGetterName(name);
719 }
720
721 /// Property name for the getter of an instance member with [originalName]
722 /// in [library].
723 ///
724 /// [library] may be `null` if [originalName] is known to be public.
725 String getterForMember(LibraryElement library, String originalName) {
726 String disambiguatedName = _disambiguateMember(library, originalName);
727 return deriveGetterName(disambiguatedName);
728 }
729
730 /// Property name for the getter or a public instance member with
731 /// [originalName].
732 String getterForPublicMember(String originalName) {
733 return getterForMember(null, originalName);
734 }
735
736 /// Disambiguated name for a compiler-owned global variable.
737 ///
738 /// The resulting name is unique within the global-member namespace.
739 String _disambiguateInternalGlobal(String name) {
740 String newName = internalGlobals[name];
741 if (newName == null) { 568 if (newName == null) {
742 newName = getFreshName(name, usedGlobalNames, suggestedGlobalNames); 569 newName = getFreshName(proposedName, usedGlobalNames,
743 internalGlobals[name] = newName; 570 suggestedGlobalNames, ensureSafe: ensureSafe);
571 globalNameMap[proposedName] = newName;
744 } 572 }
745 return newName; 573 return newName;
746 } 574 }
747 575
748 /// Returns the property name to use for a compiler-owner global variable, 576 String getMappedInstanceName(String proposedName) {
749 /// i.e. one that does not correspond to any element but is used as a utility 577 var newName = instanceNameMap[proposedName];
750 /// global by code generation.
751 ///
752 /// [name] functions as both the proposed name for the global, and as a key
753 /// identifying the global. The [name] must not contain `$` symbols, since
754 /// the [Namer] uses those names internally.
755 ///
756 /// This provides an easy mechanism of avoiding a name-clash with user-space
757 /// globals, although the callers of must still take care not to accidentally
758 /// pass in the same [name] for two different internal globals.
759 String internalGlobal(String name) {
760 assert(!name.contains(r'$'));
761 return _disambiguateInternalGlobal(name);
762 }
763
764 /// Returns the disambiguated name for a top-level or static element.
765 ///
766 /// The resulting name is unique within the global-member namespace.
767 String _disambiguateGlobal(Element element) {
768 // TODO(asgerf): We can reuse more short names if we disambiguate with
769 // a separate namespace for each of the global holder objects.
770 element = element.declaration;
771 String newName = userGlobals[element];
772 if (newName == null) { 578 if (newName == null) {
773 String proposedName = _proposeNameForGlobal(element); 579 newName = getFreshName(proposedName, usedInstanceNames,
774 newName = getFreshName(proposedName, usedGlobalNames, 580 suggestedInstanceNames, ensureSafe: true);
775 suggestedGlobalNames); 581 instanceNameMap[proposedName] = newName;
776 userGlobals[element] = newName;
777 } 582 }
778 return newName; 583 return newName;
779 } 584 }
780 585
781 /// Returns the disambiguated name for an instance method or field 586 String getMappedOperatorName(String proposedName) {
782 /// with [originalName] in [library]. 587 var newName = operatorNameMap[proposedName];
783 ///
784 /// [library] may be `null` if [originalName] is known to be public.
785 ///
786 /// This is the name used for deriving property names of accessors (getters
787 /// and setters) and as property name for storing methods and method stubs.
788 ///
789 /// [suffixes] denote an extension of [originalName] to distiguish it from
790 /// other members with that name. These are used to encode the arity and
791 /// named parameters to a method. Disambiguating the same [originalName] with
792 /// different [suffixes] will yield different disambiguated names.
793 ///
794 /// The resulting name, and its associated annotated names, are unique
795 /// to the ([originalName], [suffixes]) pair within the instance-member
796 /// namespace.
797 String _disambiguateMember(LibraryElement library,
798 String originalName,
799 [List<String> suffixes = const []]) {
800 // For private names, a library must be given.
801 assert(isPublicName(originalName) || library != null);
802
803 // Build a string encoding the library name, if the name is private.
804 String libraryKey = isPrivateName(originalName)
805 ? _disambiguateGlobal(library)
806 : '';
807
808 // In the unique key, separate the name parts by '@'.
809 // This avoids clashes since the original names cannot contain that symbol.
810 String key = '$libraryKey@$originalName@${suffixes.join('@')}';
811 String newName = userInstanceMembers[key];
812 if (newName == null) { 588 if (newName == null) {
813 String proposedName = privateName(library, originalName); 589 newName = getFreshName(proposedName, usedInstanceNames,
814 if (!suffixes.isEmpty) { 590 suggestedInstanceNames, ensureSafe: false);
815 // In the proposed name, separate the name parts by '$', because the 591 operatorNameMap[proposedName] = newName;
816 // proposed name must be a valid identifier, but not necessarily unique.
817 proposedName += r'$' + suffixes.join(r'$');
818 }
819 newName = getFreshName(proposedName,
820 usedInstanceNames, suggestedInstanceNames,
821 sanitizeForAnnotations: true);
822 userInstanceMembers[key] = newName;
823 } 592 }
824 return newName; 593 return newName;
825 } 594 }
826 595
827 /// Forces the public instance member with [originalName] to have the given
828 /// [disambiguatedName].
829 ///
830 /// The [originalName] must not have been disambiguated before, and the
831 /// [disambiguatedName] must not have been used.
832 ///
833 /// Using [_disambiguateMember] with the given [originalName] and no suffixes
834 /// will subsequently return [disambiguatedName].
835 void reservePublicMemberName(String originalName,
836 String disambiguatedName) {
837 // Build a key that corresponds to the one built in disambiguateMember.
838 String libraryPrefix = ''; // Public names have an empty library prefix.
839 String suffix = ''; // We don't need any suffixes.
840 String key = '$libraryPrefix@$originalName@$suffix';
841 assert(!userInstanceMembers.containsKey(key));
842 assert(!usedInstanceNames.contains(disambiguatedName));
843 userInstanceMembers[key] = disambiguatedName;
844 usedInstanceNames.add(disambiguatedName);
845 }
846
847 /// Disambiguated name unique to [element].
848 ///
849 /// This is used as the property name for fields, type variables,
850 /// constructor bodies, and super-accessors.
851 ///
852 /// The resulting name is unique within the instance-member namespace.
853 String _disambiguateInternalMember(Element element, String proposeName()) {
854 String newName = internalInstanceMembers[element];
855 if (newName == null) {
856 String name = proposeName();
857 bool mayClashNative = _isUserClassExtendingNative(element.enclosingClass);
858 newName = getFreshName(name,
859 usedInstanceNames, suggestedInstanceNames,
860 sanitizeForAnnotations: true,
861 sanitizeForNatives: mayClashNative);
862 internalInstanceMembers[element] = newName;
863 }
864 return newName;
865 }
866
867 /// Disambiguated name for the given operator.
868 ///
869 /// [operatorIdentifier] must be the operator's identifier, e.g.
870 /// `$add` and not `+`.
871 ///
872 /// The resulting name is unique within the instance-member namespace.
873 String _disambiguateOperator(String operatorIdentifier) {
874 String newName = userInstanceOperators[operatorIdentifier];
875 if (newName == null) {
876 newName = getFreshName(operatorIdentifier, usedInstanceNames,
877 suggestedInstanceNames);
878 userInstanceOperators[operatorIdentifier] = newName;
879 }
880 return newName;
881 }
882
883 /// Returns an unused name.
884 ///
885 /// [proposedName] must be a valid JavaScript identifier.
886 ///
887 /// If [sanitizeForAnnotations] is `true`, then the result is guaranteed not
888 /// to have the form of an annotated name.
889 ///
890 /// If [sanitizeForNatives] it `true`, then the result is guaranteed not to
891 /// clash with a property name on a native object.
892 ///
893 /// Note that [MinifyNamer] overrides this method with one that produces
894 /// minified names.
895 String getFreshName(String proposedName, 596 String getFreshName(String proposedName,
896 Set<String> usedNames, 597 Set<String> usedNames,
897 Map<String, String> suggestedNames, 598 Map<String, String> suggestedNames,
898 {bool sanitizeForAnnotations: false, 599 {bool ensureSafe: true}) {
899 bool sanitizeForNatives: false}) { 600 var candidate;
900 if (sanitizeForAnnotations) { 601 if (ensureSafe) {
901 proposedName = _sanitizeForAnnotations(proposedName); 602 proposedName = safeName(proposedName);
902 } 603 }
903 if (sanitizeForNatives) { 604 assert(!jsReserved.contains(proposedName));
904 proposedName = _sanitizeForNatives(proposedName);
905 }
906 proposedName = _sanitizeForKeywords(proposedName);
907 String candidate;
908 if (!usedNames.contains(proposedName)) { 605 if (!usedNames.contains(proposedName)) {
909 candidate = proposedName; 606 candidate = proposedName;
910 } else { 607 } else {
911 int counter = popularNameCounters[proposedName]; 608 var counter = popularNameCounters[proposedName];
912 int i = (counter == null) ? 0 : counter; 609 var i = counter == null ? 0 : counter;
913 while (usedNames.contains("$proposedName$i")) { 610 while (usedNames.contains("$proposedName$i")) {
914 i++; 611 i++;
915 } 612 }
916 popularNameCounters[proposedName] = i + 1; 613 popularNameCounters[proposedName] = i + 1;
917 candidate = "$proposedName$i"; 614 candidate = "$proposedName$i";
918 } 615 }
919 usedNames.add(candidate); 616 usedNames.add(candidate);
920 return candidate; 617 return candidate;
921 } 618 }
922 619
923 /// Returns a variant of [name] that cannot clash with the annotated
924 /// version of another name, that is, the resulting name can never be returned
925 /// by [deriveGetterName], [deriveSetterName], [deriveCallMethodName],
926 /// [operatorIs], or [substitutionName].
927 ///
928 /// For example, a name `get$x` would be converted to `$get$x` to ensure it
929 /// cannot clash with the getter for `x`.
930 ///
931 /// We don't want to register all potential annotated names in
932 /// [usedInstanceNames] (there are too many), so we use this step to avoid
933 /// clashes between annotated and unannotated names.
934 String _sanitizeForAnnotations(String name) {
935 // Ensure name does not clash with a getter or setter of another name,
936 // one of the other special names that start with `$`, such as `$is`,
937 // or with one of the `call` stubs, such as `call$1`.
938 assert(this is! MinifyNamer);
939 if (name.startsWith(r'$') ||
940 name.startsWith(getterPrefix) ||
941 name.startsWith(setterPrefix) ||
942 name.startsWith(_callPrefixDollar)) {
943 name = '\$$name';
944 }
945 return name;
946 }
947
948 /// Returns a variant of [name] that cannot clash with a native property name
949 /// (e.g. the name of a method on a JS DOM object).
950 ///
951 /// If [name] is not an annotated name, the result will not be an annotated
952 /// name either.
953 String _sanitizeForNatives(String name) {
954 if (!name.contains(r'$')) {
955 // Prepend $$. The result must not coincide with an annotated name.
956 name = '\$\$$name';
957 }
958 return name;
959 }
960
961 /// Generate a unique name for the [id]th closure variable, with proposed name
962 /// [name].
963 ///
964 /// The result is used as the name of [BoxFieldElement]s and
965 /// [ClosureFieldElement]s, and must therefore be unique to avoid breaking an
966 /// invariant in the element model (classes cannot declare multiple fields
967 /// with the same name).
968 ///
969 /// Since the result is used as an element name, it will later show up as a
970 /// *proposed name* when the element is passed to [instanceFieldPropertyName].
971 String getClosureVariableName(String name, int id) { 620 String getClosureVariableName(String name, int id) {
972 return "${name}_$id"; 621 return "${name}_$id";
973 } 622 }
974 623
975 /** 624 /**
976 * Returns a proposed name for the given top-level or static element. 625 * Returns a preferred JS-id for the given top-level or static element.
977 * The returned id is guaranteed to be a valid JS-id. 626 * The returned id is guaranteed to be a valid JS-id.
978 */ 627 */
979 String _proposeNameForGlobal(Element element) { 628 String _computeGuess(Element element) {
980 assert(!element.isInstanceMember); 629 assert(!element.isInstanceMember);
981 String name; 630 String name;
982 if (element.isGenerativeConstructor) { 631 if (element.isGenerativeConstructor) {
983 name = "${element.enclosingClass.name}\$" 632 name = "${element.enclosingClass.name}\$"
984 "${element.name}"; 633 "${element.name}";
985 } else if (element.isFactoryConstructor) { 634 } else if (element.isFactoryConstructor) {
986 // TODO(johnniwinther): Change factory name encoding as to not include 635 // TODO(johnniwinther): Change factory name encoding as to not include
987 // the class-name twice. 636 // the class-name twice.
988 String className = element.enclosingClass.name; 637 String className = element.enclosingClass.name;
989 name = '${className}_${Elements.reconstructConstructorName(element)}'; 638 name = '${className}_${Elements.reconstructConstructorName(element)}';
(...skipping 24 matching lines...) Expand all
1014 if (!IDENTIFIER.hasMatch(name)) { // e.g. starts with digit. 663 if (!IDENTIFIER.hasMatch(name)) { // e.g. starts with digit.
1015 name = 'lib_$name'; 664 name = 'lib_$name';
1016 } 665 }
1017 } 666 }
1018 } else { 667 } else {
1019 name = element.name; 668 name = element.name;
1020 } 669 }
1021 return name; 670 return name;
1022 } 671 }
1023 672
1024 String suffixForGetInterceptor(Iterable<ClassElement> classes) { 673 String getInterceptorSuffix(Iterable<ClassElement> classes) {
1025 String abbreviate(ClassElement cls) { 674 String abbreviate(ClassElement cls) {
1026 if (cls == compiler.objectClass) return "o"; 675 if (cls == compiler.objectClass) return "o";
1027 if (cls == backend.jsStringClass) return "s"; 676 if (cls == backend.jsStringClass) return "s";
1028 if (cls == backend.jsArrayClass) return "a"; 677 if (cls == backend.jsArrayClass) return "a";
1029 if (cls == backend.jsDoubleClass) return "d"; 678 if (cls == backend.jsDoubleClass) return "d";
1030 if (cls == backend.jsIntClass) return "i"; 679 if (cls == backend.jsIntClass) return "i";
1031 if (cls == backend.jsNumberClass) return "n"; 680 if (cls == backend.jsNumberClass) return "n";
1032 if (cls == backend.jsNullClass) return "u"; 681 if (cls == backend.jsNullClass) return "u";
1033 if (cls == backend.jsBoolClass) return "b"; 682 if (cls == backend.jsBoolClass) return "b";
1034 if (cls == backend.jsInterceptorClass) return "I"; 683 if (cls == backend.jsInterceptorClass) return "I";
1035 return cls.name; 684 return cls.name;
1036 } 685 }
1037 List<String> names = classes 686 List<String> names = classes
1038 .where((cls) => !Elements.isNativeOrExtendsNative(cls)) 687 .where((cls) => !Elements.isNativeOrExtendsNative(cls))
1039 .map(abbreviate) 688 .map(abbreviate)
1040 .toList(); 689 .toList();
1041 // There is one dispatch mechanism for all native classes. 690 // There is one dispatch mechanism for all native classes.
1042 if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) { 691 if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) {
1043 names.add("x"); 692 names.add("x");
1044 } 693 }
1045 // Sort the names of the classes after abbreviating them to ensure 694 // Sort the names of the classes after abbreviating them to ensure
1046 // the suffix is stable and predictable for the suggested names. 695 // the suffix is stable and predictable for the suggested names.
1047 names.sort(); 696 names.sort();
1048 return names.join(); 697 return names.join();
1049 } 698 }
1050 699
1051 /// Property name used for `getInterceptor` or one of its specializations. 700 String getInterceptorName(Element element, Iterable<ClassElement> classes) {
1052 String nameForGetInterceptor(Iterable<ClassElement> classes) {
1053 FunctionElement getInterceptor = backend.getInterceptorMethod;
1054 if (classes.contains(backend.jsInterceptorClass)) { 701 if (classes.contains(backend.jsInterceptorClass)) {
1055 // If the base Interceptor class is in the set of intercepted classes, we 702 // If the base Interceptor class is in the set of intercepted classes, we
1056 // need to go through the generic getInterceptorMethod, since any subclass 703 // need to go through the generic getInterceptorMethod, since any subclass
1057 // of the base Interceptor could match. 704 // of the base Interceptor could match.
1058 // The unspecialized getInterceptor method can also be accessed through 705 return getNameOfInstanceMember(element);
1059 // its element, so we treat this as a user-space global instead of an
1060 // internal global.
1061 return _disambiguateGlobal(getInterceptor);
1062 } 706 }
1063 String suffix = suffixForGetInterceptor(classes); 707 String suffix = getInterceptorSuffix(classes);
1064 return _disambiguateInternalGlobal("${getInterceptor.name}\$$suffix"); 708 return getMappedGlobalName("${element.name}\$$suffix");
1065 } 709 }
1066 710
1067 /// Property name used for the one-shot interceptor method for the given 711 String getOneShotInterceptorName(Selector selector,
1068 /// [selector] and return-type specialization. 712 Iterable<ClassElement> classes) {
1069 String nameForGetOneShotInterceptor(Selector selector,
1070 Iterable<ClassElement> classes) {
1071 // The one-shot name is a global name derived from the invocation name. To 713 // The one-shot name is a global name derived from the invocation name. To
1072 // avoid instability we would like the names to be unique and not clash with 714 // avoid instability we would like the names to be unique and not clash with
1073 // other global names. 715 // other global names.
1074 716
1075 String root = invocationName(selector); 717 String root = invocationName(selector); // Is already safe.
1076 718
1077 if (classes.contains(backend.jsInterceptorClass)) { 719 if (classes.contains(backend.jsInterceptorClass)) {
1078 // If the base Interceptor class is in the set of intercepted classes, 720 // If the base Interceptor class is in the set of intercepted classes,
1079 // this is the most general specialization which uses the generic 721 // this is the most general specialization which uses the generic
1080 // getInterceptor method. To keep the name short, we add '$' only to 722 // getInterceptor method. To keep the name short, we add '$' only to
1081 // distinguish from internal globals requested from outside the Namer 723 // distinguish from global getters or setters; operators and methods can't
1082 // with internalGlobal(). 724 // clash.
1083 // TODO(sra): Find a way to get the simple name when Object is not in the 725 // TODO(sra): Find a way to get the simple name when Object is not in the
1084 // set of classes for most general variant, e.g. "$lt$n" could be "$lt". 726 // set of classes for most general variant, e.g. "$lt$n" could be "$lt".
1085 if (selector.isGetter || selector.isSetter) root = '$root\$'; 727 if (selector.isGetter || selector.isSetter) root = '$root\$';
1086 return _disambiguateInternalGlobal(root); 728 return getMappedGlobalName(root, ensureSafe: false);
1087 } else { 729 } else {
1088 String suffix = suffixForGetInterceptor(classes); 730 String suffix = getInterceptorSuffix(classes);
1089 return _disambiguateInternalGlobal("$root\$$suffix"); 731 return getMappedGlobalName("$root\$$suffix", ensureSafe: false);
1090 } 732 }
1091 } 733 }
1092 734
1093 /// Returns the runtime name for [element]. 735 /// Returns the runtime name for [element]. The result is not safe as an id.
1094 /// 736 String getRuntimeTypeName(Element element) {
1095 /// This name is used as the basis for deriving `is` and `as` property names
1096 /// for the given type.
1097 ///
1098 /// The result is not always safe as a property name unless prefixing
1099 /// [operatorIsPrefix] or [operatorAsPrefix]. If this is a function type,
1100 /// then by convention, an underscore must also separate [operatorIsPrefix]
1101 /// from the type name.
1102 String runtimeTypeName(TypeDeclarationElement element) {
1103 if (element == null) return 'dynamic'; 737 if (element == null) return 'dynamic';
1104 // The returned name affects both the global and instance member namespaces: 738 return getNameForRti(element);
1105 //
1106 // - If given a class, this must coincide with the class name, which
1107 // is also the GLOBAL property name of its constructor.
1108 //
1109 // - The result is used to derive `$isX` and `$asX` names, which are used
1110 // as INSTANCE property names.
1111 //
1112 // To prevent clashes in both namespaces at once, we disambiguate the name
1113 // as a global here, and in [_sanitizeForAnnotations] we ensure that
1114 // ordinary instance members cannot start with `$is` or `$as`.
1115 return _disambiguateGlobal(element);
1116 } 739 }
1117 740
1118 /// Returns the disambiguated name of [class_]. 741 /**
1119 /// 742 * Returns a preferred JS-id for the given element. The returned id is
1120 /// This is both the *runtime type* of the class (see [runtimeTypeName]) 743 * guaranteed to be a valid JS-id. Globals and static fields are furthermore
1121 /// and a global property name in which to store its JS constructor. 744 * guaranteed to be unique.
1122 String className(ClassElement class_) => _disambiguateGlobal(class_); 745 *
746 * For accessing statics consider calling [elementAccess] instead.
747 */
748 // TODO(ahe): This is an internal method to the Namer (and its subclasses)
749 // and should not be call from outside.
750 String getNameX(Element element) {
751 if (element.isInstanceMember) {
752 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY
753 || element.kind == ElementKind.FUNCTION) {
754 return instanceMethodName(element);
755 } else if (element.kind == ElementKind.GETTER) {
756 return getterName(element);
757 } else if (element.kind == ElementKind.SETTER) {
758 return setterName(element);
759 } else if (element.kind == ElementKind.FIELD) {
760 compiler.internalError(element,
761 'Use instanceFieldPropertyName or instanceFieldAccessorName.');
762 return null;
763 } else {
764 compiler.internalError(element,
765 'getName for bad kind: ${element.kind}.');
766 return null;
767 }
768 } else {
769 // Use declaration element to ensure invariant on [globals].
770 element = element.declaration;
771 // Dealing with a top-level or static element.
772 String cached = globals[element];
773 if (cached != null) return cached;
1123 774
1124 /// Property name on which [member] can be accessed directly, 775 String guess = _computeGuess(element);
1125 /// without clashing with another JS property name. 776 ElementKind kind = element.kind;
1126 /// 777 if (kind == ElementKind.VARIABLE ||
1127 /// This is used for implementing super-calls, where ordinary dispatch 778 kind == ElementKind.PARAMETER) {
1128 /// semantics must be circumvented. For example: 779 // The name is not guaranteed to be unique.
1129 /// 780 return safeName(guess);
1130 /// class A { foo() } 781 }
1131 /// class B extends A { 782 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR ||
1132 /// foo() { super.foo() } 783 kind == ElementKind.FUNCTION ||
1133 /// } 784 kind == ElementKind.CLASS ||
1134 /// 785 kind == ElementKind.FIELD ||
1135 /// Example translation to JS: 786 kind == ElementKind.GETTER ||
1136 /// 787 kind == ElementKind.SETTER ||
1137 /// A.prototype.super$A$foo = function() {...} 788 kind == ElementKind.TYPEDEF ||
1138 /// A.prototype.foo$0 = A.prototype.super$A$foo 789 kind == ElementKind.LIBRARY) {
1139 /// 790 bool fixedName = false;
1140 /// B.prototype.foo$0 = function() { 791 if (Elements.isInstanceField(element)) {
1141 /// this.super$A$foo(); // super.foo() 792 fixedName = element.hasFixedBackendName;
1142 /// } 793 }
1143 /// 794 String result = fixedName
1144 String aliasedSuperMemberPropertyName(Element member) { 795 ? guess
1145 assert(!member.isField); // Fields do not need super aliases. 796 : getFreshName(guess, usedGlobalNames, suggestedGlobalNames,
1146 String methodName = instanceMethodName(member); 797 ensureSafe: true);
1147 return _disambiguateInternalMember(member, 798 globals[element] = result;
1148 () => 'super\$${member.enclosingClass.name}\$$methodName'); 799 return result;
800 }
801 compiler.internalError(element,
802 'getName for unknown kind: ${element.kind}.');
803 return null;
804 }
1149 } 805 }
1150 806
1151 /// Property name in which to store the given static or instance [method]. 807 String getNameForRti(Element element) => getNameX(element);
1152 /// For instance methods, this includes the suffix encoding arity and named 808
1153 /// parameters. 809 String getNameOfLibrary(LibraryElement library) => getNameX(library);
1154 /// 810
1155 /// The name is not necessarily unique to [method], since a static method 811 String getNameOfClass(ClassElement cls) => getNameX(cls);
1156 /// may share its name with an instance method. 812
1157 String methodPropertyName(Element method) { 813 String getNameOfField(VariableElement field) => getNameX(field);
1158 return method.isInstanceMember 814
1159 ? instanceMethodName(method) 815 // TODO(ahe): Remove this method. Use get getNameOfMember instead.
1160 : globalPropertyName(method); 816 String getNameOfInstanceMember(Element member) => getNameX(member);
817
818 String getNameOfAliasedSuperMember(Element member) {
819 ClassElement superClass = member.enclosingClass;
820 String className = getNameOfClass(superClass);
821 String memberName = getNameOfMember(member);
822 String proposal = "$superPrefix$className\$$memberName";
823 // TODO(herhut): Use field naming constraints (unique wrt. inheritance).
824 return getMappedInstanceName(proposal);
1161 } 825 }
1162 826
827 String getNameOfMember(Element member) => getNameX(member);
828
829 String getNameOfGlobalField(VariableElement field) => getNameX(field);
830
1163 /// Returns true if [element] is stored on current isolate ('$'). We intend 831 /// Returns true if [element] is stored on current isolate ('$'). We intend
1164 /// to store only mutable static state in [currentIsolate], constants are 832 /// to store only mutable static state in [currentIsolate], constants are
1165 /// stored in 'C', and functions, accessors, classes, etc. are stored in one 833 /// stored in 'C', and functions, accessors, classes, etc. are stored in one
1166 /// of the other objects in [reservedGlobalObjectNames]. 834 /// of the other objects in [reservedGlobalObjectNames].
1167 bool isPropertyOfCurrentIsolate(Element element) { 835 bool isPropertyOfCurrentIsolate(Element element) {
1168 // TODO(ahe): Make sure this method's documentation is always true and 836 // TODO(ahe): Make sure this method's documentation is always true and
1169 // remove the word "intend". 837 // remove the word "intend".
1170 return 838 return
1171 // TODO(ahe): Re-write these tests to be positive (so it only returns 839 // TODO(ahe): Re-write these tests to be positive (so it only returns
1172 // true for static/top-level mutable fields). Right now, a number of 840 // true for static/top-level mutable fields). Right now, a number of
(...skipping 13 matching lines...) Expand all
1186 if (library == backend.interceptorsLibrary) return 'J'; 854 if (library == backend.interceptorsLibrary) return 'J';
1187 if (library.isInternalLibrary) return 'H'; 855 if (library.isInternalLibrary) return 'H';
1188 if (library.isPlatformLibrary) { 856 if (library.isPlatformLibrary) {
1189 if ('${library.canonicalUri}' == 'dart:html') return 'W'; 857 if ('${library.canonicalUri}' == 'dart:html') return 'W';
1190 return 'P'; 858 return 'P';
1191 } 859 }
1192 return userGlobalObjects[ 860 return userGlobalObjects[
1193 library.getLibraryOrScriptName().hashCode % userGlobalObjects.length]; 861 library.getLibraryOrScriptName().hashCode % userGlobalObjects.length];
1194 } 862 }
1195 863
1196 String lazyInitializerName(Element element) { 864 String getLazyInitializerName(Element element) {
1197 assert(Elements.isStaticOrTopLevelField(element)); 865 assert(Elements.isStaticOrTopLevelField(element));
1198 String name = _disambiguateGlobal(element); 866 return getMappedGlobalName("$getterPrefix${getNameX(element)}");
1199 return _disambiguateInternalGlobal("$getterPrefix$name");
1200 } 867 }
1201 868
1202 String staticClosureName(Element element) { 869 String getStaticClosureName(Element element) {
1203 assert(Elements.isStaticOrTopLevelFunction(element)); 870 assert(invariant(element, Elements.isStaticOrTopLevelFunction(element)));
1204 String name = _disambiguateGlobal(element); 871 return getMappedGlobalName("${getNameX(element)}\$closure");
1205 return _disambiguateInternalGlobal("$name\$closure");
1206 } 872 }
1207 873
1208 // This name is used as part of the name of a TypeConstant 874 // This name is used as part of the name of a TypeConstant
1209 String uniqueNameForTypeConstantElement(Element element) { 875 String uniqueNameForTypeConstantElement(Element element) {
1210 // TODO(sra): If we replace the period with an identifier character, 876 // TODO(sra): If we replace the period with an identifier character,
1211 // TypeConstants will have better names in unminified code. 877 // TypeConstants will have better names in unminified code.
1212 return "${globalObjectFor(element)}.${globalPropertyName(element)}"; 878 return "${globalObjectFor(element)}.${getNameX(element)}";
1213 } 879 }
1214 880
1215 String globalObjectForConstant(ConstantValue constant) => 'C'; 881 String globalObjectForConstant(ConstantValue constant) => 'C';
1216 882
1217 String get operatorIsPrefix => r'$is'; 883 String get operatorIsPrefix => r'$is';
1218 884
1219 String get operatorAsPrefix => r'$as'; 885 String get operatorAsPrefix => r'$as';
1220 886
1221 String get operatorSignature => r'$signature'; 887 String get operatorSignature => r'$signature';
1222 888
(...skipping 12 matching lines...) Expand all
1235 String get functionTypeNamedParametersTag => r'named'; 901 String get functionTypeNamedParametersTag => r'named';
1236 902
1237 Map<FunctionType,String> functionTypeNameMap = 903 Map<FunctionType,String> functionTypeNameMap =
1238 new Map<FunctionType,String>(); 904 new Map<FunctionType,String>();
1239 final FunctionTypeNamer functionTypeNamer; 905 final FunctionTypeNamer functionTypeNamer;
1240 906
1241 String getFunctionTypeName(FunctionType functionType) { 907 String getFunctionTypeName(FunctionType functionType) {
1242 return functionTypeNameMap.putIfAbsent(functionType, () { 908 return functionTypeNameMap.putIfAbsent(functionType, () {
1243 String proposedName = functionTypeNamer.computeName(functionType); 909 String proposedName = functionTypeNamer.computeName(functionType);
1244 String freshName = getFreshName(proposedName, usedInstanceNames, 910 String freshName = getFreshName(proposedName, usedInstanceNames,
1245 suggestedInstanceNames); 911 suggestedInstanceNames, ensureSafe: true);
1246 return freshName; 912 return freshName;
1247 }); 913 });
1248 } 914 }
1249 915
1250 String operatorIsType(DartType type) { 916 String operatorIsType(DartType type) {
1251 if (type.isFunctionType) { 917 if (type.isFunctionType) {
1252 // TODO(erikcorry): Reduce from $isx to ix when we are minifying. 918 // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
1253 return '${operatorIsPrefix}_${getFunctionTypeName(type)}'; 919 return '${operatorIsPrefix}_${getFunctionTypeName(type)}';
1254 } 920 }
1255 return operatorIs(type.element); 921 return operatorIs(type.element);
1256 } 922 }
1257 923
1258 String operatorIs(ClassElement element) { 924 String operatorIs(Element element) {
1259 // TODO(erikcorry): Reduce from $isx to ix when we are minifying. 925 // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
1260 return '${operatorIsPrefix}${runtimeTypeName(element)}'; 926 return '${operatorIsPrefix}${getRuntimeTypeName(element)}';
1261 } 927 }
1262 928
1263 /// Returns a name that does not clash with reserved JS keywords. 929 /*
1264 String _sanitizeForKeywords(String name) { 930 * Returns a name that does not clash with reserved JS keywords,
1265 if (jsReserved.contains(name)) { 931 * and also ensures it won't clash with other identifiers.
932 */
933 String _safeName(String name, Set<String> reserved) {
934 if (reserved.contains(name) || name.startsWith(r'$')) {
1266 name = '\$$name'; 935 name = '\$$name';
1267 } 936 }
1268 assert(!jsReserved.contains(name)); 937 assert(!reserved.contains(name));
1269 return name; 938 return name;
1270 } 939 }
1271 940
1272 String substitutionName(Element element) { 941 String substitutionName(Element element) {
1273 return '${operatorAsPrefix}${runtimeTypeName(element)}'; 942 // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
943 // string concatenation in the implementation), and may prevent
944 // segmentation of '$'.
945 return '${operatorAsPrefix}${getNameForRti(element)}';
1274 } 946 }
1275 947
1276 /// Returns a variable name that cannot clash with a keyword, a global 948 String safeName(String name) => _safeName(name, jsReserved);
1277 /// variable, or any name starting with a single '$'. 949 String safeVariableName(String name) => _safeName(name, jsVariableReserved);
1278 ///
1279 /// Furthermore, this function is injective, that is, it never returns the
1280 /// same name for two different inputs.
1281 String safeVariableName(String name) {
1282 if (jsVariableReserved.contains(name) || name.startsWith(r'$')) {
1283 return '\$$name';
1284 }
1285 return name;
1286 }
1287 950
1288 String operatorNameToIdentifier(String name) { 951 String operatorNameToIdentifier(String name) {
1289 if (name == null) return null; 952 if (name == null) return null;
1290 if (name == '==') { 953 if (name == '==') {
1291 return r'$eq'; 954 return r'$eq';
1292 } else if (name == '~') { 955 } else if (name == '~') {
1293 return r'$not'; 956 return r'$not';
1294 } else if (name == '[]') { 957 } else if (name == '[]') {
1295 return r'$index'; 958 return r'$index';
1296 } else if (name == '[]=') { 959 } else if (name == '[]=') {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
1333 } 996 }
1334 997
1335 String get incrementalHelperName => r'$dart_unsafe_incremental_support'; 998 String get incrementalHelperName => r'$dart_unsafe_incremental_support';
1336 999
1337 jsAst.Expression get accessIncrementalHelper { 1000 jsAst.Expression get accessIncrementalHelper {
1338 assert(compiler.hasIncrementalSupport); 1001 assert(compiler.hasIncrementalSupport);
1339 return js('self.${incrementalHelperName}'); 1002 return js('self.${incrementalHelperName}');
1340 } 1003 }
1341 1004
1342 void forgetElement(Element element) { 1005 void forgetElement(Element element) {
1343 String globalName = userGlobals[element]; 1006 String globalName = globals[element];
1344 invariant(element, globalName != null, message: 'No global name.'); 1007 invariant(element, globalName != null, message: 'No global name.');
1345 usedGlobalNames.remove(globalName); 1008 usedGlobalNames.remove(globalName);
1346 userGlobals.remove(element); 1009 globals.remove(element);
1347 } 1010 }
1348 } 1011 }
1349 1012
1350 /** 1013 /**
1351 * Generator of names for [ConstantValue] values. 1014 * Generator of names for [ConstantValue] values.
1352 * 1015 *
1353 * The names are stable under perturbations of the source. The name is either a 1016 * The names are stable under perturbations of the source. The name is either a
1354 * short sequence of words, if this can be found from the constant, or a type 1017 * short sequence of words, if this can be found from the constant, or a type
1355 * followed by a hash tag. 1018 * followed by a hash tag.
1356 * 1019 *
(...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after
1755 if (!first) { 1418 if (!first) {
1756 sb.write('_'); 1419 sb.write('_');
1757 } 1420 }
1758 sb.write('_'); 1421 sb.write('_');
1759 visit(parameter); 1422 visit(parameter);
1760 first = true; 1423 first = true;
1761 } 1424 }
1762 } 1425 }
1763 } 1426 }
1764 } 1427 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/minify_namer.dart ('k') | pkg/compiler/lib/src/js_backend/runtime_types.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698