OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 part of dart_backend; | |
6 | |
7 Comparator get _compareNodes => compareBy((n) => n.getBeginToken().charOffset); | |
8 | |
9 abstract class Renamable implements Comparable { | |
10 final int RENAMABLE_TYPE_ELEMENT = 1; | |
11 final int RENAMABLE_TYPE_MEMBER = 2; | |
12 final int RENAMABLE_TYPE_LOCAL = 3; | |
13 | |
14 final Set<Node> nodes; | |
15 | |
16 Renamable(this.nodes); | |
17 int compareTo(Renamable other) { | |
18 int nodesDiff = other.nodes.length.compareTo(this.nodes.length); | |
19 if (nodesDiff != 0) return nodesDiff; | |
20 int typeDiff = this.kind.compareTo(other.kind); | |
21 return typeDiff != 0 ? typeDiff : compareInternals(other); | |
22 } | |
23 | |
24 int compareInternals(Renamable other); | |
25 int get kind; | |
26 | |
27 String createNewName(PlaceholderRenamer placeholderRenamer); | |
28 } | |
29 | |
30 class GlobalRenamable extends Renamable { | |
31 final Entity entity; | |
32 | |
33 GlobalRenamable(this.entity, Set<Node> nodes) : super(nodes); | |
34 | |
35 int compareInternals(GlobalRenamable other) => | |
36 compareElements(this.entity, other.entity); | |
37 int get kind => RENAMABLE_TYPE_ELEMENT; | |
38 String createNewName(PlaceholderRenamer placeholderRenamer) { | |
39 return placeholderRenamer._renameGlobal(entity); | |
40 } | |
41 } | |
42 | |
43 class MemberRenamable extends Renamable { | |
44 final String identifier; | |
45 MemberRenamable(this.identifier, Set<Node> nodes) : super(nodes); | |
46 int compareInternals(MemberRenamable other) => | |
47 this.identifier.compareTo(other.identifier); | |
48 int get kind => RENAMABLE_TYPE_MEMBER; | |
49 String createNewName(PlaceholderRenamer placeholderRenamer) { | |
50 return placeholderRenamer._generateMemberName(identifier); | |
51 } | |
52 } | |
53 | |
54 class LocalRenamable extends Renamable { | |
55 LocalRenamable(Set<Node> nodes) : super(nodes); | |
56 int compareInternals(LocalRenamable other) => _compareNodes( | |
57 sorted(this.nodes, _compareNodes)[0], | |
58 sorted(other.nodes, _compareNodes)[0]); | |
59 int get kind => RENAMABLE_TYPE_LOCAL; | |
60 String createNewName(PlaceholderRenamer placeholderRenamer) { | |
61 return placeholderRenamer._generateUniqueTopLevelName(""); | |
62 } | |
63 } | |
64 | |
65 /** | |
66 * Renames only top-level elements that would lead to ambiguity if not renamed. | |
67 */ | |
68 class PlaceholderRenamer { | |
69 /// After running [computeRenames] this will contain the computed renames. | |
70 final Map<Node, String> renames = new Map<Node, String>(); | |
71 | |
72 /// After running [computeRenames] this will map the used platform | |
73 /// libraries to their respective prefixes. | |
74 final Map<LibraryElement, String> platformImports = | |
75 <LibraryElement, String>{}; | |
76 | |
77 final bool enableMinification; | |
78 final Set<String> fixedDynamicNames; | |
79 final Set<String> fixedStaticNames; | |
80 final Map<Element, LibraryElement> reexportingLibraries; | |
81 final bool cutDeclarationTypes; | |
82 | |
83 final Map<Entity, String> _renamedCache = new Map<Entity, String>(); | |
84 final Map<Entity, Map<String, String>> _privateCache = | |
85 new Map<Entity, Map<String, String>>(); | |
86 | |
87 // Identifiers that has already been used, or are reserved by the | |
88 // language/platform. | |
89 Set<String> _forbiddenIdentifiers; | |
90 Set<String> _allNamedParameterIdentifiers; | |
91 | |
92 Generator _generator; | |
93 | |
94 PlaceholderRenamer( | |
95 this.fixedDynamicNames, this.fixedStaticNames, this.reexportingLibraries, | |
96 {this.enableMinification, this.cutDeclarationTypes}); | |
97 | |
98 void _renameNodes(Iterable<Node> nodes, String renamer(Node node)) { | |
99 for (Node node in sorted(nodes, _compareNodes)) { | |
100 renames[node] = renamer(node); | |
101 } | |
102 } | |
103 | |
104 String _generateUniqueTopLevelName(String originalName) { | |
105 String newName = _generator.generate(originalName, (name) { | |
106 return _forbiddenIdentifiers.contains(name) || | |
107 _allNamedParameterIdentifiers.contains(name); | |
108 }); | |
109 _forbiddenIdentifiers.add(newName); | |
110 return newName; | |
111 } | |
112 | |
113 String _generateMemberName(String original) { | |
114 return _generator.generate(original, _forbiddenIdentifiers.contains); | |
115 } | |
116 | |
117 /// Looks up [originalName] in the [_privateCache] cache of [library]. | |
118 /// If [originalName] was not renamed before, generate a new name. | |
119 String _getPrivateName(LibraryElement library, String originalName) { | |
120 return _privateCache | |
121 .putIfAbsent(library, () => new Map<String, String>()) | |
122 .putIfAbsent( | |
123 originalName, () => _generateUniqueTopLevelName(originalName)); | |
124 } | |
125 | |
126 String _renameConstructor(ConstructorPlaceholder placeholder) { | |
127 String name = placeholder.element.name; | |
128 if (name == '') return ""; | |
129 String result = _renameGlobal(placeholder.element); | |
130 return result; | |
131 } | |
132 | |
133 String _renameGlobal(Entity entity) { | |
134 assert(entity is! Element || | |
135 Elements.isMalformed(entity) || | |
136 Elements.isStaticOrTopLevel(entity) || | |
137 entity is TypeVariableElement); | |
138 // TODO(smok): We may want to reuse class static field and method names. | |
139 if (entity is Element) { | |
140 LibraryElement library = entity.library; | |
141 if (reexportingLibraries.containsKey(entity)) { | |
142 library = reexportingLibraries[entity]; | |
143 } | |
144 if (library.isPlatformLibrary) { | |
145 // TODO(johnniwinther): Handle prefixes for dart:core. | |
146 if (library.canonicalUri == Uris.dart_core) return entity.name; | |
147 if (library.isInternalLibrary) { | |
148 throw new SpannableAssertionFailure( | |
149 entity, | |
150 "Internal library $library should never have been imported from " | |
151 "the code compiled by dart2dart."); | |
152 } | |
153 | |
154 String prefix = platformImports.putIfAbsent(library, () => null); | |
155 if (entity.isTopLevel && fixedDynamicNames.contains(entity.name)) { | |
156 if (prefix == null) { | |
157 prefix = _generateUniqueTopLevelName(''); | |
158 platformImports[library] = prefix; | |
159 } | |
160 return '$prefix.${entity.name}'; | |
161 } | |
162 return entity.name; | |
163 } | |
164 } | |
165 String name = _renamedCache.putIfAbsent( | |
166 entity, () => _generateUniqueTopLevelName(entity.name)); | |
167 // Look up in [_renamedCache] for a name for [entity] . | |
168 // If it was not renamed before, generate a new name. | |
169 return name; | |
170 } | |
171 | |
172 void _computeMinifiedRenames(PlaceholderCollector placeholderCollector) { | |
173 _generator = new MinifyingGenerator(); | |
174 | |
175 // Build a list sorted by usage of local nodes that will be renamed to | |
176 // the same identifier. So the top-used local variables in all functions | |
177 // will be renamed first and will all share the same new identifier. | |
178 int maxLength = placeholderCollector.functionScopes.values | |
179 .fold(0, (a, b) => max(a, b.localPlaceholders.length)); | |
180 | |
181 List<Set<Node>> allLocals = | |
182 new List<Set<Node>>.generate(maxLength, (_) => new Set<Node>()); | |
183 | |
184 for (FunctionScope functionScope | |
185 in placeholderCollector.functionScopes.values) { | |
186 // Add current sorted local identifiers to the whole sorted list | |
187 // of all local identifiers for all functions. | |
188 List<LocalPlaceholder> currentSortedPlaceholders = sorted( | |
189 functionScope.localPlaceholders, | |
190 compareBy((LocalPlaceholder ph) => -ph.nodes.length)); | |
191 | |
192 List<Set<Node>> currentSortedNodes = currentSortedPlaceholders | |
193 .map((LocalPlaceholder ph) => ph.nodes) | |
194 .toList(); | |
195 | |
196 for (int i = 0; i < currentSortedNodes.length; i++) { | |
197 allLocals[i].addAll(currentSortedNodes[i]); | |
198 } | |
199 } | |
200 | |
201 // Rename elements, members and locals together based on their usage | |
202 // count, otherwise when we rename elements first there will be no good | |
203 // identifiers left for members even if they are used often. | |
204 List<Renamable> renamables = new List<Renamable>(); | |
205 placeholderCollector.elementNodes | |
206 .forEach((Element element, Set<Node> nodes) { | |
207 renamables.add(new GlobalRenamable(element, nodes)); | |
208 }); | |
209 placeholderCollector.memberPlaceholders | |
210 .forEach((String memberName, Set<Identifier> identifiers) { | |
211 renamables.add(new MemberRenamable(memberName, identifiers)); | |
212 }); | |
213 for (Set<Node> localIdentifiers in allLocals) { | |
214 renamables.add(new LocalRenamable(localIdentifiers)); | |
215 } | |
216 renamables.sort(); | |
217 for (Renamable renamable in renamables) { | |
218 String newName = renamable.createNewName(this); | |
219 _renameNodes(renamable.nodes, (_) => newName); | |
220 } | |
221 } | |
222 | |
223 void _computeNonMinifiedRenames(PlaceholderCollector placeholderCollector) { | |
224 _generator = new ConservativeGenerator(); | |
225 // Rename elements. | |
226 placeholderCollector.elementNodes | |
227 .forEach((Element element, Set<Node> nodes) { | |
228 _renameNodes(nodes, (_) => _renameGlobal(element)); | |
229 }); | |
230 | |
231 // Rename locals. | |
232 placeholderCollector.functionScopes | |
233 .forEach((functionElement, functionScope) { | |
234 Set<String> memberIdentifiers = new Set<String>(); | |
235 Set<LocalPlaceholder> placeholders = functionScope.localPlaceholders; | |
236 if (functionElement != null && functionElement.enclosingClass != null) { | |
237 functionElement.enclosingClass.forEachMember((enclosingClass, member) { | |
238 memberIdentifiers.add(member.name); | |
239 }); | |
240 } | |
241 Set<String> usedLocalIdentifiers = new Set<String>(); | |
242 for (LocalPlaceholder placeholder in placeholders) { | |
243 String nextId = _generator.generate(placeholder.identifier, (name) { | |
244 return functionScope.parameterIdentifiers.contains(name) || | |
245 _forbiddenIdentifiers.contains(name) || | |
246 usedLocalIdentifiers.contains(name) || | |
247 memberIdentifiers.contains(name); | |
248 }); | |
249 usedLocalIdentifiers.add(nextId); | |
250 _renameNodes(placeholder.nodes, (_) => nextId); | |
251 } | |
252 }); | |
253 | |
254 // Do not rename members to top-levels, that allows to avoid renaming | |
255 // members to constructors. | |
256 placeholderCollector.memberPlaceholders.forEach((identifier, nodes) { | |
257 String newIdentifier = _generateMemberName(identifier); | |
258 _renameNodes(nodes, (_) => newIdentifier); | |
259 }); | |
260 } | |
261 | |
262 /// Finds renamings for all the placeholders in [placeholderCollector] and | |
263 /// stores them in [renames]. | |
264 /// Also adds to [platformImports] all the platform-libraries that are used. | |
265 void computeRenames(PlaceholderCollector placeholderCollector) { | |
266 _allNamedParameterIdentifiers = new Set<String>(); | |
267 for (FunctionScope functionScope | |
268 in placeholderCollector.functionScopes.values) { | |
269 _allNamedParameterIdentifiers.addAll(functionScope.parameterIdentifiers); | |
270 } | |
271 | |
272 _forbiddenIdentifiers = new Set<String>.from(fixedDynamicNames); | |
273 _forbiddenIdentifiers.addAll(fixedStaticNames); | |
274 _forbiddenIdentifiers.addAll(Keyword.keywords.keys); | |
275 _forbiddenIdentifiers.add('main'); | |
276 | |
277 if (enableMinification) { | |
278 _computeMinifiedRenames(placeholderCollector); | |
279 } else { | |
280 _computeNonMinifiedRenames(placeholderCollector); | |
281 } | |
282 | |
283 // Rename constructors. | |
284 for (ConstructorPlaceholder placeholder | |
285 in placeholderCollector.constructorPlaceholders) { | |
286 renames[placeholder.node] = _renameConstructor(placeholder); | |
287 } | |
288 ; | |
289 | |
290 // Rename private identifiers uniquely for each library. | |
291 placeholderCollector.privateNodes | |
292 .forEach((LibraryElement library, Set<Identifier> identifiers) { | |
293 for (Identifier identifier in identifiers) { | |
294 renames[identifier] = _getPrivateName(library, identifier.source); | |
295 } | |
296 }); | |
297 | |
298 // Rename unresolved nodes, to make sure they still do not resolve. | |
299 for (Node node in placeholderCollector.unresolvedNodes) { | |
300 renames[node] = _generateUniqueTopLevelName('Unresolved'); | |
301 } | |
302 | |
303 // Erase prefixes that are now not needed. | |
304 for (Node node in placeholderCollector.prefixNodesToErase) { | |
305 renames[node] = ''; | |
306 } | |
307 | |
308 if (cutDeclarationTypes) { | |
309 for (DeclarationTypePlaceholder placeholder | |
310 in placeholderCollector.declarationTypePlaceholders) { | |
311 renames[placeholder.typeNode] = placeholder.requiresVar ? 'var' : ''; | |
312 } | |
313 } | |
314 } | |
315 } | |
316 | |
317 /** | |
318 * Generates mini ID based on index. | |
319 * In other words, it converts index to visual representation | |
320 * as if digits are given characters. | |
321 */ | |
322 String generateMiniId(int index) { | |
323 const String firstCharAlphabet = | |
324 r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; | |
325 const String otherCharsAlphabet = | |
326 r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$'; | |
327 // It's like converting index in decimal to [chars] radix. | |
328 if (index < firstCharAlphabet.length) return firstCharAlphabet[index]; | |
329 StringBuffer resultBuilder = new StringBuffer(); | |
330 resultBuilder.writeCharCode( | |
331 firstCharAlphabet.codeUnitAt(index % firstCharAlphabet.length)); | |
332 index ~/= firstCharAlphabet.length; | |
333 int length = otherCharsAlphabet.length; | |
334 while (index >= length) { | |
335 resultBuilder.writeCharCode(otherCharsAlphabet.codeUnitAt(index % length)); | |
336 index ~/= length; | |
337 } | |
338 resultBuilder.write(otherCharsAlphabet[index]); | |
339 return resultBuilder.toString(); | |
340 } | |
341 | |
342 abstract class Generator { | |
343 String generate(String originalName, bool isForbidden(String name)); | |
344 } | |
345 | |
346 /// Always tries to return original identifier name unless it is forbidden. | |
347 class ConservativeGenerator implements Generator { | |
348 String generate(String originalName, bool isForbidden(String name)) { | |
349 String result = originalName; | |
350 int index = 0; | |
351 while (isForbidden(result)) { | |
352 //|| result == originalName) { | |
353 result = '${originalName}_${generateMiniId(index++)}'; | |
354 } | |
355 return result; | |
356 } | |
357 } | |
358 | |
359 /// Always tries to generate the most compact identifier. | |
360 class MinifyingGenerator implements Generator { | |
361 int index = 0; | |
362 | |
363 MinifyingGenerator(); | |
364 | |
365 String generate(String originalName, bool isForbidden(String name)) { | |
366 String result; | |
367 do { | |
368 result = generateMiniId(index++); | |
369 } while (isForbidden(result)); | |
370 return result; | |
371 } | |
372 } | |
OLD | NEW |