| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 dart2js.js_emitter; | |
| 6 | |
| 7 class ClassEmitter extends CodeEmitterHelper { | |
| 8 | |
| 9 ClassStubGenerator get _stubGenerator => | |
| 10 new ClassStubGenerator(compiler, namer, backend); | |
| 11 | |
| 12 /** | |
| 13 * Documentation wanted -- johnniwinther | |
| 14 * | |
| 15 * Invariant: [classElement] must be a declaration element. | |
| 16 */ | |
| 17 void generateClass(ClassElement classElement, | |
| 18 ClassBuilder properties, | |
| 19 Map<String, jsAst.Expression> additionalProperties) { | |
| 20 final onlyForRti = | |
| 21 emitter.typeTestEmitter.rtiNeededClasses.contains(classElement); | |
| 22 | |
| 23 assert(invariant(classElement, classElement.isDeclaration)); | |
| 24 assert(invariant(classElement, !classElement.isNative || onlyForRti)); | |
| 25 | |
| 26 emitter.needsDefineClass = true; | |
| 27 String className = namer.getNameOfClass(classElement); | |
| 28 | |
| 29 ClassElement superclass = classElement.superclass; | |
| 30 String superName = ""; | |
| 31 if (superclass != null) { | |
| 32 superName = namer.getNameOfClass(superclass); | |
| 33 } | |
| 34 | |
| 35 if (classElement.isMixinApplication) { | |
| 36 String mixinName = namer.getNameOfClass(computeMixinClass(classElement)); | |
| 37 superName = '$superName+$mixinName'; | |
| 38 emitter.needsMixinSupport = true; | |
| 39 } | |
| 40 | |
| 41 ClassBuilder builder = new ClassBuilder(classElement, namer); | |
| 42 emitClassConstructor(classElement, builder, onlyForRti: onlyForRti); | |
| 43 emitFields(classElement, builder, superName, onlyForRti: onlyForRti); | |
| 44 emitClassGettersSetters(classElement, builder, onlyForRti: onlyForRti); | |
| 45 emitInstanceMembers(classElement, builder, onlyForRti: onlyForRti); | |
| 46 emitter.typeTestEmitter.emitIsTests(classElement, builder); | |
| 47 if (additionalProperties != null) { | |
| 48 additionalProperties.forEach(builder.addProperty); | |
| 49 } | |
| 50 | |
| 51 if (classElement == backend.closureClass) { | |
| 52 // We add a special getter here to allow for tearing off a closure from | |
| 53 // itself. | |
| 54 String name = namer.getMappedInstanceName(Compiler.CALL_OPERATOR_NAME); | |
| 55 jsAst.Fun function = js('function() { return this; }'); | |
| 56 builder.addProperty(namer.getterNameFromAccessorName(name), function); | |
| 57 } | |
| 58 | |
| 59 emitTypeVariableReaders(classElement, builder); | |
| 60 | |
| 61 emitClassBuilderWithReflectionData( | |
| 62 className, classElement, builder, properties); | |
| 63 } | |
| 64 | |
| 65 void emitClassConstructor(ClassElement classElement, | |
| 66 ClassBuilder builder, | |
| 67 {bool onlyForRti: false}) { | |
| 68 List<String> fields = <String>[]; | |
| 69 if (!onlyForRti && !classElement.isNative) { | |
| 70 visitFields(classElement, false, | |
| 71 (Element member, | |
| 72 String name, | |
| 73 String accessorName, | |
| 74 bool needsGetter, | |
| 75 bool needsSetter, | |
| 76 bool needsCheckedSetter) { | |
| 77 fields.add(name); | |
| 78 }); | |
| 79 } | |
| 80 | |
| 81 jsAst.Expression constructorAst = | |
| 82 _stubGenerator.generateClassConstructor(classElement, fields); | |
| 83 | |
| 84 String constructorName = namer.getNameOfClass(classElement); | |
| 85 OutputUnit outputUnit = | |
| 86 compiler.deferredLoadTask.outputUnitForElement(classElement); | |
| 87 emitter.emitPrecompiledConstructor( | |
| 88 outputUnit, constructorName, constructorAst); | |
| 89 } | |
| 90 | |
| 91 /// Returns `true` if fields added. | |
| 92 bool emitFields(Element element, | |
| 93 ClassBuilder builder, | |
| 94 String superName, | |
| 95 { bool classIsNative: false, | |
| 96 bool emitStatics: false, | |
| 97 bool onlyForRti: false }) { | |
| 98 assert(!emitStatics || !onlyForRti); | |
| 99 if (element.isLibrary) { | |
| 100 assert(invariant(element, emitStatics)); | |
| 101 } else if (!element.isClass) { | |
| 102 throw new SpannableAssertionFailure( | |
| 103 element, 'Must be a ClassElement or a LibraryElement'); | |
| 104 } | |
| 105 if (emitStatics) { | |
| 106 assert(invariant(element, superName == null, message: superName)); | |
| 107 } else { | |
| 108 assert(invariant(element, superName != null)); | |
| 109 builder.superName = superName; | |
| 110 } | |
| 111 var fieldMetadata = []; | |
| 112 bool hasMetadata = false; | |
| 113 bool fieldsAdded = false; | |
| 114 | |
| 115 if (!onlyForRti) { | |
| 116 visitFields(element, emitStatics, | |
| 117 (VariableElement field, | |
| 118 String name, | |
| 119 String accessorName, | |
| 120 bool needsGetter, | |
| 121 bool needsSetter, | |
| 122 bool needsCheckedSetter) { | |
| 123 // Ignore needsCheckedSetter - that is handled below. | |
| 124 bool needsAccessor = (needsGetter || needsSetter); | |
| 125 // We need to output the fields for non-native classes so we can auto- | |
| 126 // generate the constructor. For native classes there are no | |
| 127 // constructors, so we don't need the fields unless we are generating | |
| 128 // accessors at runtime. | |
| 129 if (!classIsNative || needsAccessor) { | |
| 130 var metadata = emitter.metadataEmitter.buildMetadataFunction(field); | |
| 131 if (metadata != null) { | |
| 132 hasMetadata = true; | |
| 133 } else { | |
| 134 metadata = new jsAst.LiteralNull(); | |
| 135 } | |
| 136 fieldMetadata.add(metadata); | |
| 137 recordMangledField(field, accessorName, | |
| 138 namer.privateName(field.library, field.name)); | |
| 139 String fieldName = name; | |
| 140 String fieldCode = ''; | |
| 141 String reflectionMarker = ''; | |
| 142 if (!needsAccessor) { | |
| 143 // Emit field for constructor generation. | |
| 144 assert(!classIsNative); | |
| 145 } else { | |
| 146 // Emit (possibly renaming) field name so we can add accessors at | |
| 147 // runtime. | |
| 148 if (name != accessorName) { | |
| 149 fieldName = '$accessorName:$name'; | |
| 150 } | |
| 151 | |
| 152 int getterCode = 0; | |
| 153 if (needsAccessor && backend.fieldHasInterceptedGetter(field)) { | |
| 154 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
| 155 namer.getterName(field)); | |
| 156 } | |
| 157 if (needsAccessor && backend.fieldHasInterceptedGetter(field)) { | |
| 158 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
| 159 namer.setterName(field)); | |
| 160 } | |
| 161 if (needsGetter) { | |
| 162 if (field.isInstanceMember) { | |
| 163 // 01: function() { return this.field; } | |
| 164 // 10: function(receiver) { return receiver.field; } | |
| 165 // 11: function(receiver) { return this.field; } | |
| 166 bool isIntercepted = backend.fieldHasInterceptedGetter(field); | |
| 167 getterCode += isIntercepted ? 2 : 0; | |
| 168 getterCode += backend.isInterceptorClass(element) ? 0 : 1; | |
| 169 // TODO(sra): 'isInterceptorClass' might not be the correct test | |
| 170 // for methods forced to use the interceptor convention because | |
| 171 // the method's class was elsewhere mixed-in to an interceptor. | |
| 172 assert(!field.isInstanceMember || getterCode != 0); | |
| 173 if (isIntercepted) { | |
| 174 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
| 175 namer.getterName(field)); | |
| 176 } | |
| 177 } else { | |
| 178 getterCode = 1; | |
| 179 } | |
| 180 } | |
| 181 int setterCode = 0; | |
| 182 if (needsSetter) { | |
| 183 if (field.isInstanceMember) { | |
| 184 // 01: function(value) { this.field = value; } | |
| 185 // 10: function(receiver, value) { receiver.field = value; } | |
| 186 // 11: function(receiver, value) { this.field = value; } | |
| 187 bool isIntercepted = backend.fieldHasInterceptedSetter(field); | |
| 188 setterCode += isIntercepted ? 2 : 0; | |
| 189 setterCode += backend.isInterceptorClass(element) ? 0 : 1; | |
| 190 assert(!field.isInstanceMember || setterCode != 0); | |
| 191 if (isIntercepted) { | |
| 192 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
| 193 namer.setterName(field)); | |
| 194 } | |
| 195 } else { | |
| 196 setterCode = 1; | |
| 197 } | |
| 198 } | |
| 199 int code = getterCode + (setterCode << 2); | |
| 200 if (code == 0) { | |
| 201 compiler.internalError(field, | |
| 202 'Field code is 0 ($element/$field).'); | |
| 203 } else { | |
| 204 fieldCode = FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE]; | |
| 205 } | |
| 206 } | |
| 207 if (backend.isAccessibleByReflection(field)) { | |
| 208 DartType type = field.type; | |
| 209 reflectionMarker = '-${emitter.metadataEmitter.reifyType(type)}'; | |
| 210 } | |
| 211 String builtFieldname = '$fieldName$fieldCode$reflectionMarker'; | |
| 212 builder.addField(builtFieldname); | |
| 213 // Add 1 because adding a field to the class also requires a comma | |
| 214 compiler.dumpInfoTask.recordFieldNameSize(field, | |
| 215 builtFieldname.length + 1); | |
| 216 fieldsAdded = true; | |
| 217 } | |
| 218 }); | |
| 219 } | |
| 220 | |
| 221 if (hasMetadata) { | |
| 222 builder.fieldMetadata = fieldMetadata; | |
| 223 } | |
| 224 return fieldsAdded; | |
| 225 } | |
| 226 | |
| 227 void emitClassGettersSetters(ClassElement classElement, | |
| 228 ClassBuilder builder, | |
| 229 {bool onlyForRti: false}) { | |
| 230 if (onlyForRti) return; | |
| 231 | |
| 232 visitFields(classElement, false, | |
| 233 (VariableElement member, | |
| 234 String name, | |
| 235 String accessorName, | |
| 236 bool needsGetter, | |
| 237 bool needsSetter, | |
| 238 bool needsCheckedSetter) { | |
| 239 compiler.withCurrentElement(member, () { | |
| 240 if (needsCheckedSetter) { | |
| 241 assert(!needsSetter); | |
| 242 generateCheckedSetter(member, name, accessorName, builder); | |
| 243 } | |
| 244 if (needsGetter) { | |
| 245 generateGetter(member, name, accessorName, builder); | |
| 246 } | |
| 247 if (needsSetter) { | |
| 248 generateSetter(member, name, accessorName, builder); | |
| 249 } | |
| 250 }); | |
| 251 }); | |
| 252 } | |
| 253 | |
| 254 /** | |
| 255 * Documentation wanted -- johnniwinther | |
| 256 * | |
| 257 * Invariant: [classElement] must be a declaration element. | |
| 258 */ | |
| 259 void emitInstanceMembers(ClassElement classElement, | |
| 260 ClassBuilder builder, | |
| 261 {bool onlyForRti: false}) { | |
| 262 assert(invariant(classElement, classElement.isDeclaration)); | |
| 263 | |
| 264 if (onlyForRti || classElement.isMixinApplication) return; | |
| 265 | |
| 266 void visitMember(ClassElement enclosing, Element member) { | |
| 267 assert(invariant(classElement, member.isDeclaration)); | |
| 268 if (member.isInstanceMember) { | |
| 269 emitter.containerBuilder.addMember(member, builder); | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 classElement.implementation.forEachMember( | |
| 274 visitMember, | |
| 275 includeBackendMembers: true); | |
| 276 | |
| 277 if (identical(classElement, compiler.objectClass) | |
| 278 && compiler.enabledNoSuchMethod) { | |
| 279 // Emit the noSuchMethod handlers on the Object prototype now, | |
| 280 // so that the code in the dynamicFunction helper can find | |
| 281 // them. Note that this helper is invoked before analyzing the | |
| 282 // full JS script. | |
| 283 if (!emitter.nativeEmitter.handleNoSuchMethod) { | |
| 284 emitter.nsmEmitter.emitNoSuchMethodHandlers(builder.addProperty); | |
| 285 } | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void emitClassBuilderWithReflectionData(String className, | |
| 290 ClassElement classElement, | |
| 291 ClassBuilder classBuilder, | |
| 292 ClassBuilder enclosingBuilder) { | |
| 293 var metadata = emitter.metadataEmitter.buildMetadataFunction(classElement); | |
| 294 if (metadata != null) { | |
| 295 classBuilder.addProperty("@", metadata); | |
| 296 } | |
| 297 | |
| 298 if (backend.isAccessibleByReflection(classElement)) { | |
| 299 List<DartType> typeVars = classElement.typeVariables; | |
| 300 Iterable typeVariableProperties = emitter.typeVariableHandler | |
| 301 .typeVariablesOf(classElement).map(js.number); | |
| 302 | |
| 303 ClassElement superclass = classElement.superclass; | |
| 304 bool hasSuper = superclass != null; | |
| 305 if ((!typeVariableProperties.isEmpty && !hasSuper) || | |
| 306 (hasSuper && !equalElements(superclass.typeVariables, typeVars))) { | |
| 307 classBuilder.addProperty('<>', | |
| 308 new jsAst.ArrayInitializer.from(typeVariableProperties)); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 List<jsAst.Property> statics = new List<jsAst.Property>(); | |
| 313 ClassBuilder staticsBuilder = new ClassBuilder(classElement, namer); | |
| 314 if (emitFields(classElement, staticsBuilder, null, emitStatics: true)) { | |
| 315 jsAst.ObjectInitializer initializer = | |
| 316 staticsBuilder.toObjectInitializer(); | |
| 317 compiler.dumpInfoTask.registerElementAst(classElement, | |
| 318 initializer); | |
| 319 jsAst.Node property = initializer.properties.single; | |
| 320 compiler.dumpInfoTask.registerElementAst(classElement, property); | |
| 321 statics.add(property); | |
| 322 } | |
| 323 | |
| 324 ClassBuilder classProperties = | |
| 325 emitter.elementDescriptors.remove(classElement); | |
| 326 if (classProperties != null) { | |
| 327 statics.addAll(classProperties.properties); | |
| 328 } | |
| 329 | |
| 330 if (!statics.isEmpty) { | |
| 331 classBuilder.addProperty('static', new jsAst.ObjectInitializer(statics)); | |
| 332 } | |
| 333 | |
| 334 // TODO(ahe): This method (generateClass) should return a jsAst.Expression. | |
| 335 jsAst.ObjectInitializer propertyValue = classBuilder.toObjectInitializer(); | |
| 336 compiler.dumpInfoTask.registerElementAst(classBuilder.element, propertyValue
); | |
| 337 enclosingBuilder.addProperty(className, propertyValue); | |
| 338 | |
| 339 String reflectionName = emitter.getReflectionName(classElement, className); | |
| 340 if (reflectionName != null) { | |
| 341 if (!backend.isAccessibleByReflection(classElement)) { | |
| 342 enclosingBuilder.addProperty("+$reflectionName", js.number(0)); | |
| 343 } else { | |
| 344 List<int> types = <int>[]; | |
| 345 if (classElement.supertype != null) { | |
| 346 types.add(emitter.metadataEmitter.reifyType(classElement.supertype)); | |
| 347 } | |
| 348 for (DartType interface in classElement.interfaces) { | |
| 349 types.add(emitter.metadataEmitter.reifyType(interface)); | |
| 350 } | |
| 351 enclosingBuilder.addProperty("+$reflectionName", | |
| 352 new jsAst.ArrayInitializer.from(types.map(js.number))); | |
| 353 } | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 /** | |
| 358 * Invokes [f] for each of the fields of [element]. | |
| 359 * | |
| 360 * [element] must be a [ClassElement] or a [LibraryElement]. | |
| 361 * | |
| 362 * If [element] is a [ClassElement], the static fields of the class are | |
| 363 * visited if [visitStatics] is true and the instance fields are visited if | |
| 364 * [visitStatics] is false. | |
| 365 * | |
| 366 * If [element] is a [LibraryElement], [visitStatics] must be true. | |
| 367 * | |
| 368 * When visiting the instance fields of a class, the fields of its superclass | |
| 369 * are also visited if the class is instantiated. | |
| 370 * | |
| 371 * Invariant: [element] must be a declaration element. | |
| 372 */ | |
| 373 void visitFields(Element element, bool visitStatics, AcceptField f) { | |
| 374 assert(invariant(element, element.isDeclaration)); | |
| 375 | |
| 376 bool isClass = false; | |
| 377 bool isLibrary = false; | |
| 378 if (element.isClass) { | |
| 379 isClass = true; | |
| 380 } else if (element.isLibrary) { | |
| 381 isLibrary = true; | |
| 382 assert(invariant(element, visitStatics)); | |
| 383 } else { | |
| 384 throw new SpannableAssertionFailure( | |
| 385 element, 'Expected a ClassElement or a LibraryElement.'); | |
| 386 } | |
| 387 | |
| 388 // If the class is never instantiated we still need to set it up for | |
| 389 // inheritance purposes, but we can simplify its JavaScript constructor. | |
| 390 bool isInstantiated = | |
| 391 compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | |
| 392 | |
| 393 void visitField(Element holder, VariableElement field) { | |
| 394 assert(invariant(element, field.isDeclaration)); | |
| 395 String name = field.name; | |
| 396 | |
| 397 // Keep track of whether or not we're dealing with a field mixin | |
| 398 // into a native class. | |
| 399 bool isMixinNativeField = | |
| 400 isClass && element.isNative && holder.isMixinApplication; | |
| 401 | |
| 402 // See if we can dynamically create getters and setters. | |
| 403 // We can only generate getters and setters for [element] since | |
| 404 // the fields of super classes could be overwritten with getters or | |
| 405 // setters. | |
| 406 bool needsGetter = false; | |
| 407 bool needsSetter = false; | |
| 408 if (isLibrary || isMixinNativeField || holder == element) { | |
| 409 needsGetter = fieldNeedsGetter(field); | |
| 410 needsSetter = fieldNeedsSetter(field); | |
| 411 } | |
| 412 | |
| 413 if ((isInstantiated && !holder.isNative) | |
| 414 || needsGetter | |
| 415 || needsSetter) { | |
| 416 String accessorName = namer.fieldAccessorName(field); | |
| 417 String fieldName = namer.fieldPropertyName(field); | |
| 418 bool needsCheckedSetter = false; | |
| 419 if (compiler.enableTypeAssertions | |
| 420 && needsSetter | |
| 421 && !canAvoidGeneratedCheckedSetter(field)) { | |
| 422 needsCheckedSetter = true; | |
| 423 needsSetter = false; | |
| 424 } | |
| 425 // Getters and setters with suffixes will be generated dynamically. | |
| 426 f(field, fieldName, accessorName, needsGetter, needsSetter, | |
| 427 needsCheckedSetter); | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 if (isLibrary) { | |
| 432 LibraryElement library = element; | |
| 433 library.implementation.forEachLocalMember((Element member) { | |
| 434 if (member.isField) visitField(library, member); | |
| 435 }); | |
| 436 } else if (visitStatics) { | |
| 437 ClassElement cls = element; | |
| 438 cls.implementation.forEachStaticField(visitField); | |
| 439 } else { | |
| 440 ClassElement cls = element; | |
| 441 // TODO(kasperl): We should make sure to only emit one version of | |
| 442 // overridden fields. Right now, we rely on the ordering so the | |
| 443 // fields pulled in from mixins are replaced with the fields from | |
| 444 // the class definition. | |
| 445 | |
| 446 // If a class is not instantiated then we add the field just so we can | |
| 447 // generate the field getter/setter dynamically. Since this is only | |
| 448 // allowed on fields that are in [element] we don't need to visit | |
| 449 // superclasses for non-instantiated classes. | |
| 450 cls.implementation.forEachInstanceField( | |
| 451 visitField, includeSuperAndInjectedMembers: isInstantiated); | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 void recordMangledField(Element member, | |
| 456 String accessorName, | |
| 457 String memberName) { | |
| 458 if (!backend.shouldRetainGetter(member)) return; | |
| 459 String previousName; | |
| 460 if (member.isInstanceMember) { | |
| 461 previousName = emitter.mangledFieldNames.putIfAbsent( | |
| 462 '${namer.getterPrefix}$accessorName', | |
| 463 () => memberName); | |
| 464 } else { | |
| 465 previousName = emitter.mangledGlobalFieldNames.putIfAbsent( | |
| 466 accessorName, | |
| 467 () => memberName); | |
| 468 } | |
| 469 assert(invariant(member, previousName == memberName, | |
| 470 message: '$previousName != ${memberName}')); | |
| 471 } | |
| 472 | |
| 473 bool fieldNeedsGetter(VariableElement field) { | |
| 474 assert(field.isField); | |
| 475 if (fieldAccessNeverThrows(field)) return false; | |
| 476 return backend.shouldRetainGetter(field) | |
| 477 || compiler.codegenWorld.hasInvokedGetter(field, compiler.world); | |
| 478 } | |
| 479 | |
| 480 bool fieldNeedsSetter(VariableElement field) { | |
| 481 assert(field.isField); | |
| 482 if (fieldAccessNeverThrows(field)) return false; | |
| 483 return (!field.isFinal && !field.isConst) | |
| 484 && (backend.shouldRetainSetter(field) | |
| 485 || compiler.codegenWorld.hasInvokedSetter(field, compiler.world)); | |
| 486 } | |
| 487 | |
| 488 // We never access a field in a closure (a captured variable) without knowing | |
| 489 // that it is there. Therefore we don't need to use a getter (that will throw | |
| 490 // if the getter method is missing), but can always access the field directly. | |
| 491 static bool fieldAccessNeverThrows(VariableElement field) { | |
| 492 return field is ClosureFieldElement; | |
| 493 } | |
| 494 | |
| 495 bool canAvoidGeneratedCheckedSetter(VariableElement member) { | |
| 496 // We never generate accessors for top-level/static fields. | |
| 497 if (!member.isInstanceMember) return true; | |
| 498 DartType type = member.type; | |
| 499 return type.treatAsDynamic || (type.element == compiler.objectClass); | |
| 500 } | |
| 501 | |
| 502 void generateCheckedSetter(Element member, | |
| 503 String fieldName, | |
| 504 String accessorName, | |
| 505 ClassBuilder builder) { | |
| 506 jsAst.Expression code = backend.generatedCode[member]; | |
| 507 assert(code != null); | |
| 508 String setterName = namer.setterNameFromAccessorName(accessorName); | |
| 509 compiler.dumpInfoTask.registerElementAst(member, | |
| 510 builder.addProperty(setterName, code)); | |
| 511 generateReflectionDataForFieldGetterOrSetter( | |
| 512 member, setterName, builder, isGetter: false); | |
| 513 } | |
| 514 | |
| 515 void generateGetter(Element member, String fieldName, String accessorName, | |
| 516 ClassBuilder builder) { | |
| 517 jsAst.Expression function = | |
| 518 _stubGenerator.generateGetter(member, fieldName); | |
| 519 | |
| 520 String getterName = namer.getterNameFromAccessorName(accessorName); | |
| 521 ClassElement cls = member.enclosingClass; | |
| 522 String className = namer.getNameOfClass(cls); | |
| 523 OutputUnit outputUnit = | |
| 524 compiler.deferredLoadTask.outputUnitForElement(member); | |
| 525 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 526 js('#.prototype.# = #', [className, getterName, function])); | |
| 527 if (backend.isAccessibleByReflection(member)) { | |
| 528 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 529 js('#.prototype.#.${namer.reflectableField} = 1', | |
| 530 [className, getterName])); | |
| 531 } | |
| 532 } | |
| 533 | |
| 534 void generateSetter(Element member, String fieldName, String accessorName, | |
| 535 ClassBuilder builder) { | |
| 536 jsAst.Expression function = | |
| 537 _stubGenerator.generateSetter(member, fieldName); | |
| 538 | |
| 539 String setterName = namer.setterNameFromAccessorName(accessorName); | |
| 540 ClassElement cls = member.enclosingClass; | |
| 541 String className = namer.getNameOfClass(cls); | |
| 542 OutputUnit outputUnit = | |
| 543 compiler.deferredLoadTask.outputUnitForElement(member); | |
| 544 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 545 js('#.prototype.# = #', [className, setterName, function])); | |
| 546 if (backend.isAccessibleByReflection(member)) { | |
| 547 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 548 js('#.prototype.#.${namer.reflectableField} = 1', | |
| 549 [className, setterName])); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 void generateReflectionDataForFieldGetterOrSetter(Element member, | |
| 554 String name, | |
| 555 ClassBuilder builder, | |
| 556 {bool isGetter}) { | |
| 557 Selector selector = isGetter | |
| 558 ? new Selector.getter(member.name, member.library) | |
| 559 : new Selector.setter(member.name, member.library); | |
| 560 String reflectionName = emitter.getReflectionName(selector, name); | |
| 561 if (reflectionName != null) { | |
| 562 var reflectable = | |
| 563 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | |
| 564 builder.addProperty('+$reflectionName', reflectable); | |
| 565 } | |
| 566 } | |
| 567 | |
| 568 void emitTypeVariableReaders(ClassElement cls, ClassBuilder builder) { | |
| 569 List typeVariables = []; | |
| 570 ClassElement superclass = cls; | |
| 571 while (superclass != null) { | |
| 572 for (TypeVariableType parameter in superclass.typeVariables) { | |
| 573 if (backend.emitter.readTypeVariables.contains(parameter.element)) { | |
| 574 emitTypeVariableReader(cls, builder, parameter.element); | |
| 575 } | |
| 576 } | |
| 577 superclass = superclass.superclass; | |
| 578 } | |
| 579 } | |
| 580 | |
| 581 void emitTypeVariableReader(ClassElement cls, | |
| 582 ClassBuilder builder, | |
| 583 TypeVariableElement element) { | |
| 584 String name = namer.readTypeVariableName(element); | |
| 585 jsAst.Expression index = | |
| 586 js.number(RuntimeTypes.getTypeVariableIndex(element)); | |
| 587 jsAst.Expression computeTypeVariable; | |
| 588 | |
| 589 Substitution substitution = | |
| 590 backend.rti.computeSubstitution( | |
| 591 cls, element.typeDeclaration, alwaysGenerateFunction: true); | |
| 592 if (substitution != null) { | |
| 593 jsAst.Expression typeArguments = | |
| 594 js(r'#.apply(null, this.$builtinTypeInfo)', | |
| 595 substitution.getCode(backend.rti, true)); | |
| 596 computeTypeVariable = js('#[#]', [typeArguments, index]); | |
| 597 } else { | |
| 598 // TODO(ahe): These can be generated dynamically. | |
| 599 computeTypeVariable = | |
| 600 js(r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]', index); | |
| 601 } | |
| 602 jsAst.Expression convertRtiToRuntimeType = | |
| 603 namer.elementAccess(backend.findHelper('convertRtiToRuntimeType')); | |
| 604 compiler.dumpInfoTask.registerElementAst(element, | |
| 605 builder.addProperty(name, | |
| 606 js('function () { return #(#) }', | |
| 607 [convertRtiToRuntimeType, computeTypeVariable]))); | |
| 608 } | |
| 609 } | |
| OLD | NEW |