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