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