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