| 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 |