Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: pkg/compiler/lib/src/js_backend/namer.dart

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

Powered by Google App Engine
This is Rietveld 408576698