OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * Assigns JavaScript identifiers to Dart variables, class-names and members. | |
7 */ | |
8 class Namer { | |
9 final Compiler compiler; | |
10 | |
11 static final CLOSURE_INVOCATION_NAME = const SourceString('\$call'); | |
12 static final OPERATOR_EQUALS = const SourceString('operator\$eq'); | |
13 | |
14 static Set<String> _jsReserved = null; | |
15 Set<String> get jsReserved() { | |
16 if (_jsReserved === null) { | |
17 _jsReserved = new Set<String>(); | |
18 _jsReserved.addAll(JsNames.javaScriptKeywords); | |
19 _jsReserved.addAll(JsNames.reservedPropertySymbols); | |
20 } | |
21 return _jsReserved; | |
22 } | |
23 | |
24 Map<Element, String> globals; | |
25 Map<String, int> usedGlobals; | |
26 | |
27 Namer(this.compiler) | |
28 : globals = new Map<Element, String>(), | |
29 usedGlobals = new Map<String, int>(); | |
30 | |
31 final String CURRENT_ISOLATE = "\$"; | |
32 final String ISOLATE = "Isolate"; | |
33 | |
34 | |
35 String closureInvocationName(Selector selector) { | |
36 // TODO(floitsch): mangle, while not conflicting with instance names. | |
37 return instanceMethodInvocationName(null, CLOSURE_INVOCATION_NAME, | |
38 selector); | |
39 } | |
40 | |
41 String privateName(LibraryElement lib, SourceString name) { | |
42 if (name.isPrivate()) { | |
43 return '_${getName(lib)}${name.slowToString()}'; | |
44 } else { | |
45 return '${name.slowToString()}'; | |
46 } | |
47 } | |
48 | |
49 String instanceMethodName(LibraryElement lib, SourceString name, int arity) { | |
50 return '${privateName(lib, name)}\$$arity'; | |
51 } | |
52 | |
53 String instanceMethodInvocationName(LibraryElement lib, SourceString name, | |
54 Selector selector) { | |
55 // TODO(floitsch): mangle, while preserving uniqueness. | |
56 StringBuffer buffer = new StringBuffer(); | |
57 List<SourceString> names = selector.getOrderedNamedArguments(); | |
58 for (SourceString argumentName in names) { | |
59 buffer.add(@'$'); | |
60 argumentName.printOn(buffer); | |
61 } | |
62 return '${privateName(lib, name)}\$${selector.argumentCount}$buffer'; | |
63 } | |
64 | |
65 String instanceFieldName(LibraryElement lib, SourceString name) { | |
66 return privateName(lib, name); | |
67 } | |
68 | |
69 String setterName(LibraryElement lib, SourceString name) { | |
70 return 'set\$${privateName(lib, name)}'; | |
71 } | |
72 | |
73 String getterName(LibraryElement lib, SourceString name) { | |
74 return 'get\$${privateName(lib, name)}'; | |
75 } | |
76 | |
77 String getFreshGlobalName(String proposedName) { | |
78 int usedCount = usedGlobals[proposedName]; | |
79 if (usedCount === null) { | |
80 // No element with this name has been used before. | |
81 usedGlobals[proposedName] = 1; | |
82 return proposedName; | |
83 } else { | |
84 // Not the first time we see this name. Append a number to make it unique. | |
85 String name; | |
86 do { | |
87 usedCount++; | |
88 name = '$proposedName$usedCount'; | |
89 } while (usedGlobals[name] !== null); | |
90 usedGlobals[proposedName] = usedCount; | |
91 return name; | |
92 } | |
93 } | |
94 | |
95 /** | |
96 * Returns a preferred JS-id for the given top-level or static element. | |
97 * The returned id is guaranteed to be a valid JS-id. | |
98 */ | |
99 String _computeGuess(Element element) { | |
100 assert(!element.isInstanceMember()); | |
101 LibraryElement lib = element.getLibrary(); | |
102 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
103 FunctionElement functionElement = element; | |
104 return instanceMethodName(lib, element.name, | |
105 functionElement.parameterCount(compiler)); | |
106 } else { | |
107 // TODO(floitsch): deal with named constructors. | |
108 String name; | |
109 if (Elements.isStaticOrTopLevel(element)) { | |
110 name = element.name.slowToString(); | |
111 } else if (element.kind == ElementKind.GETTER) { | |
112 name = getterName(lib, element.name); | |
113 } else if (element.kind == ElementKind.SETTER) { | |
114 name = setterName(lib, element.name); | |
115 } else if (element.kind == ElementKind.FUNCTION) { | |
116 FunctionElement functionElement = element; | |
117 name = element.name.slowToString(); | |
118 name = '$name\$${functionElement.parameterCount(compiler)}'; | |
119 } else if (element.kind === ElementKind.LIBRARY) { | |
120 name = 'lib'; | |
121 } else { | |
122 name = element.name.slowToString(); | |
123 } | |
124 // Prefix the name with '$' if it is reserved. | |
125 return safeName(name); | |
126 } | |
127 } | |
128 | |
129 String getBailoutName(Element element) { | |
130 return '${getName(element)}\$bailout'; | |
131 } | |
132 | |
133 /** | |
134 * Returns a preferred JS-id for the given element. The returned id is | |
135 * guaranteed to be a valid JS-id. Globals and static fields are furthermore | |
136 * guaranteed to be unique. | |
137 * | |
138 * For accessing statics consider calling | |
139 * [isolateAccess]/[isolateBailoutAccess] or [isolatePropertyAccess] instead. | |
140 */ | |
141 String getName(Element element) { | |
142 if (element.isInstanceMember()) { | |
143 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
144 ConstructorBodyElement bodyElement = element; | |
145 SourceString name = bodyElement.constructor.name; | |
146 return instanceMethodName(element.getLibrary(), | |
147 name, bodyElement.parameterCount(compiler)); | |
148 } else if (element.kind == ElementKind.FUNCTION) { | |
149 FunctionElement functionElement = element; | |
150 return instanceMethodName(element.getLibrary(), | |
151 element.name, | |
152 functionElement.parameterCount(compiler)); | |
153 } else if (element.kind == ElementKind.GETTER) { | |
154 return getterName(element.getLibrary(), element.name); | |
155 } else if (element.kind == ElementKind.SETTER) { | |
156 return setterName(element.getLibrary(), element.name); | |
157 } else { | |
158 return instanceFieldName(element.getLibrary(), element.name); | |
159 } | |
160 } else { | |
161 // Dealing with a top-level or static element. | |
162 String cached = globals[element]; | |
163 if (cached !== null) return cached; | |
164 | |
165 String guess = _computeGuess(element); | |
166 switch (element.kind) { | |
167 case ElementKind.VARIABLE: | |
168 case ElementKind.PARAMETER: | |
169 // The name is not guaranteed to be unique. | |
170 return guess; | |
171 | |
172 case ElementKind.GENERATIVE_CONSTRUCTOR: | |
173 case ElementKind.FUNCTION: | |
174 case ElementKind.CLASS: | |
175 case ElementKind.FIELD: | |
176 case ElementKind.GETTER: | |
177 case ElementKind.SETTER: | |
178 case ElementKind.TYPEDEF: | |
179 case ElementKind.LIBRARY: | |
180 String result = getFreshGlobalName(guess); | |
181 globals[element] = result; | |
182 return result; | |
183 | |
184 default: | |
185 compiler.internalError('getName for unknown kind: ${element.kind}', | |
186 node: element.parseNode(compiler)); | |
187 } | |
188 } | |
189 } | |
190 | |
191 String isolateAccess(Element element) { | |
192 return "$CURRENT_ISOLATE.${getName(element)}"; | |
193 } | |
194 | |
195 String isolatePropertyAccess(Element element) { | |
196 return "$ISOLATE.prototype.${getName(element)}"; | |
197 } | |
198 | |
199 String isolateBailoutPropertyAccess(Element element) { | |
200 return '${isolatePropertyAccess(element)}\$bailout'; | |
201 } | |
202 | |
203 String isolateBailoutAccess(Element element) { | |
204 return '${isolateAccess(element)}\$bailout'; | |
205 } | |
206 | |
207 String operatorIs(Element element) { | |
208 return 'is\$${getName(element)}'; | |
209 } | |
210 | |
211 String safeName(String name) { | |
212 if (jsReserved.contains(name) || name.startsWith('\$')) { | |
213 name = "\$$name"; | |
214 assert(!jsReserved.contains(name)); | |
215 } | |
216 return name; | |
217 } | |
218 } | |
OLD | NEW |