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

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

Powered by Google App Engine
This is Rietveld 408576698