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