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

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: Typo and TODO about clash in named parameters 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 * `x1$2` (if `x` is a method with 2 parameters)
39 *
40 * The [Namer] can choose the disambiguated names, and to some degree the
41 * prefix/suffix constants used to construct annotated names. It cannot choose
42 * annotated names with total freedom, for example, it cannot choose that the
43 * getter for `x1` should be called `getX` -- the annotated names are always
44 * built by concatenation.
45 *
46 * Disambiguated names must be chosen such that none of the annotated names can
47 * clash with each other. This may happen even if the disambiguated names are
48 * distinct, for example, suppose a field `x` and `get$x` exists in the input:
49 *
50 * Original names: `x` and `get$x`
51 *
52 * Disambiguated names: `x` and `get$x` (the two names a different)
53 *
54 * Annotated names: `x` (field for `x`)
55 * `get$x` (getter for `x`)
56 * `get$x` (field for `get$x`)
57 * `get$get$x` (getter for `get$x`)
58 *
59 * The getter for `x` clashes with the field name for `get$x`, so the
60 * disambiguated names are invalid.
61 *
62 * Additionally, disambiguated names must be chosen such that all annotated
63 * names are valid JavaScript identifiers and do not coincide with a native
64 * JavaScript property such as `__proto__`.
65 *
66 * The following annotated names are generated for instance members, where
67 * <NAME> denotes the disambiguated name.
68 *
69 * 0. The disambiguated name can itself be seen as a annotated name.
floitsch 2015/01/30 21:03:38 as an annotated name.
asgerf 2015/02/03 17:39:11 Done.
70 *
71 * 1. Multiple annotated names exist per method name, encoding arity and named
72 * parameters with the pattern:
73 *
74 * <NAME>$<N>$namedParam1...$namedParam<M>
75 *
76 * where <N> is the number of parameters (required and optional) and <M> is
77 * the number of named parameters, and namedParam<n> are the names of the
78 * named parameters in alphabetical order.
79 *
80 * Note that this convention is not encapsulated in the [Namer], and is
81 * hardcoded into other components, such as [Element] and [Selector].
floitsch 2015/01/30 21:03:36 :(
asgerf 2015/02/03 17:39:12 Clarified that it's just the ordering of parameter
floitsch 2015/02/04 00:10:10 ok. that's not a big deal.
82 *
83 * 2. The getter/setter for a field, or tear-off getter for a method:
84 *
85 * get$<NAME>
86 * set$<NAME>
87 *
88 * (The [getterPrefix] and [setterPrefix] are different in [MinifyNamer]).
89 *
90 * 3. The `is` and operator uses the following names:
91 *
92 * $is<NAME>
93 * $as<NAME>
94 *
95 * For local variables, the [Namer] only provides *proposed names*. These names
96 * must be disambiguated elsewhere.
9 */ 97 */
10 class Namer implements ClosureNamer { 98 class Namer implements ClosureNamer {
11 99
12 static const javaScriptKeywords = const <String>[ 100 static const List<String> javaScriptKeywords = const <String>[
13 // These are current keywords. 101 // These are current keywords.
14 "break", "delete", "function", "return", "typeof", "case", "do", "if", 102 "break", "delete", "function", "return", "typeof", "case", "do", "if",
15 "switch", "var", "catch", "else", "in", "this", "void", "continue", 103 "switch", "var", "catch", "else", "in", "this", "void", "continue",
16 "false", "instanceof", "throw", "while", "debugger", "finally", "new", 104 "false", "instanceof", "throw", "while", "debugger", "finally", "new",
17 "true", "with", "default", "for", "null", "try", 105 "true", "with", "default", "for", "null", "try",
18 106
19 // These are future keywords. 107 // These are future keywords.
20 "abstract", "double", "goto", "native", "static", "boolean", "enum", 108 "abstract", "double", "goto", "native", "static", "boolean", "enum",
21 "implements", "package", "super", "byte", "export", "import", "private", 109 "implements", "package", "super", "byte", "export", "import", "private",
22 "synchronized", "char", "extends", "int", "protected", "throws", 110 "synchronized", "char", "extends", "int", "protected", "throws",
23 "class", "final", "interface", "public", "transient", "const", "float", 111 "class", "final", "interface", "public", "transient", "const", "float",
24 "long", "short", "volatile" 112 "long", "short", "volatile"
25 ]; 113 ];
26 114
27 static const reservedPropertySymbols = 115 static const List<String> reservedPropertySymbols =
28 const <String>["__proto__", "prototype", "constructor", "call"]; 116 const <String>["__proto__", "prototype", "constructor", "call"];
29 117
30 // Symbols that we might be using in our JS snippets. 118 // Symbols that we might be using in our JS snippets.
31 static const reservedGlobalSymbols = const <String>[ 119 static const List<String> reservedGlobalSymbols = const <String>[
32 // Section references are from Ecma-262 120 // Section references are from Ecma-262
33 // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pd f) 121 // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pd f)
34 122
35 // 15.1.1 Value Properties of the Global Object 123 // 15.1.1 Value Properties of the Global Object
36 "NaN", "Infinity", "undefined", 124 "NaN", "Infinity", "undefined",
37 125
38 // 15.1.2 Function Properties of the Global Object 126 // 15.1.2 Function Properties of the Global Object
39 "eval", "parseInt", "parseFloat", "isNaN", "isFinite", 127 "eval", "parseInt", "parseFloat", "isNaN", "isFinite",
40 128
41 // 15.1.3 URI Handling Function Properties 129 // 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", 229 "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
142 "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer", 230 "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
143 "XPathException", "XPathResult", "XSLTProcessor", 231 "XPathException", "XPathResult", "XSLTProcessor",
144 232
145 // These keywords trigger the loading of the java-plugin. For the 233 // These keywords trigger the loading of the java-plugin. For the
146 // next-generation plugin, this results in starting a new Java process. 234 // next-generation plugin, this results in starting a new Java process.
147 "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass", 235 "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
148 "JavaArray", "JavaMember", 236 "JavaArray", "JavaMember",
149 ]; 237 ];
150 238
151 static const reservedGlobalObjectNames = const <String>[ 239 static const List<String> reservedGlobalObjectNames = const <String>[
152 "A", 240 "A",
153 "B", 241 "B",
154 "C", // Global object for *C*onstants. 242 "C", // Global object for *C*onstants.
155 "D", 243 "D",
156 "E", 244 "E",
157 "F", 245 "F",
158 "G", 246 "G",
159 "H", // Global object for internal (*H*elper) libraries. 247 "H", // Global object for internal (*H*elper) libraries.
160 // I is used for used for the Isolate function. 248 // I is used for used for the Isolate function.
161 "J", // Global object for the interceptor library. 249 "J", // Global object for the interceptor library.
162 "K", 250 "K",
163 "L", 251 "L",
164 "M", 252 "M",
165 "N", 253 "N",
166 "O", 254 "O",
167 "P", // Global object for other *P*latform libraries. 255 "P", // Global object for other *P*latform libraries.
168 "Q", 256 "Q",
169 "R", 257 "R",
170 "S", 258 "S",
171 "T", 259 "T",
172 "U", 260 "U",
173 "V", 261 "V",
174 "W", // Global object for *W*eb libraries (dart:html). 262 "W", // Global object for *W*eb libraries (dart:html).
175 "X", 263 "X",
176 "Y", 264 "Y",
177 "Z", 265 "Z",
178 ]; 266 ];
179 267
180 static const reservedGlobalHelperFunctions = const <String>[ 268 static const List<String> reservedGlobalHelperFunctions = const <String>[
181 "init", 269 "init",
182 "Isolate", 270 "Isolate",
183 ]; 271 ];
184 272
185 static final userGlobalObjects = new List.from(reservedGlobalObjectNames) 273 static final List<String> userGlobalObjects =
274 new List.from(reservedGlobalObjectNames)
186 ..remove('C') 275 ..remove('C')
187 ..remove('H') 276 ..remove('H')
188 ..remove('J') 277 ..remove('J')
189 ..remove('P') 278 ..remove('P')
190 ..remove('W'); 279 ..remove('W');
191 280
192 Set<String> _jsReserved = null; 281 Set<String> _jsReserved = null;
193 /// Names that cannot be used by members, top level and static 282 /// Names that cannot be used by members, top level and static
194 /// methods. 283 /// methods.
195 Set<String> get jsReserved { 284 Set<String> get jsReserved {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 323
235 final String classDescriptorProperty = r'^'; 324 final String classDescriptorProperty = r'^';
236 325
237 // Name of property in a class description for the native dispatch metadata. 326 // Name of property in a class description for the native dispatch metadata.
238 final String nativeSpecProperty = '%'; 327 final String nativeSpecProperty = '%';
239 328
240 static final RegExp IDENTIFIER = new RegExp(r'^[A-Za-z_$][A-Za-z0-9_$]*$'); 329 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$]'); 330 static final RegExp NON_IDENTIFIER_CHAR = new RegExp(r'[^A-Za-z_0-9$]');
242 331
243 /** 332 /**
244 * Map from top-level or static elements to their unique identifiers provided 333 * Map from top-level or static elements to their unique identifiers provided
floitsch 2015/01/30 21:03:38 This comment is on the wrong field.
asgerf 2015/02/03 17:39:13 Done.
245 * by [getName]. 334 * by [getName].
floitsch 2015/01/30 21:03:37 `getName` doesn't exist anymore. Was replaced with
asgerf 2015/02/03 17:39:12 disambiguateGlobal is the closest we have now
246 * 335 *
247 * Invariant: Keys must be declaration elements. 336 * Invariant: Keys must be declaration elements.
248 */ 337 */
249 final Compiler compiler; 338 final Compiler compiler;
250 final Map<Element, String> globals; 339 final Map<Element, String> globals;
251 final Map<String, LibraryElement> shortPrivateNameOwners; 340 final Map<String, LibraryElement> shortPrivateNameOwners;
252 341
253 final Set<String> usedGlobalNames; 342 final Set<String> usedGlobalNames;
254 final Set<String> usedInstanceNames; 343 final Set<String> usedInstanceNames;
255 final Map<String, String> globalNameMap; 344 final Map<String, String> internGlobals;
floitsch 2015/01/30 21:03:36 add comment. internGlobals -> internalGlobals? Ma
asgerf 2015/02/03 17:39:12 Done.
256 final Map<String, String> suggestedGlobalNames; 345 final Map<String, String> suggestedGlobalNames;
floitsch 2015/01/30 21:03:38 I would group the fields: globals usedGlobalNames
asgerf 2015/02/03 17:39:12 Grouped and renamed, to be more streamlined. I add
257 final Map<String, String> instanceNameMap; 346 final Map<LibraryElement, Map<String, String>> instanceMembers;
floitsch 2015/01/30 21:03:37 Add a comment explaining that `null` is used as ke
asgerf 2015/02/03 17:39:13 The map works differently now, see disambiguateMem
258 final Map<String, String> suggestedInstanceNames; 347 final Map<String, String> suggestedInstanceNames;
348 final Map<Element, String> directAccessMap = <Element, String>{};
floitsch 2015/01/30 21:03:37 comment.
asgerf 2015/02/03 17:39:12 see above.
259 349
260 final Map<String, String> operatorNameMap; 350 final Map<String, String> operatorNameMap;
261 final Map<String, int> popularNameCounters; 351 final Map<String, int> popularNameCounters;
262 352
263 final Map<ConstantValue, String> constantNames; 353 final Map<ConstantValue, String> constantNames;
264 final Map<ConstantValue, String> constantLongNames; 354 final Map<ConstantValue, String> constantLongNames;
265 ConstantCanonicalHasher constantHasher; 355 ConstantCanonicalHasher constantHasher;
266 356
267 // All alphanumeric characters. 357 // All alphanumeric characters.
268 static const String _alphaNumeric = 358 static const String _alphaNumeric =
269 'abcdefghijklmnopqrstuvwxyzABZDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 359 'abcdefghijklmnopqrstuvwxyzABZDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
270 360
271 Namer(Compiler compiler) 361 Namer(Compiler compiler)
272 : compiler = compiler, 362 : compiler = compiler,
floitsch 2015/01/30 21:03:36 nit: Namer(this.compiler); You could move all ass
asgerf 2015/02/03 17:39:12 Done.
273 globals = new Map<Element, String>(), 363 globals = new Map<Element, String>(),
274 shortPrivateNameOwners = new Map<String, LibraryElement>(), 364 shortPrivateNameOwners = new Map<String, LibraryElement>(),
275 usedGlobalNames = new Set<String>(), 365 usedGlobalNames = new Set<String>(),
276 usedInstanceNames = new Set<String>(), 366 usedInstanceNames = new Set<String>(),
277 instanceNameMap = new Map<String, String>(), 367 instanceMembers = new Map<LibraryElement, Map<String, String>>(),
278 operatorNameMap = new Map<String, String>(), 368 operatorNameMap = new Map<String, String>(),
279 globalNameMap = new Map<String, String>(), 369 internGlobals = new Map<String, String>(),
280 suggestedGlobalNames = new Map<String, String>(), 370 suggestedGlobalNames = new Map<String, String>(),
281 suggestedInstanceNames = new Map<String, String>(), 371 suggestedInstanceNames = new Map<String, String>(),
282 popularNameCounters = new Map<String, int>(), 372 popularNameCounters = new Map<String, int>(),
283 constantNames = new Map<ConstantValue, String>(), 373 constantNames = new Map<ConstantValue, String>(),
284 constantLongNames = new Map<ConstantValue, String>(), 374 constantLongNames = new Map<ConstantValue, String>(),
285 constantHasher = new ConstantCanonicalHasher(compiler), 375 constantHasher = new ConstantCanonicalHasher(compiler),
286 functionTypeNamer = new FunctionTypeNamer(compiler); 376 functionTypeNamer = new FunctionTypeNamer(compiler);
287 377
288 JavaScriptBackend get backend => compiler.backend; 378 JavaScriptBackend get backend => compiler.backend;
289 379
(...skipping 16 matching lines...) Expand all
306 case 'REFLECTABLE': return reflectableField; 396 case 'REFLECTABLE': return reflectableField;
307 case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty; 397 case 'CLASS_DESCRIPTOR_PROPERTY': return classDescriptorProperty;
308 default: 398 default:
309 compiler.reportError( 399 compiler.reportError(
310 node, MessageKind.GENERIC, 400 node, MessageKind.GENERIC,
311 {'text': 'Error: Namer has no name for "$name".'}); 401 {'text': 'Error: Namer has no name for "$name".'});
312 return 'BROKEN'; 402 return 'BROKEN';
313 } 403 }
314 } 404 }
315 405
406 /// Disambiguated name for [constant].
407 ///
408 /// Unique within the global-member namespace.
floitsch 2015/01/30 21:03:37 Is that necessary? After all, we always put consta
asgerf 2015/02/03 17:39:13 You are right, but this is how it worked to begin
floitsch 2015/02/04 00:10:10 TODO is fine.
316 String constantName(ConstantValue constant) { 409 String constantName(ConstantValue constant) {
317 // In the current implementation it doesn't make sense to give names to 410 // In the current implementation it doesn't make sense to give names to
318 // function constants since the function-implementation itself serves as 411 // function constants since the function-implementation itself serves as
319 // constant and can be accessed directly. 412 // constant and can be accessed directly.
320 assert(!constant.isFunction); 413 assert(!constant.isFunction);
321 String result = constantNames[constant]; 414 String result = constantNames[constant];
322 if (result == null) { 415 if (result == null) {
323 String longName = constantLongName(constant); 416 String longName = constantLongName(constant);
324 result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames, 417 result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames,
325 ensureSafe: true); 418 ensureSafe: true);
326 constantNames[constant] = result; 419 constantNames[constant] = result;
327 } 420 }
328 return result; 421 return result;
329 } 422 }
330 423
331 // The long name is unminified and may have collisions. 424 /// Proposed name for [constant].
332 String constantLongName(ConstantValue constant) { 425 String constantLongName(ConstantValue constant) {
floitsch 2015/01/30 21:03:37 This looks like a function that shouldn't be calle
asgerf 2015/02/03 17:39:13 The emitters currently call it for some sort of co
floitsch 2015/02/04 00:10:11 Acknowledged.
333 String longName = constantLongNames[constant]; 426 String longName = constantLongNames[constant];
334 if (longName == null) { 427 if (longName == null) {
335 longName = new ConstantNamingVisitor(compiler, constantHasher) 428 longName = new ConstantNamingVisitor(compiler, constantHasher)
336 .getName(constant); 429 .getName(constant);
337 constantLongNames[constant] = longName; 430 constantLongNames[constant] = longName;
338 } 431 }
339 return longName; 432 return longName;
340 } 433 }
341 434
342 String breakLabelName(LabelDefinition label) { 435 String breakLabelName(LabelDefinition label) {
343 return '\$${label.labelName}\$${label.target.nestingLevel}'; 436 return '\$${label.labelName}\$${label.target.nestingLevel}';
344 } 437 }
345 438
346 String implicitBreakLabelName(JumpTarget target) { 439 String implicitBreakLabelName(JumpTarget target) {
347 return '\$${target.nestingLevel}'; 440 return '\$${target.nestingLevel}';
348 } 441 }
349 442
350 // We sometimes handle continue targets differently from break targets, 443 // We sometimes handle continue targets differently from break targets,
351 // so we have special continue-only labels. 444 // so we have special continue-only labels.
352 String continueLabelName(LabelDefinition label) { 445 String continueLabelName(LabelDefinition label) {
353 return 'c\$${label.labelName}\$${label.target.nestingLevel}'; 446 return 'c\$${label.labelName}\$${label.target.nestingLevel}';
354 } 447 }
355 448
356 String implicitContinueLabelName(JumpTarget target) { 449 String implicitContinueLabelName(JumpTarget target) {
357 return 'c\$${target.nestingLevel}'; 450 return 'c\$${target.nestingLevel}';
358 } 451 }
359 452
360 /** 453 /**
361 * If the [name] is not private returns [:name:]. Otherwise 454 * If the [originalName] is not private returns [originalName]. Otherwise
362 * mangles the [name] so that each library has a unique name. 455 * mangles the [originalName] so that each library has its own distinguished
456 * version of the name.
457 *
458 * Although the name is not guaranteed to be unique within any namespace,
459 * clashes are very unlikely in practice. Therefore, it can be used in cases
460 * where uniqueness is nice but not a strict requirement.
461 *
462 * The resulting name is a *proposed name* and is never minified.
363 */ 463 */
364 String privateName(LibraryElement library, String name) { 464 String privateName(LibraryElement library, String originalName) {
365 // Public names are easy. 465 // Public names are easy.
366 String nameString = name; 466 if (!isPrivateName(originalName)) return originalName;
367 if (!isPrivateName(name)) return nameString;
368 467
369 // The first library asking for a short private name wins. 468 // The first library asking for a short private name wins.
370 LibraryElement owner = 469 LibraryElement owner =
371 shortPrivateNameOwners.putIfAbsent(nameString, () => library); 470 shortPrivateNameOwners.putIfAbsent(originalName, () => library);
372 471
373 if (owner == library && !nameString.contains('\$')) { 472 if (owner == library && !originalName.contains('\$')) {
374 // Since the name doesn't contain $ it doesn't clash with any 473 // Since the name doesn't contain $ it doesn't clash with any
375 // of the private names that have the library name as the prefix. 474 // of the private names that have the library name as the prefix.
376 return nameString; 475 return originalName;
377 } else { 476 } else {
378 // Make sure to return a private name that starts with _ so it 477 // Make sure to return a private name that starts with _ so it
379 // cannot clash with any public names. 478 // cannot clash with any public names.
380 String libraryName = getNameOfLibrary(library); 479 String libraryName = disambiguateGlobal(library);
381 return '_$libraryName\$$nameString'; 480 return '_$libraryName\$${originalName}';
floitsch 2015/01/30 21:03:38 Add comment why this is not unique.
asgerf 2015/02/03 17:39:13 Done.
382 } 481 }
383 } 482 }
384 483
385 String instanceMethodName(FunctionElement element) { 484 /// Annotated name for [method] encoding arity and named parameters.
386 // TODO(ahe): Could this be: return invocationName(new 485 String instanceMethodName(FunctionElement method) {
387 // Selector.fromElement(element))? 486 if (method.isGenerativeConstructorBody) {
388 String elementName = element.name; 487 return disambiguateDirectAccess(method,
389 String name = operatorNameToIdentifier(elementName); 488 () => Elements.reconstructConstructorNameSourceString(method));
390 if (name != elementName) return getMappedOperatorName(name);
391
392 LibraryElement library = element.library;
393 if (element.isGenerativeConstructorBody) {
394 name = Elements.reconstructConstructorNameSourceString(element);
395 } 489 }
396 FunctionSignature signature = element.functionSignature; 490 return invocationName(new Selector.fromElement(method));
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 } 491 }
418 492
419 String publicInstanceMethodNameByArity(String name, int arity) { 493 /// Annotated name for a public method with the given [originalName]
420 String newName = operatorNameToIdentifier(name); 494 /// and [arity] and no named parameters.
floitsch 2015/01/30 21:03:36 Maybe mention that it handles operators?
asgerf 2015/02/03 17:39:12 Not anymore.
421 if (newName != name) return getMappedOperatorName(newName); 495 String publicInstanceMethodNameByArity(String originalName, int arity) {
422 assert(!isPrivateName(name)); 496 String opName = operatorNameToIdentifier(originalName);
423 // We don't mangle the closure invoking function name because it 497 if (opName != originalName) return disambiguateOperator(opName);
424 // is generated by string concatenation in applyFunction from 498 assert(!isPrivateName(originalName));
425 // js_helper.dart. To keep code size down, we potentially shorten 499 String disambiguatedName = disambiguateMember(null, originalName);
426 // the prefix though. 500 Selector selector = new Selector.call(originalName, null, arity);
427 if (name == closureInvocationSelectorName) return '$callPrefix\$$arity'; 501 return deriveMethodName(disambiguatedName, selector);
428
429 return getMappedInstanceName('$name\$$arity');
430 } 502 }
431 503
504 /// Returns the annotated name for a method with the given name,
505 /// encoding arity and named parameters with the pattern:
506 ///
507 /// <NAME>$<N>$namedParam1...$namedParam<M>
508 ///
509 String deriveMethodName(String disambiguatedName, Selector selector) {
510 assert(selector.isCall);
511 // TODO(asgerf): Avoid clash when named parameters contain '$' symbols.
floitsch 2015/01/30 21:03:37 Create a failing test.
asgerf 2015/02/03 17:39:12 Done. And the issue has been fixed.
512 StringBuffer buffer = new StringBuffer();
513 for (String argumentName in selector.getOrderedNamedArguments()) {
514 buffer.write('\$${safeName(argumentName)}');
515 }
516 return '$disambiguatedName\$${selector.argumentCount}$buffer';
517 }
518
519 /// Annotated name for the member being invoked by [selector].
432 String invocationName(Selector selector) { 520 String invocationName(Selector selector) {
433 if (selector.isGetter) { 521 LibraryElement library = selector.library;
434 String proposedName = privateName(selector.library, selector.name); 522 switch (selector.kind) {
435 return '$getterPrefix${getMappedInstanceName(proposedName)}'; 523 case SelectorKind.GETTER:
436 } else if (selector.isSetter) { 524 String disambiguatedName = disambiguateMember(library, selector.name);
437 String proposedName = privateName(selector.library, selector.name); 525 return deriveGetterName(disambiguatedName);
438 return '$setterPrefix${getMappedInstanceName(proposedName)}'; 526
439 } else { 527 case SelectorKind.SETTER:
440 String name = selector.name; 528 String disambiguatedName = disambiguateMember(library, selector.name);
441 if (selector.kind == SelectorKind.OPERATOR 529 return deriveSetterName(disambiguatedName);
442 || selector.kind == SelectorKind.INDEX) { 530
443 name = operatorNameToIdentifier(name); 531 case SelectorKind.OPERATOR:
444 assert(name != selector.name); 532 case SelectorKind.INDEX:
445 return getMappedOperatorName(name); 533 String operatorIdentifier = operatorNameToIdentifier(selector.name);
446 } 534 String disambiguatedName = disambiguateOperator(operatorIdentifier);
447 assert(name == operatorNameToIdentifier(name)); 535 return disambiguatedName; // Operators are not annotated.
448 StringBuffer buffer = new StringBuffer(); 536
449 for (String argumentName in selector.getOrderedNamedArguments()) { 537 case SelectorKind.CALL:
450 buffer.write('\$${safeName(argumentName)}'); 538 String disambiguatedName = disambiguateMember(library, selector.name);
451 } 539 return deriveMethodName(disambiguatedName, selector);
452 String suffix = '\$${selector.argumentCount}$buffer'; 540
453 // We don't mangle the closure invoking function name because it 541 default: throw 'Unexpected selector kind: ${selector.kind}';
floitsch 2015/01/30 21:03:37 throw an internal error. (we tend to avoid throwin
asgerf 2015/02/03 17:39:13 Done.
454 // is generated by string concatenation in applyFunction from
455 // js_helper.dart. We potentially shorten the prefix though.
456 if (selector.isClosureCall) {
457 return "$callPrefix$suffix";
458 } else {
459 String proposedName = privateName(selector.library, name);
460 return getMappedInstanceName('$proposedName$suffix');
461 }
462 } 542 }
463 } 543 }
464 544
465 /** 545 /**
466 * Returns the internal name used for an invocation mirror of this selector. 546 * Returns the internal name used for an invocation mirror of this selector.
floitsch 2015/01/30 21:03:38 What's an "internal" name?
asgerf 2015/02/03 17:39:12 I don't know what this function is used for.
467 */ 547 */
468 String invocationMirrorInternalName(Selector selector) 548 String invocationMirrorInternalName(Selector selector)
469 => invocationName(selector); 549 => invocationName(selector);
470 550
471 /** 551 /**
472 * Returns name of accessor (root to getter and setter) for a static or 552 * Returns the disambiguated name for the given field, used for constructing
473 * instance field. 553 * the getter and setter names.
474 */ 554 */
475 String fieldAccessorName(Element element) { 555 String fieldAccessorName(Element element) {
476 return element.isInstanceMember 556 return element.isInstanceMember
477 ? instanceFieldAccessorName(element) 557 ? disambiguateMember(element.library, element.name)
478 : getNameOfField(element); 558 : disambiguateGlobal(element);
479 } 559 }
480 560
481 /** 561 /**
482 * Returns name of the JavaScript property used to store a static or instance 562 * Returns name of the JavaScript property used to store a static or instance
483 * field. 563 * field.
484 */ 564 */
485 String fieldPropertyName(Element element) { 565 String fieldPropertyName(Element element) {
486 return element.isInstanceMember 566 return element.isInstanceMember
487 ? instanceFieldPropertyName(element) 567 ? instanceFieldPropertyName(element)
488 : getNameOfField(element); 568 : disambiguateGlobal(element);
489 } 569 }
490 570
491 /** 571 /**
492 * Returns name of accessor (root to getter and setter) for an instance field. 572 * Returns name of the JavaScript property used to store the given type
573 * variable.
floitsch 2015/01/30 21:03:38 Also mention that this is inside a class and thus
asgerf 2015/02/03 17:39:13 Done.
493 */ 574 */
494 String instanceFieldAccessorName(Element element) {
495 String proposedName = privateName(element.library, element.name);
496 return getMappedInstanceName(proposedName);
497 }
498
499 String readTypeVariableName(TypeVariableElement element) { 575 String readTypeVariableName(TypeVariableElement element) {
floitsch 2015/01/30 21:03:37 Why is this prefixed "read" ?
asgerf 2015/02/03 17:39:13 Don't know. Changed to typeVariableName.
500 return '\$tv_${instanceFieldAccessorName(element)}'; 576 return disambiguateDirectAccess(element, () => element.name);
501 } 577 }
502 578
503 /** 579 /**
504 * Returns name of the JavaScript property used to store an instance field. 580 * Returns a JavaScript property name used to store [element] on one
581 * of the global objects.
582 *
583 * Should be used together with [globalObjectFor], which denotes the object
584 * on which the returned property name should be used.
585 */
586 String globalPropertyName(Element element) {
587 return disambiguateGlobal(element);
588 }
589
590 /**
591 * Returns the JavaScript property name used to store an instance field.
505 */ 592 */
506 String instanceFieldPropertyName(Element element) { 593 String instanceFieldPropertyName(Element element) {
507 if (element.hasFixedBackendName) { 594 if (element.hasFixedBackendName) {
508 return element.fixedBackendName; 595 return element.fixedBackendName;
floitsch 2015/01/30 21:03:36 Does this potentially lead to clashes? What if th
asgerf 2015/02/03 17:39:12 Turns out clashes are possible. Added custom_eleme
509 } 596 }
510 // If a class is used anywhere as a mixin, we must make the name unique so 597
511 // that it does not accidentally shadow. Also, the mixin name must be 598 // If the name of the field might clash with another field,
512 // constant over all mixins. 599 // use a mangled field name to avoid potential clashes.
513 ClassWorld classWorld = compiler.world; 600 ClassWorld classWorld = compiler.world;
514 if (classWorld.isUsedAsMixin(element.enclosingClass) || 601 if (classWorld.isUsedAsMixin(element.enclosingClass) ||
515 shadowingAnotherField(element)) { 602 shadowingAnotherField(element)) {
516 // Construct a new name for the element based on the library and class it 603 return disambiguateDirectAccess(element,
517 // is in. The name here is not important, we just need to make sure it is 604 () => '${element.enclosingClass.name}_${element.name}');
floitsch 2015/01/30 21:03:37 Is this enough? What if I have a mixin class A_b w
asgerf 2015/02/03 17:39:13 The string we're building is only a proposed name.
518 // unique. If we are minifying, we actually construct the name from the 605 }
519 // minified version of the class name, but the result is minified once 606
520 // again, so that is not visible in the end result. 607 // No inheritance-related name clashes exist, so the accessor and property
floitsch 2015/01/30 21:03:37 I don't understand this comment. Do you mean: no i
asgerf 2015/02/03 17:39:12 Done following offline discussion.
521 String libraryName = getNameOfLibrary(element.library); 608 // name can be shared. This generates nicer field names since otherwise
522 String className = getNameOfClass(element.enclosingClass); 609 // one of them would have to be mangled.
523 String instanceName = privateName(element.library, element.name); 610 return disambiguateMember(element.library, element.name);
524 return getMappedInstanceName('$libraryName\$$className\$$instanceName'); 611 }
525 }
526
527 String proposedName = privateName(element.library, element.name);
528 return getMappedInstanceName(proposedName);
529 }
530
531 612
532 bool shadowingAnotherField(Element element) { 613 bool shadowingAnotherField(Element element) {
floitsch 2015/01/30 21:03:37 isShadowingASuperField ?
asgerf 2015/02/03 17:39:13 Done.
533 return element.enclosingClass.hasFieldShadowedBy(element); 614 return element.enclosingClass.hasFieldShadowedBy(element);
534 } 615 }
535 616
536 String setterName(Element element) { 617 /// Annotated name for the setter of [element].
618 String setterForElement(Element element) {
537 // We dynamically create setters from the field-name. The setter name must 619 // We dynamically create setters from the field-name. The setter name must
538 // therefore be derived from the instance field-name. 620 // therefore be derived from the instance field-name.
539 LibraryElement library = element.library; 621 String name = disambiguateMember(element.library, element.name);
540 String name = getMappedInstanceName(privateName(library, element.name)); 622 return deriveSetterName(name);
541 return '$setterPrefix$name'; 623 }
542 } 624
543 625 /// Annotated name for the setter of any member with [disambiguatedName].
544 String setterNameFromAccessorName(String name) { 626 String deriveSetterName(String disambiguatedName) {
545 // We dynamically create setters from the field-name. The setter name must 627 // We dynamically create setters from the field-name. The setter name must
546 // therefore be derived from the instance field-name. 628 // therefore be derived from the instance field-name.
547 return '$setterPrefix$name'; 629 return '$setterPrefix$disambiguatedName';
548 } 630 }
549 631
550 String getterNameFromAccessorName(String name) { 632 /// Annotated name for the setter of any member with [disambiguatedName].
633 String deriveGetterName(String disambiguatedName) {
551 // We dynamically create getters from the field-name. The getter name must 634 // We dynamically create getters from the field-name. The getter name must
552 // therefore be derived from the instance field-name. 635 // therefore be derived from the instance field-name.
553 return '$getterPrefix$name'; 636 return '$getterPrefix$disambiguatedName';
554 } 637 }
555 638
556 String getterName(Element element) { 639 /// Annotated name for the getter of [element].
640 String getterForElement(Element element) {
floitsch 2015/01/30 21:03:38 nit: either put getter and setter next to each oth
asgerf 2015/02/03 17:39:12 I agree. I kept it like this to avoid messing up t
557 // We dynamically create getters from the field-name. The getter name must 641 // We dynamically create getters from the field-name. The getter name must
558 // therefore be derived from the instance field-name. 642 // therefore be derived from the instance field-name.
559 LibraryElement library = element.library; 643 String name = disambiguateMember(element.library, element.name);
560 String name = getMappedInstanceName(privateName(library, element.name)); 644 return deriveGetterName(name);
561 return '$getterPrefix$name'; 645 }
562 } 646
563 647 /// Property name for the getter of an instance member with [originalName]
564 String getMappedGlobalName(String proposedName, {bool ensureSafe: true}) { 648 /// in [library].
565 var newName = globalNameMap[proposedName]; 649 ///
566 if (newName == null) { 650 /// [library] may be `null` if [originalName] is known to be public.
651 String getterForMember(LibraryElement library, String originalName) {
652 String disambiguatedName = disambiguateMember(library, originalName);
653 return deriveGetterName(disambiguatedName);
654 }
655
656 /// Disambiguated name for a compiler-owned global variable.
657 ///
658 /// The resulting name is unique within the global-member namespace.
659 String disambiguateInternGlobal(String name, {bool ensureSafe: true}) {
floitsch 2015/01/30 21:03:36 disambiguateInternalGlobal ? disambiguateCompilerG
floitsch 2015/01/30 21:03:36 Comment what "ensureSafe" means.
660 String newName = internGlobals[name];
661 if (newName == null) {
662 newName = getFreshName(name, usedGlobalNames,
663 suggestedGlobalNames, ensureSafe: ensureSafe);
664 internGlobals[name] = newName;
665 }
666 return newName;
667 }
668
669 /// Returns the property name to use for a compiler-owner global variable,
670 /// i.e. one that does not correspond to any element but is used as a utility
671 /// global by code generation.
672 ///
673 /// [name] functions as both the proposed name for the global, and as a key
674 /// identifying the global. The [name] should not contain `$` symbols, since
floitsch 2015/01/30 21:03:36 Make this a "must" and assert it.
asgerf 2015/02/03 17:39:13 Done.
675 /// the [Namer] uses those names internally.
676 ///
677 /// This provides an easy mechanism for avoiding a name-clash with user-space
678 /// globals, although the callers of must still take care not to accidentally
floitsch 2015/01/30 21:03:36 -of-
asgerf 2015/02/03 17:39:12 Done.
679 /// pass in the same [name] for two different intern globals.
680 String internGlobal(String name, {bool ensureSafe: true}) {
floitsch 2015/01/30 21:03:37 internGlobal -> internalGlobal ?
asgerf 2015/02/03 17:39:13 Done.
681 return disambiguateInternGlobal(name, ensureSafe: ensureSafe);
682 }
683
684 /// Returns the disambiguated name for a top-level or static element.
685 ///
686 /// The resulting name is unique within the global-member namespace.
floitsch 2015/01/30 21:03:37 Same as for the constant: we might be able to only
asgerf 2015/02/03 17:39:12 Again, probably yes. But this CL is big enough alr
floitsch 2015/02/04 00:10:10 Didn't expect more than a TODO.
687 String disambiguateGlobal(Element element) {
688 element = element.declaration;
689 String newName = globals[element];
690 if (newName == null) {
691 String proposedName = _proposeNameForGlobal(element);
567 newName = getFreshName(proposedName, usedGlobalNames, 692 newName = getFreshName(proposedName, usedGlobalNames,
568 suggestedGlobalNames, ensureSafe: ensureSafe); 693 suggestedGlobalNames, ensureSafe: true);
569 globalNameMap[proposedName] = newName; 694 globals[element] = newName;
570 } 695 }
571 return newName; 696 return newName;
572 } 697 }
573 698
574 String getMappedInstanceName(String proposedName) { 699 /// Returns the disambiguated name for an instance method or field
575 var newName = instanceNameMap[proposedName]; 700 /// with [originalName] in [library].
576 if (newName == null) { 701 ///
577 newName = getFreshName(proposedName, usedInstanceNames, 702 /// [library] may be `null` if [originalName] is known to be public.
578 suggestedInstanceNames, ensureSafe: true); 703 ///
579 instanceNameMap[proposedName] = newName; 704 /// This is the name used for deriving names of accessors (getters and
580 } 705 /// setters) and annotated method names (encoding arity and named parameters).
581 return newName; 706 ///
582 } 707 /// The resulting name, and its associated annotated names, are unique within
583 708 /// the instance-member namespace.
584 String getMappedOperatorName(String proposedName) { 709 String disambiguateMember(LibraryElement library, String originalName) {
585 var newName = operatorNameMap[proposedName]; 710 if (originalName == closureInvocationSelectorName) {
floitsch 2015/01/30 21:03:36 assert that library is not null if the originalNam
asgerf 2015/02/03 17:39:11 Done.
586 if (newName == null) { 711 // We hardcode the disambiguated name of 'call'.
587 newName = getFreshName(proposedName, usedInstanceNames, 712 return callPrefix;
713 }
714 if (!isPrivateName(originalName)) {
715 library = null;
716 }
717 Map<String, String> nameMap =
718 instanceMembers.putIfAbsent(library, () => new Map<String,String>());
719 String newName = nameMap[originalName];
720 if (newName == null) {
721 String proposedName = privateName(library, originalName);
722 newName = getFreshName(proposedName,
723 usedInstanceNames, suggestedInstanceNames,
724 ensureSafe: true,
725 sanitizeForAnnotations: true);
726 nameMap[originalName] = newName;
727 }
728 return newName;
729 }
730
731 /// Disambiguated name unique to [element].
732 ///
733 /// This is used as the property name for fields, type variables,
734 /// constructor bodies, and super-accessors.
735 ///
736 /// The resulting name is unique within the instance-member namespace.
737 String disambiguateDirectAccess(Element element, String proposeName()) {
floitsch 2015/01/30 21:03:38 Explain how this is different from disambiguateMem
asgerf 2015/02/03 17:39:12 I've changed it to disambiguateInternalMember. I h
738 String newName = directAccessMap[element];
739 if (newName == null) {
740 String name = proposeName();
741 newName = getFreshName(name,
742 usedInstanceNames, suggestedInstanceNames,
743 ensureSafe: true,
744 sanitizeForAnnotations: true);
745 directAccessMap[element] = newName;
746 }
747 return newName;
748 }
749
750 /// Disambiguated name for the given operator.
751 ///
752 /// [operatorIdentifier] must be the operator's identifier, e.g.
753 /// `$add` and not `+`.
754 ///
755 /// The resulting name is unique within the instance-member namespace.
756 String disambiguateOperator(String operatorIdentifier) {
757 String newName = operatorNameMap[operatorIdentifier];
758 if (newName == null) {
759 newName = getFreshName(operatorIdentifier, usedInstanceNames,
588 suggestedInstanceNames, ensureSafe: false); 760 suggestedInstanceNames, ensureSafe: false);
589 operatorNameMap[proposedName] = newName; 761 operatorNameMap[operatorIdentifier] = newName;
590 } 762 }
591 return newName; 763 return newName;
592 } 764 }
593 765
766 /// Returns an unused name.
767 ///
768 /// [proposedName] must be a valid JavaScript identifier.
769 ///
770 /// If [ensureSafe] is `false`, then [proposedName] must not be a keyword.
771 ///
772 /// If [sanitizeForAnnotations] is `true`, then the result is guaranteed not
773 /// to have the form of an annotated name.
774 ///
775 /// Note that [MinifyNamer] overrides this method with one that produces
776 /// minified names.
594 String getFreshName(String proposedName, 777 String getFreshName(String proposedName,
595 Set<String> usedNames, 778 Set<String> usedNames,
596 Map<String, String> suggestedNames, 779 Map<String, String> suggestedNames,
597 {bool ensureSafe: true}) { 780 {bool ensureSafe: true,
598 var candidate; 781 bool sanitizeForAnnotations: false}) {
782 if (sanitizeForAnnotations) {
783 proposedName = _sanitizeForAnnotations(proposedName);
784 }
599 if (ensureSafe) { 785 if (ensureSafe) {
600 proposedName = safeName(proposedName); 786 proposedName = safeName(proposedName);
601 } 787 }
602 assert(!jsReserved.contains(proposedName)); 788 assert(!jsReserved.contains(proposedName));
789 String candidate;
603 if (!usedNames.contains(proposedName)) { 790 if (!usedNames.contains(proposedName)) {
604 candidate = proposedName; 791 candidate = proposedName;
605 } else { 792 } else {
606 var counter = popularNameCounters[proposedName]; 793 int counter = popularNameCounters[proposedName];
607 var i = counter == null ? 0 : counter; 794 int i = counter == null ? 0 : counter;
floitsch 2015/01/30 21:03:36 (counter == null)
asgerf 2015/02/03 17:39:11 Done.
608 while (usedNames.contains("$proposedName$i")) { 795 while (usedNames.contains("$proposedName$i")) {
609 i++; 796 i++;
610 } 797 }
611 popularNameCounters[proposedName] = i + 1; 798 popularNameCounters[proposedName] = i + 1;
612 candidate = "$proposedName$i"; 799 candidate = "$proposedName$i";
613 } 800 }
614 usedNames.add(candidate); 801 usedNames.add(candidate);
615 return candidate; 802 return candidate;
616 } 803 }
617 804
805 /// Returns a variant of [name] that cannot clash with the annotated
806 /// version of another name, that is, the resulting name can never be returned
807 /// by [deriveGetterName], [deriveSetterName], [deriveMethodName],
808 /// [operatorIs], or [substitutionName].
809 ///
810 /// For example, a name `get$x` would be converted to `$get$x` to ensure it
811 /// cannot clash with the getter for `x`.
floitsch 2015/01/30 21:03:37 Add '_isAnnotated' method and assert that every an
asgerf 2015/02/03 17:39:13 I don't think it's worth it anymore, since it woul
812 ///
813 /// We don't want to register all potential annotated names in
814 /// [usedInstanceNames] (there are too many), so we use this step to avoid
815 /// clashes between annotated and unannotated names.
816 String _sanitizeForAnnotations(String name) {
817 // Ensure name does not contain a $ followed by a digit, since this could
818 // clash with the suffix we append on method names.
819 for (int i = name.indexOf(r'$'); i != -1; i = name.indexOf(r'$', i+1)) {
820 if (i + 1 < name.length && isDigit(name.codeUnitAt(i+1))) {
821 // Strip off everything after the $ to be safe.
822 name = name.substring(0, i + 1);
823 break;
824 }
825 }
826 // Ensure name does not clash with a getter or setter of another name, or
827 // one of the other special names that start with `$`, such as `$is`.
828 if (name.startsWith(r'$') ||
829 name.startsWith(getterPrefix) ||
830 name.startsWith(setterPrefix)) {
831 name = '\$$name';
832 }
833 return name;
834 }
835
618 String getClosureVariableName(String name, int id) { 836 String getClosureVariableName(String name, int id) {
619 return "${name}_$id"; 837 return "${name}_$id";
620 } 838 }
621 839
622 /** 840 /**
623 * Returns a preferred JS-id for the given top-level or static element. 841 * Returns a proposed name for the given top-level or static element.
624 * The returned id is guaranteed to be a valid JS-id. 842 * The returned id is guaranteed to be a valid JS-id.
625 */ 843 */
626 String _computeGuess(Element element) { 844 String _proposeNameForGlobal(Element element) {
627 assert(!element.isInstanceMember); 845 assert(!element.isInstanceMember);
628 String name; 846 String name;
629 if (element.isGenerativeConstructor) { 847 if (element.isGenerativeConstructor) {
630 name = "${element.enclosingClass.name}\$" 848 name = "${element.enclosingClass.name}\$"
631 "${element.name}"; 849 "${element.name}";
632 } else if (element.isFactoryConstructor) { 850 } else if (element.isFactoryConstructor) {
633 // TODO(johnniwinther): Change factory name encoding as to not include 851 // TODO(johnniwinther): Change factory name encoding as to not include
634 // the class-name twice. 852 // the class-name twice.
635 String className = element.enclosingClass.name; 853 String className = element.enclosingClass.name;
636 name = '${className}_${Elements.reconstructConstructorName(element)}'; 854 name = '${className}_${Elements.reconstructConstructorName(element)}';
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
688 // There is one dispatch mechanism for all native classes. 906 // There is one dispatch mechanism for all native classes.
689 if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) { 907 if (classes.any((cls) => Elements.isNativeOrExtendsNative(cls))) {
690 names.add("x"); 908 names.add("x");
691 } 909 }
692 // Sort the names of the classes after abbreviating them to ensure 910 // Sort the names of the classes after abbreviating them to ensure
693 // the suffix is stable and predictable for the suggested names. 911 // the suffix is stable and predictable for the suggested names.
694 names.sort(); 912 names.sort();
695 return names.join(); 913 return names.join();
696 } 914 }
697 915
698 String getInterceptorName(Element element, Iterable<ClassElement> classes) { 916 /// Property name used for `getInterceptor` or one of its specializations.
floitsch 2015/01/30 21:03:37 as written in another file: "nameOfGetInterceptorF
asgerf 2015/02/03 17:39:11 Done.
917 String getInterceptorName(Iterable<ClassElement> classes) {
918 FunctionElement getInterceptor = backend.getInterceptorMethod;
699 if (classes.contains(backend.jsInterceptorClass)) { 919 if (classes.contains(backend.jsInterceptorClass)) {
700 // If the base Interceptor class is in the set of intercepted classes, we 920 // If the base Interceptor class is in the set of intercepted classes, we
701 // need to go through the generic getInterceptorMethod, since any subclass 921 // need to go through the generic getInterceptorMethod, since any subclass
702 // of the base Interceptor could match. 922 // of the base Interceptor could match.
703 return getNameOfInstanceMember(element); 923 // The unspecialized getInterceptor method can also be accessed through
924 // its element, so we treat this as a user-space global instead of an
925 // intern global.
926 return disambiguateGlobal(getInterceptor);
704 } 927 }
705 String suffix = getInterceptorSuffix(classes); 928 String suffix = getInterceptorSuffix(classes);
706 return getMappedGlobalName("${element.name}\$$suffix"); 929 return disambiguateInternGlobal("${getInterceptor.name}\$$suffix");
707 } 930 }
708 931
932 /// Property name used for the one-shot interceptor method for the given
933 /// [selector] and return-type specialization.
709 String getOneShotInterceptorName(Selector selector, 934 String getOneShotInterceptorName(Selector selector,
710 Iterable<ClassElement> classes) { 935 Iterable<ClassElement> classes) {
711 // The one-shot name is a global name derived from the invocation name. To 936 // 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 937 // avoid instability we would like the names to be unique and not clash with
713 // other global names. 938 // other global names.
714 939
715 String root = invocationName(selector); // Is already safe. 940 String root = invocationName(selector); // Is already safe.
716 941
717 if (classes.contains(backend.jsInterceptorClass)) { 942 if (classes.contains(backend.jsInterceptorClass)) {
718 // If the base Interceptor class is in the set of intercepted classes, 943 // If the base Interceptor class is in the set of intercepted classes,
719 // this is the most general specialization which uses the generic 944 // this is the most general specialization which uses the generic
720 // getInterceptor method. To keep the name short, we add '$' only to 945 // getInterceptor method. To keep the name short, we add '$' only to
721 // distinguish from global getters or setters; operators and methods can't 946 // distinguish from other intern globals that don't contain '$' symbols.
722 // clash.
723 // TODO(sra): Find a way to get the simple name when Object is not in the 947 // 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". 948 // set of classes for most general variant, e.g. "$lt$n" could be "$lt".
725 if (selector.isGetter || selector.isSetter) root = '$root\$'; 949 if (selector.isGetter || selector.isSetter) root = '$root\$';
floitsch 2015/01/30 21:03:37 Why does a getter and setter need the "$" ? The ol
asgerf 2015/02/03 17:39:12 Rephrased comment.
726 return getMappedGlobalName(root, ensureSafe: false); 950 return disambiguateInternGlobal(root, ensureSafe: false);
727 } else { 951 } else {
728 String suffix = getInterceptorSuffix(classes); 952 String suffix = getInterceptorSuffix(classes);
729 return getMappedGlobalName("$root\$$suffix", ensureSafe: false); 953 return disambiguateInternGlobal("$root\$$suffix", ensureSafe: false);
730 } 954 }
731 } 955 }
732 956
733 /// Returns the runtime name for [element]. The result is not safe as an id. 957 /// Returns the runtime name for [element].
734 String getRuntimeTypeName(Element element) { 958 ///
959 /// This name is used as the basis for deriving `is` and `as` property names
960 /// for the given type.
961 ///
962 /// The result is not always safe as a property name unless prefixing
963 /// [operatorIsPrefix] or [operatorAsPrefix]. If this is a function type,
964 /// then by convention, an underscore must also separate [operatorIsPrefix]
965 /// from the type name.
floitsch 2015/01/30 21:03:37 Yeah for weird arbitrary rules...
asgerf 2015/02/03 17:39:12 Still better than undocumented arbitrary rules...
966 String getRuntimeTypeName(TypeDeclarationElement element) {
735 if (element == null) return 'dynamic'; 967 if (element == null) return 'dynamic';
736 return getNameForRti(element); 968 // The returned name affects both the global and instance member namespaces:
969 //
970 // - If given a class, this must coincide with the class name, which
971 // is also the GLOBAL property name of its constructor.
972 //
973 // - The result is used to derive `$isX` and `$asX` names, which are used
974 // as INSTANCE property names.
975 //
976 // To prevent clashes in both namespaces at once, we disambiguate the name
977 // as a global here, and in [_sanitizeForAnnotations] we ensure that
978 // ordinary instance members cannot start with a '$' character.
979 return disambiguateGlobal(element);
737 } 980 }
738 981
739 /** 982 /// Returns the disambiguated name of [class_].
740 * Returns a preferred JS-id for the given element. The returned id is 983 ///
741 * guaranteed to be a valid JS-id. Globals and static fields are furthermore 984 /// This is both the *runtime type* of the class (see [getRuntimeTypeName])
742 * guaranteed to be unique. 985 /// and a global property name in which to store its JS constructor.
743 * 986 String getNameOfClass(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 987
773 String guess = _computeGuess(element); 988 /// Property name on which [member] can be accessed directly,
774 ElementKind kind = element.kind; 989 /// without clashing with another JS property name.
floitsch 2015/01/30 21:03:36 Might help to give a small example when this is ne
asgerf 2015/02/03 17:39:13 Done, although I don't understand your example, so
floitsch 2015/02/04 00:10:11 Interesting. I didn't know that we added the alias
775 if (kind == ElementKind.VARIABLE || 990 String getNameOfAliasedSuperMember(Element member) {
776 kind == ElementKind.PARAMETER) { 991 assert(!member.isField); // Fields do not need super aliases.
777 // The name is not guaranteed to be unique. 992 return disambiguateDirectAccess(member,
778 return safeName(guess); 993 () => '\$super\$${member.enclosingClass.name}\$${member.name}');
779 }
780 if (kind == ElementKind.GENERATIVE_CONSTRUCTOR ||
781 kind == ElementKind.FUNCTION ||
782 kind == ElementKind.CLASS ||
783 kind == ElementKind.FIELD ||
784 kind == ElementKind.GETTER ||
785 kind == ElementKind.SETTER ||
786 kind == ElementKind.TYPEDEF ||
787 kind == ElementKind.LIBRARY) {
788 bool fixedName = false;
789 if (Elements.isInstanceField(element)) {
790 fixedName = element.hasFixedBackendName;
791 }
792 String result = fixedName
793 ? guess
794 : getFreshName(guess, usedGlobalNames, suggestedGlobalNames,
795 ensureSafe: true);
796 globals[element] = result;
797 return result;
798 }
799 compiler.internalError(element,
800 'getName for unknown kind: ${element.kind}.');
801 return null;
802 }
803 } 994 }
804 995
805 String getNameForRti(Element element) => getNameX(element); 996 /// Property name in which to store the given static or instance [method].
806 997 /// For instance methods, this includes the suffix encoding arity and named
807 String getNameOfLibrary(LibraryElement library) => getNameX(library); 998 /// parameters.
808 999 ///
809 String getNameOfClass(ClassElement cls) => getNameX(cls); 1000 /// The name is not necessarily unique to [method], since a static method
810 1001 /// may share its name with an instance method.
floitsch 2015/01/30 21:03:37 But is it unique within their respective categorie
asgerf 2015/02/03 17:39:12 Ideally, the users of Namer shouldn't have to know
811 String getNameOfField(VariableElement field) => getNameX(field); 1002 String getNameOfMethod(Element method) {
floitsch 2015/01/30 21:03:38 Why is this prefixed with "get"? Most of the othe
asgerf 2015/02/03 17:39:13 I agree. I've changed it to "methodName".
812 1003 return method.isInstanceMember
813 // TODO(ahe): Remove this method. Use get getNameOfMember instead. 1004 ? instanceMethodName(method)
814 String getNameOfInstanceMember(Element member) => getNameX(member); 1005 : 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 } 1006 }
824 1007
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 1008 /// Returns true if [element] is stored on current isolate ('$'). We intend
830 /// to store only mutable static state in [currentIsolate], constants are 1009 /// to store only mutable static state in [currentIsolate], constants are
831 /// stored in 'C', and functions, accessors, classes, etc. are stored in one 1010 /// stored in 'C', and functions, accessors, classes, etc. are stored in one
832 /// of the other objects in [reservedGlobalObjectNames]. 1011 /// of the other objects in [reservedGlobalObjectNames].
833 bool isPropertyOfCurrentIsolate(Element element) { 1012 bool isPropertyOfCurrentIsolate(Element element) {
834 // TODO(ahe): Make sure this method's documentation is always true and 1013 // TODO(ahe): Make sure this method's documentation is always true and
835 // remove the word "intend". 1014 // remove the word "intend".
836 return 1015 return
837 // TODO(ahe): Re-write these tests to be positive (so it only returns 1016 // 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 1017 // true for static/top-level mutable fields). Right now, a number of
(...skipping 15 matching lines...) Expand all
854 if (library.isPlatformLibrary) { 1033 if (library.isPlatformLibrary) {
855 if ('${library.canonicalUri}' == 'dart:html') return 'W'; 1034 if ('${library.canonicalUri}' == 'dart:html') return 'W';
856 return 'P'; 1035 return 'P';
857 } 1036 }
858 return userGlobalObjects[ 1037 return userGlobalObjects[
859 library.getLibraryOrScriptName().hashCode % userGlobalObjects.length]; 1038 library.getLibraryOrScriptName().hashCode % userGlobalObjects.length];
860 } 1039 }
861 1040
862 String getLazyInitializerName(Element element) { 1041 String getLazyInitializerName(Element element) {
863 assert(Elements.isStaticOrTopLevelField(element)); 1042 assert(Elements.isStaticOrTopLevelField(element));
864 return getMappedGlobalName("$getterPrefix${getNameX(element)}"); 1043 return disambiguateInternGlobal("$getterPrefix${globalPropertyName(element)} ");
floitsch 2015/01/30 21:03:36 long line.
asgerf 2015/02/03 17:39:12 Done.
865 } 1044 }
866 1045
867 String getStaticClosureName(Element element) { 1046 String getStaticClosureName(Element element) {
868 assert(Elements.isStaticOrTopLevelFunction(element)); 1047 assert(Elements.isStaticOrTopLevelFunction(element));
869 return getMappedGlobalName("${getNameX(element)}\$closure"); 1048 return disambiguateInternGlobal("${globalPropertyName(element)}\$closure");
870 } 1049 }
871 1050
872 // This name is used as part of the name of a TypeConstant 1051 // This name is used as part of the name of a TypeConstant
873 String uniqueNameForTypeConstantElement(Element element) { 1052 String uniqueNameForTypeConstantElement(Element element) {
874 // TODO(sra): If we replace the period with an identifier character, 1053 // TODO(sra): If we replace the period with an identifier character,
875 // TypeConstants will have better names in unminified code. 1054 // TypeConstants will have better names in unminified code.
876 return "${globalObjectFor(element)}.${getNameX(element)}"; 1055 return "${globalObjectFor(element)}.${globalPropertyName(element)}";
877 } 1056 }
878 1057
879 String globalObjectForConstant(ConstantValue constant) => 'C'; 1058 String globalObjectForConstant(ConstantValue constant) => 'C';
880 1059
881 String get operatorIsPrefix => r'$is'; 1060 String get operatorIsPrefix => r'$is';
882 1061
883 String get operatorAsPrefix => r'$as'; 1062 String get operatorAsPrefix => r'$as';
884 1063
885 String get operatorSignature => r'$signature'; 1064 String get operatorSignature => r'$signature';
886 1065
(...skipping 25 matching lines...) Expand all
912 } 1091 }
913 1092
914 String operatorIsType(DartType type) { 1093 String operatorIsType(DartType type) {
915 if (type.isFunctionType) { 1094 if (type.isFunctionType) {
916 // TODO(erikcorry): Reduce from $isx to ix when we are minifying. 1095 // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
917 return '${operatorIsPrefix}_${getFunctionTypeName(type)}'; 1096 return '${operatorIsPrefix}_${getFunctionTypeName(type)}';
918 } 1097 }
919 return operatorIs(type.element); 1098 return operatorIs(type.element);
920 } 1099 }
921 1100
922 String operatorIs(Element element) { 1101 String operatorIs(ClassElement element) {
923 // TODO(erikcorry): Reduce from $isx to ix when we are minifying. 1102 // TODO(erikcorry): Reduce from $isx to ix when we are minifying.
924 return '${operatorIsPrefix}${getRuntimeTypeName(element)}'; 1103 return '${operatorIsPrefix}${getRuntimeTypeName(element)}';
925 } 1104 }
926 1105
927 /* 1106 /*
928 * Returns a name that does not clash with reserved JS keywords, 1107 * Returns a name that does not clash with reserved JS keywords,
929 * and also ensures it won't clash with other identifiers. 1108 * and also ensures it won't clash with other identifiers.
floitsch 2015/01/30 21:03:38 with other reserved identifiers (like "__proto__")
asgerf 2015/02/03 17:39:13 Actually I think that was a stray comment from whe
930 */ 1109 */
931 String _safeName(String name, Set<String> reserved) { 1110 String _safeName(String name, Set<String> reserved) {
932 if (reserved.contains(name) || name.startsWith(r'$')) { 1111 if (reserved.contains(name)) {
933 name = '\$$name'; 1112 name = '\$$name';
934 } 1113 }
935 assert(!reserved.contains(name)); 1114 assert(!reserved.contains(name));
936 return name; 1115 return name;
937 } 1116 }
938 1117
939 String substitutionName(Element element) { 1118 String substitutionName(Element element) {
940 // TODO(ahe): Creating a string here is unfortunate. It is slow (due to 1119 // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
floitsch 2015/01/30 21:03:38 I don't know what Peter means. The cost in string
asgerf 2015/02/03 17:39:12 Done.
941 // string concatenation in the implementation), and may prevent 1120 // string concatenation in the implementation), and may prevent
942 // segmentation of '$'. 1121 // segmentation of '$'.
943 return '${operatorAsPrefix}${getNameForRti(element)}'; 1122 return '${operatorAsPrefix}${getRuntimeTypeName(element)}';
944 } 1123 }
945 1124
946 String safeName(String name) => _safeName(name, jsReserved); 1125 String safeName(String name) => _safeName(name, jsReserved);
947 String safeVariableName(String name) => _safeName(name, jsVariableReserved); 1126 String safeVariableName(String name) => _safeName(name, jsVariableReserved);
948 1127
949 String operatorNameToIdentifier(String name) { 1128 String operatorNameToIdentifier(String name) {
950 if (name == null) return null; 1129 if (name == null) return null;
951 if (name == '==') { 1130 if (name == '==') {
952 return r'$eq'; 1131 return r'$eq';
953 } else if (name == '~') { 1132 } else if (name == '~') {
(...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after
1416 if (!first) { 1595 if (!first) {
1417 sb.write('_'); 1596 sb.write('_');
1418 } 1597 }
1419 sb.write('_'); 1598 sb.write('_');
1420 visit(parameter); 1599 visit(parameter);
1421 first = true; 1600 first = true;
1422 } 1601 }
1423 } 1602 }
1424 } 1603 }
1425 } 1604 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698