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

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

Powered by Google App Engine
This is Rietveld 408576698