| 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 Set<String> _jsReserved = null; | |
| 12 Set<String> get jsReserved { | |
| 13 if (_jsReserved === null) { | |
| 14 _jsReserved = new Set<String>(); | |
| 15 _jsReserved.addAll(JsNames.javaScriptKeywords); | |
| 16 _jsReserved.addAll(JsNames.reservedPropertySymbols); | |
| 17 } | |
| 18 return _jsReserved; | |
| 19 } | |
| 20 | |
| 21 Map<Element, String> globals; | |
| 22 Map<String, int> usedGlobals; | |
| 23 Map<String, LibraryElement> shortPrivateNameOwners; | |
| 24 | |
| 25 Namer(this.compiler) | |
| 26 : globals = new Map<Element, String>(), | |
| 27 usedGlobals = new Map<String, int>(), | |
| 28 shortPrivateNameOwners = new Map<String, LibraryElement>(); | |
| 29 | |
| 30 final String CURRENT_ISOLATE = @'$'; | |
| 31 final String ISOLATE = 'Isolate'; | |
| 32 final String ISOLATE_PROPERTIES = @"$isolateProperties"; | |
| 33 /** Some closures must contain their name. The name is stored in | |
| 34 * [STATIC_CLOSURE_NAME_NAME]. */ | |
| 35 final String STATIC_CLOSURE_NAME_NAME = @'$name'; | |
| 36 static const SourceString CLOSURE_INVOCATION_NAME = | |
| 37 Compiler.CALL_OPERATOR_NAME; | |
| 38 | |
| 39 | |
| 40 String closureInvocationName(Selector selector) { | |
| 41 // TODO(floitsch): mangle, while not conflicting with instance names. | |
| 42 return instanceMethodInvocationName(null, CLOSURE_INVOCATION_NAME, | |
| 43 selector); | |
| 44 } | |
| 45 | |
| 46 String breakLabelName(LabelElement label) { | |
| 47 return '\$${label.labelName}\$${label.target.nestingLevel}'; | |
| 48 } | |
| 49 | |
| 50 String implicitBreakLabelName(TargetElement target) { | |
| 51 return '\$${target.nestingLevel}'; | |
| 52 } | |
| 53 | |
| 54 // We sometimes handle continue targets differently from break targets, | |
| 55 // so we have special continue-only labels. | |
| 56 String continueLabelName(LabelElement label) { | |
| 57 return 'c\$${label.labelName}\$${label.target.nestingLevel}'; | |
| 58 } | |
| 59 | |
| 60 String implicitContinueLabelName(TargetElement target) { | |
| 61 return 'c\$${target.nestingLevel}'; | |
| 62 } | |
| 63 | |
| 64 /** Returns a non-unique name for the given closure element. */ | |
| 65 String closureName(Element element) { | |
| 66 List<String> parts = <String>[]; | |
| 67 SourceString ownName = element.name; | |
| 68 if (ownName == null || ownName.stringValue == "") { | |
| 69 parts.add("anon"); | |
| 70 } else { | |
| 71 parts.add(ownName.slowToString()); | |
| 72 } | |
| 73 for (Element enclosingElement = element.enclosingElement; | |
| 74 enclosingElement != null && | |
| 75 (enclosingElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY | |
| 76 || enclosingElement.kind === ElementKind.CLASS | |
| 77 || enclosingElement.kind === ElementKind.FUNCTION | |
| 78 || enclosingElement.kind === ElementKind.GETTER | |
| 79 || enclosingElement.kind === ElementKind.SETTER); | |
| 80 enclosingElement = enclosingElement.enclosingElement) { | |
| 81 SourceString surroundingName = enclosingElement.name; | |
| 82 if (surroundingName != null) { | |
| 83 String surroundingNameString = surroundingName.slowToString(); | |
| 84 if (surroundingNameString != "") parts.add(surroundingNameString); | |
| 85 } | |
| 86 } | |
| 87 // Invert the parts. | |
| 88 for (int i = 0, j = parts.length - 1; i < j; i++, j--) { | |
| 89 var tmp = parts[i]; | |
| 90 parts[i] = parts[j]; | |
| 91 parts[j] = tmp; | |
| 92 } | |
| 93 return safeName(Strings.join(parts, "_")); | |
| 94 } | |
| 95 | |
| 96 String privateName(LibraryElement lib, SourceString name) { | |
| 97 if (name.isPrivate()) { | |
| 98 String nameString = name.slowToString(); | |
| 99 // The first library asking for a short private name wins. | |
| 100 LibraryElement owner = | |
| 101 shortPrivateNameOwners.putIfAbsent(nameString, () => lib); | |
| 102 // If a private name could clash with a mangled private name we don't | |
| 103 // use the short name. For example a private name "_lib3_foo" would | |
| 104 // clash with "_foo" from "lib3". | |
| 105 if (owner === lib && !nameString.startsWith('_$LIBRARY_PREFIX')) { | |
| 106 return nameString; | |
| 107 } | |
| 108 String libName = getName(lib); | |
| 109 // If a library name does not start with the [LIBRARY_PREFIX] then our | |
| 110 // assumptions about clashing with mangled private members do not hold. | |
| 111 assert(libName.startsWith(LIBRARY_PREFIX)); | |
| 112 return '_$libName$nameString'; | |
| 113 } else { | |
| 114 return name.slowToString(); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 String instanceMethodName(LibraryElement lib, SourceString name, int arity) { | |
| 119 return '${privateName(lib, name)}\$$arity'; | |
| 120 } | |
| 121 | |
| 122 String instanceMethodInvocationName(LibraryElement lib, SourceString name, | |
| 123 Selector selector) { | |
| 124 // TODO(floitsch): mangle, while preserving uniqueness. | |
| 125 StringBuffer buffer = new StringBuffer(); | |
| 126 List<SourceString> names = selector.getOrderedNamedArguments(); | |
| 127 for (SourceString argumentName in names) { | |
| 128 buffer.add(@'$'); | |
| 129 argumentName.printOn(buffer); | |
| 130 } | |
| 131 return '${privateName(lib, name)}\$${selector.argumentCount}$buffer'; | |
| 132 } | |
| 133 | |
| 134 String instanceFieldName(LibraryElement libraryElement, SourceString name) { | |
| 135 String proposedName = privateName(libraryElement, name); | |
| 136 return safeName(proposedName); | |
| 137 } | |
| 138 | |
| 139 String shadowedFieldName(Element fieldElement) { | |
| 140 ClassElement cls = fieldElement.getEnclosingClass(); | |
| 141 LibraryElement libraryElement = fieldElement.getLibrary(); | |
| 142 String libName = getName(libraryElement); | |
| 143 String clsName = getName(cls); | |
| 144 String instanceName = instanceFieldName(libraryElement, fieldElement.name); | |
| 145 return safeName('$libName\$$clsName\$$instanceName'); | |
| 146 } | |
| 147 | |
| 148 String setterName(LibraryElement lib, SourceString name) { | |
| 149 // We dynamically create setters from the field-name. The setter name must | |
| 150 // therefore be derived from the instance field-name. | |
| 151 String fieldName = safeName(privateName(lib, name)); | |
| 152 return 'set\$$fieldName'; | |
| 153 } | |
| 154 | |
| 155 String getterName(LibraryElement lib, SourceString name) { | |
| 156 // We dynamically create getters from the field-name. The getter name must | |
| 157 // therefore be derived from the instance field-name. | |
| 158 String fieldName = safeName(privateName(lib, name)); | |
| 159 return 'get\$$fieldName'; | |
| 160 } | |
| 161 | |
| 162 String getFreshGlobalName(String proposedName) { | |
| 163 String name = proposedName; | |
| 164 int count = usedGlobals[name]; | |
| 165 if (count !== null) { | |
| 166 // Not the first time we see this name. Append a number to make it unique. | |
| 167 do { | |
| 168 name = '$proposedName${count++}'; | |
| 169 } while (usedGlobals[name] !== null); | |
| 170 // Record the count in case we see this name later. We | |
| 171 // frequently see names multiple times, as all our closures use | |
| 172 // the same name for their class. | |
| 173 usedGlobals[proposedName] = count; | |
| 174 } | |
| 175 usedGlobals[name] = 0; | |
| 176 return name; | |
| 177 } | |
| 178 | |
| 179 static const String LIBRARY_PREFIX = "lib"; | |
| 180 | |
| 181 /** | |
| 182 * Returns a preferred JS-id for the given top-level or static element. | |
| 183 * The returned id is guaranteed to be a valid JS-id. | |
| 184 */ | |
| 185 String _computeGuess(Element element) { | |
| 186 assert(!element.isInstanceMember()); | |
| 187 LibraryElement lib = element.getLibrary(); | |
| 188 String name; | |
| 189 if (element.isGenerativeConstructor()) { | |
| 190 if (element.name == element.getEnclosingClass().name) { | |
| 191 // Keep the class name for the class and not the factory. | |
| 192 name = "${element.name.slowToString()}\$"; | |
| 193 } else { | |
| 194 name = element.name.slowToString(); | |
| 195 } | |
| 196 } else if (Elements.isStaticOrTopLevel(element)) { | |
| 197 if (element.isMember()) { | |
| 198 ClassElement enclosingClass = element.getEnclosingClass(); | |
| 199 name = "${enclosingClass.name.slowToString()}_" | |
| 200 "${element.name.slowToString()}"; | |
| 201 } else { | |
| 202 name = element.name.slowToString(); | |
| 203 } | |
| 204 } else if (element.kind === ElementKind.LIBRARY) { | |
| 205 name = LIBRARY_PREFIX; | |
| 206 } else { | |
| 207 name = element.name.slowToString(); | |
| 208 } | |
| 209 // Prefix the name with '$' if it is reserved. | |
| 210 return safeName(name); | |
| 211 } | |
| 212 | |
| 213 String getBailoutName(Element element) { | |
| 214 return '${getName(element)}\$bailout'; | |
| 215 } | |
| 216 | |
| 217 /** | |
| 218 * Returns a preferred JS-id for the given element. The returned id is | |
| 219 * guaranteed to be a valid JS-id. Globals and static fields are furthermore | |
| 220 * guaranteed to be unique. | |
| 221 * | |
| 222 * For accessing statics consider calling | |
| 223 * [isolateAccess]/[isolateBailoutAccess] or [isolatePropertyAccess] instead. | |
| 224 */ | |
| 225 String getName(Element element) { | |
| 226 if (element.isInstanceMember()) { | |
| 227 if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
| 228 ConstructorBodyElement bodyElement = element; | |
| 229 SourceString name = bodyElement.constructor.name; | |
| 230 return instanceMethodName(element.getLibrary(), | |
| 231 name, bodyElement.parameterCount(compiler)); | |
| 232 } else if (element.kind == ElementKind.FUNCTION) { | |
| 233 FunctionElement functionElement = element; | |
| 234 return instanceMethodName(element.getLibrary(), | |
| 235 element.name, | |
| 236 functionElement.parameterCount(compiler)); | |
| 237 } else if (element.kind == ElementKind.GETTER) { | |
| 238 return getterName(element.getLibrary(), element.name); | |
| 239 } else if (element.kind == ElementKind.SETTER) { | |
| 240 return setterName(element.getLibrary(), element.name); | |
| 241 } else if (element.kind == ElementKind.FIELD) { | |
| 242 return instanceFieldName(element.getLibrary(), element.name); | |
| 243 } else { | |
| 244 compiler.internalError('getName for bad kind: ${element.kind}', | |
| 245 node: element.parseNode(compiler)); | |
| 246 } | |
| 247 } else { | |
| 248 // Dealing with a top-level or static element. | |
| 249 String cached = globals[element]; | |
| 250 if (cached !== null) return cached; | |
| 251 | |
| 252 String guess = _computeGuess(element); | |
| 253 ElementKind kind = element.kind; | |
| 254 if (kind === ElementKind.VARIABLE || | |
| 255 kind === ElementKind.PARAMETER) { | |
| 256 // The name is not guaranteed to be unique. | |
| 257 return guess; | |
| 258 } | |
| 259 if (kind === ElementKind.GENERATIVE_CONSTRUCTOR || | |
| 260 kind === ElementKind.FUNCTION || | |
| 261 kind === ElementKind.CLASS || | |
| 262 kind === ElementKind.FIELD || | |
| 263 kind === ElementKind.GETTER || | |
| 264 kind === ElementKind.SETTER || | |
| 265 kind === ElementKind.TYPEDEF || | |
| 266 kind === ElementKind.LIBRARY) { | |
| 267 String result = getFreshGlobalName(guess); | |
| 268 globals[element] = result; | |
| 269 return result; | |
| 270 } | |
| 271 compiler.internalError('getName for unknown kind: ${element.kind}', | |
| 272 node: element.parseNode(compiler)); | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 String getLazyInitializerName(Element element) { | |
| 277 // TODO(floitsch): mangle while not conflicting with other statics. | |
| 278 assert(Elements.isStaticOrTopLevelField(element)); | |
| 279 return "get\$${getName(element)}"; | |
| 280 } | |
| 281 | |
| 282 String isolatePropertiesAccess(Element element) { | |
| 283 return "$ISOLATE.$ISOLATE_PROPERTIES.${getName(element)}"; | |
| 284 } | |
| 285 | |
| 286 String isolatePropertiesAccessForConstant(String constantName) { | |
| 287 return "$ISOLATE.$ISOLATE_PROPERTIES.$constantName"; | |
| 288 } | |
| 289 | |
| 290 String isolateAccess(Element element) { | |
| 291 return "$CURRENT_ISOLATE.${getName(element)}"; | |
| 292 } | |
| 293 | |
| 294 String isolateBailoutAccess(Element element) { | |
| 295 return '${isolateAccess(element)}\$bailout'; | |
| 296 } | |
| 297 | |
| 298 String isolateLazyInitializerAccess(Element element) { | |
| 299 return "$CURRENT_ISOLATE.${getLazyInitializerName(element)}"; | |
| 300 } | |
| 301 | |
| 302 String operatorIs(Element element) { | |
| 303 return 'is\$${getName(element)}'; | |
| 304 } | |
| 305 | |
| 306 String safeName(String name) { | |
| 307 if (jsReserved.contains(name) || name.startsWith('\$')) { | |
| 308 name = "\$$name"; | |
| 309 assert(!jsReserved.contains(name)); | |
| 310 } | |
| 311 return name; | |
| 312 } | |
| 313 } | |
| OLD | NEW |