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