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

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

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