| 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 void emitClass(Class cls, ClassBuilder enclosingBuilder, Fragment fragment) { | |
| 16 ClassElement classElement = cls.element; | |
| 17 | |
| 18 assert(invariant(classElement, classElement.isDeclaration)); | |
| 19 | |
| 20 emitter.needsClassSupport = true; | |
| 21 | |
| 22 ClassElement superclass = classElement.superclass; | |
| 23 jsAst.Name superName; | |
| 24 if (superclass != null) { | |
| 25 superName = namer.className(superclass); | |
| 26 } | |
| 27 | |
| 28 if (cls.isMixinApplication) { | |
| 29 MixinApplication mixinApplication = cls; | |
| 30 jsAst.Name mixinName = mixinApplication.mixinClass.name; | |
| 31 superName = | |
| 32 new CompoundName([superName, Namer.literalPlus, mixinName]); | |
| 33 emitter.needsMixinSupport = true; | |
| 34 } | |
| 35 | |
| 36 ClassBuilder builder = new ClassBuilder.forClass(classElement, namer); | |
| 37 builder.superName = superName; | |
| 38 emitConstructorsForCSP(cls); | |
| 39 emitFields(cls, builder); | |
| 40 emitCheckedClassSetters(cls, builder); | |
| 41 emitClassGettersSettersForCSP(cls, builder); | |
| 42 emitInstanceMembers(cls, builder); | |
| 43 emitStubs(cls.callStubs, builder); | |
| 44 emitStubs(cls.typeVariableReaderStubs, builder); | |
| 45 emitRuntimeTypeInformation(cls, builder); | |
| 46 emitNativeInfo(cls, builder); | |
| 47 | |
| 48 if (classElement == backend.closureClass) { | |
| 49 // We add a special getter here to allow for tearing off a closure from | |
| 50 // itself. | |
| 51 jsAst.Fun function = js('function() { return this; }'); | |
| 52 jsAst.Name name = namer.getterForMember(Selector.CALL_NAME); | |
| 53 builder.addProperty(name, function); | |
| 54 } | |
| 55 | |
| 56 emitClassBuilderWithReflectionData(cls, builder, enclosingBuilder, | |
| 57 fragment); | |
| 58 } | |
| 59 /** | |
| 60 * Emits the precompiled constructor when in CSP mode. | |
| 61 */ | |
| 62 void emitConstructorsForCSP(Class cls) { | |
| 63 List<jsAst.Name> fieldNames = <jsAst.Name>[]; | |
| 64 | |
| 65 if (!compiler.useContentSecurityPolicy) return; | |
| 66 | |
| 67 if (!cls.onlyForRti && !cls.isNative) { | |
| 68 fieldNames = cls.fields.map((Field field) => field.name).toList(); | |
| 69 } | |
| 70 | |
| 71 ClassElement classElement = cls.element; | |
| 72 | |
| 73 jsAst.Expression constructorAst = | |
| 74 _stubGenerator.generateClassConstructor(classElement, fieldNames); | |
| 75 | |
| 76 jsAst.Name constructorName = namer.className(classElement); | |
| 77 OutputUnit outputUnit = | |
| 78 compiler.deferredLoadTask.outputUnitForElement(classElement); | |
| 79 emitter.assemblePrecompiledConstructor( | |
| 80 outputUnit, constructorName, constructorAst, fieldNames); | |
| 81 } | |
| 82 | |
| 83 /// Returns `true` if fields added. | |
| 84 bool emitFields(FieldContainer container, | |
| 85 ClassBuilder builder, | |
| 86 { bool classIsNative: false, | |
| 87 bool emitStatics: false }) { | |
| 88 Iterable<Field> fields; | |
| 89 if (container is Class) { | |
| 90 if (emitStatics) { | |
| 91 fields = container.staticFieldsForReflection; | |
| 92 } else if (container.onlyForRti) { | |
| 93 return false; | |
| 94 } else { | |
| 95 fields = container.fields; | |
| 96 } | |
| 97 } else { | |
| 98 assert(container is Library); | |
| 99 assert(emitStatics); | |
| 100 fields = container.staticFieldsForReflection; | |
| 101 } | |
| 102 | |
| 103 var fieldMetadata = []; | |
| 104 bool hasMetadata = false; | |
| 105 bool fieldsAdded = false; | |
| 106 | |
| 107 for (Field field in fields) { | |
| 108 FieldElement fieldElement = field.element; | |
| 109 jsAst.Name name = field.name; | |
| 110 jsAst.Name accessorName = field.accessorName; | |
| 111 bool needsGetter = field.needsGetter; | |
| 112 bool needsSetter = field.needsUncheckedSetter; | |
| 113 | |
| 114 // Ignore needsCheckedSetter - that is handled below. | |
| 115 bool needsAccessor = (needsGetter || needsSetter); | |
| 116 // We need to output the fields for non-native classes so we can auto- | |
| 117 // generate the constructor. For native classes there are no | |
| 118 // constructors, so we don't need the fields unless we are generating | |
| 119 // accessors at runtime. | |
| 120 bool needsFieldsForConstructor = !emitStatics && !classIsNative; | |
| 121 if (needsFieldsForConstructor || needsAccessor) { | |
| 122 var metadata = | |
| 123 task.metadataCollector.buildMetadataFunction(fieldElement); | |
| 124 if (metadata != null) { | |
| 125 hasMetadata = true; | |
| 126 } else { | |
| 127 metadata = new jsAst.LiteralNull(); | |
| 128 } | |
| 129 fieldMetadata.add(metadata); | |
| 130 recordMangledField(fieldElement, accessorName, | |
| 131 namer.privateName(fieldElement.memberName)); | |
| 132 List<jsAst.Literal> fieldNameParts = <jsAst.Literal>[]; | |
| 133 if (!needsAccessor) { | |
| 134 // Emit field for constructor generation. | |
| 135 assert(!classIsNative); | |
| 136 fieldNameParts.add(name); | |
| 137 } else { | |
| 138 // Emit (possibly renaming) field name so we can add accessors at | |
| 139 // runtime. | |
| 140 if (name != accessorName) { | |
| 141 fieldNameParts.add(accessorName); | |
| 142 fieldNameParts.add(js.stringPart(':')); | |
| 143 } | |
| 144 fieldNameParts.add(name); | |
| 145 if (field.needsInterceptedGetter) { | |
| 146 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
| 147 namer.getterForElement(fieldElement)); | |
| 148 } | |
| 149 // TODO(16168): The setter creator only looks at the getter-name. | |
| 150 // Even though the setter could avoid the interceptor convention we | |
| 151 // currently still need to add the additional argument. | |
| 152 if (field.needsInterceptedGetter || field.needsInterceptedSetter) { | |
| 153 emitter.interceptorEmitter.interceptorInvocationNames.add( | |
| 154 namer.setterForElement(fieldElement)); | |
| 155 } | |
| 156 | |
| 157 int code = field.getterFlags + (field.setterFlags << 2); | |
| 158 if (code == 0) { | |
| 159 compiler.internalError(fieldElement, | |
| 160 'Field code is 0 ($fieldElement).'); | |
| 161 } | |
| 162 fieldNameParts.add( | |
| 163 js.stringPart(FIELD_CODE_CHARACTERS[code - FIRST_FIELD_CODE])); | |
| 164 } | |
| 165 // Fields can only be reflected if their declaring class is reflectable | |
| 166 // (as they are only accessible via [ClassMirror.declarations]). | |
| 167 // However, set/get operations can be performed on them, so they are | |
| 168 // reflectable in some sense, which leads to [isAccessibleByReflection] | |
| 169 // reporting `true`. | |
| 170 if (backend.isAccessibleByReflection(fieldElement)) { | |
| 171 fieldNameParts.add(new jsAst.LiteralString('-')); | |
| 172 if (fieldElement.isTopLevel || | |
| 173 backend.isAccessibleByReflection(fieldElement.enclosingClass)) { | |
| 174 DartType type = fieldElement.type; | |
| 175 fieldNameParts.add(task.metadataCollector.reifyType(type)); | |
| 176 } | |
| 177 } | |
| 178 jsAst.Literal fieldNameAst = js.concatenateStrings(fieldNameParts); | |
| 179 builder.addField(fieldNameAst); | |
| 180 // Add 1 because adding a field to the class also requires a comma | |
| 181 compiler.dumpInfoTask.registerElementAst(fieldElement, fieldNameAst); | |
| 182 fieldsAdded = true; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 if (hasMetadata) { | |
| 187 builder.fieldMetadata = fieldMetadata; | |
| 188 } | |
| 189 return fieldsAdded; | |
| 190 } | |
| 191 | |
| 192 /// Emits checked setters for fields. | |
| 193 void emitCheckedClassSetters(Class cls, ClassBuilder builder) { | |
| 194 if (cls.onlyForRti) return; | |
| 195 | |
| 196 for (Field field in cls.fields) { | |
| 197 if (field.needsCheckedSetter) { | |
| 198 assert(!field.needsUncheckedSetter); | |
| 199 compiler.withCurrentElement(field.element, () { | |
| 200 generateCheckedSetter( | |
| 201 field.element, field.name, field.accessorName, builder); | |
| 202 }); | |
| 203 } | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 /// Emits getters/setters for fields if compiling in CSP mode. | |
| 208 void emitClassGettersSettersForCSP(Class cls, ClassBuilder builder) { | |
| 209 | |
| 210 if (!compiler.useContentSecurityPolicy || cls.onlyForRti) return; | |
| 211 | |
| 212 for (Field field in cls.fields) { | |
| 213 Element member = field.element; | |
| 214 compiler.withCurrentElement(member, () { | |
| 215 if (field.needsGetter) { | |
| 216 emitGetterForCSP(member, field.name, field.accessorName, builder); | |
| 217 } | |
| 218 if (field.needsUncheckedSetter) { | |
| 219 emitSetterForCSP(member, field.name, field.accessorName, builder); | |
| 220 } | |
| 221 }); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 void emitStubs(Iterable<StubMethod> stubs, ClassBuilder builder) { | |
| 226 for (Method method in stubs) { | |
| 227 jsAst.Property property = builder.addProperty(method.name, method.code); | |
| 228 compiler.dumpInfoTask.registerElementAst(method.element, property); | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 /** | |
| 233 * Documentation wanted -- johnniwinther | |
| 234 * | |
| 235 * Invariant: [classElement] must be a declaration element. | |
| 236 */ | |
| 237 void emitInstanceMembers(Class cls, | |
| 238 ClassBuilder builder) { | |
| 239 ClassElement classElement = cls.element; | |
| 240 assert(invariant(classElement, classElement.isDeclaration)); | |
| 241 | |
| 242 if (cls.onlyForRti || cls.isMixinApplication) return; | |
| 243 | |
| 244 // TODO(herhut): This is a no-op. Should it be removed? | |
| 245 for (Field field in cls.fields) { | |
| 246 emitter.containerBuilder.addMemberField(field, builder); | |
| 247 } | |
| 248 | |
| 249 for (Method method in cls.methods) { | |
| 250 assert(invariant(classElement, method.element.isDeclaration)); | |
| 251 assert(invariant(classElement, method.element.isInstanceMember)); | |
| 252 emitter.containerBuilder.addMemberMethod(method, builder); | |
| 253 } | |
| 254 | |
| 255 if (identical(classElement, compiler.objectClass) | |
| 256 && backend.enabledNoSuchMethod) { | |
| 257 // Emit the noSuchMethod handlers on the Object prototype now, | |
| 258 // so that the code in the dynamicFunction helper can find | |
| 259 // them. Note that this helper is invoked before analyzing the | |
| 260 // full JS script. | |
| 261 emitter.nsmEmitter.emitNoSuchMethodHandlers(builder.addProperty); | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 /// Emits the members from the model. | |
| 266 void emitRuntimeTypeInformation(Class cls, ClassBuilder builder) { | |
| 267 assert(builder.functionType == null); | |
| 268 if (cls.functionTypeIndex != null) { | |
| 269 builder.functionType = cls.functionTypeIndex; | |
| 270 } | |
| 271 | |
| 272 for (Method method in cls.isChecks) { | |
| 273 builder.addProperty(method.name, method.code); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 void emitNativeInfo(Class cls, ClassBuilder builder) { | |
| 278 jsAst.Expression nativeInfo = NativeGenerator.encodeNativeInfo(cls); | |
| 279 if (nativeInfo != null) { | |
| 280 builder.addPropertyByName(namer.nativeSpecProperty, nativeInfo); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 void emitClassBuilderWithReflectionData(Class cls, | |
| 285 ClassBuilder classBuilder, | |
| 286 ClassBuilder enclosingBuilder, | |
| 287 Fragment fragment) { | |
| 288 ClassElement classElement = cls.element; | |
| 289 jsAst.Name className = cls.name; | |
| 290 | |
| 291 var metadata = task.metadataCollector.buildMetadataFunction(classElement); | |
| 292 if (metadata != null) { | |
| 293 classBuilder.addPropertyByName("@", metadata); | |
| 294 } | |
| 295 | |
| 296 if (backend.isAccessibleByReflection(classElement)) { | |
| 297 List<DartType> typeVars = classElement.typeVariables; | |
| 298 Iterable typeVariableProperties = emitter.typeVariableHandler | |
| 299 .typeVariablesOf(classElement); | |
| 300 | |
| 301 ClassElement superclass = classElement.superclass; | |
| 302 bool hasSuper = superclass != null; | |
| 303 if ((!typeVariableProperties.isEmpty && !hasSuper) || | |
| 304 (hasSuper && !equalElements(superclass.typeVariables, typeVars))) { | |
| 305 classBuilder.addPropertyByName('<>', | |
| 306 new jsAst.ArrayInitializer(typeVariableProperties.toList())); | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 List<jsAst.Property> statics = new List<jsAst.Property>(); | |
| 311 ClassBuilder staticsBuilder = | |
| 312 new ClassBuilder.forStatics(classElement, namer); | |
| 313 if (emitFields(cls, staticsBuilder, emitStatics: true)) { | |
| 314 jsAst.ObjectInitializer initializer = | |
| 315 staticsBuilder.toObjectInitializer(); | |
| 316 compiler.dumpInfoTask.registerElementAst(classElement, | |
| 317 initializer); | |
| 318 jsAst.Node property = initializer.properties.single; | |
| 319 compiler.dumpInfoTask.registerElementAst(classElement, property); | |
| 320 statics.add(property); | |
| 321 } | |
| 322 | |
| 323 // TODO(herhut): Do not grab statics out of the properties. | |
| 324 ClassBuilder classProperties = | |
| 325 emitter.elementDescriptors[fragment].remove(classElement); | |
| 326 if (classProperties != null) { | |
| 327 statics.addAll(classProperties.properties); | |
| 328 } | |
| 329 | |
| 330 if (!statics.isEmpty) { | |
| 331 classBuilder.addPropertyByName('static', | |
| 332 new jsAst.ObjectInitializer(statics)); | |
| 333 } | |
| 334 | |
| 335 // TODO(ahe): This method (generateClass) should return a jsAst.Expression. | |
| 336 jsAst.ObjectInitializer propertyValue = | |
| 337 classBuilder.toObjectInitializer(); | |
| 338 compiler.dumpInfoTask.registerElementAst(classBuilder.element, propertyValue
); | |
| 339 enclosingBuilder.addProperty(className, propertyValue); | |
| 340 | |
| 341 String reflectionName = emitter.getReflectionName(classElement, className); | |
| 342 if (reflectionName != null) { | |
| 343 if (!backend.isAccessibleByReflection(classElement)) { | |
| 344 // TODO(herhut): Fix use of reflection name here. | |
| 345 enclosingBuilder.addPropertyByName("+$reflectionName", js.number(0)); | |
| 346 } else { | |
| 347 List<jsAst.Expression> types = <jsAst.Expression>[]; | |
| 348 if (classElement.supertype != null) { | |
| 349 types.add(task.metadataCollector.reifyType(classElement.supertype)); | |
| 350 } | |
| 351 for (DartType interface in classElement.interfaces) { | |
| 352 types.add(task.metadataCollector.reifyType(interface)); | |
| 353 } | |
| 354 // TODO(herhut): Fix use of reflection name here. | |
| 355 enclosingBuilder.addPropertyByName("+$reflectionName", | |
| 356 new jsAst.ArrayInitializer(types)); | |
| 357 } | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 /** | |
| 362 * Invokes [f] for each of the fields of [element]. | |
| 363 * | |
| 364 * [element] must be a [ClassElement] or a [LibraryElement]. | |
| 365 * | |
| 366 * If [element] is a [ClassElement], the static fields of the class are | |
| 367 * visited if [visitStatics] is true and the instance fields are visited if | |
| 368 * [visitStatics] is false. | |
| 369 * | |
| 370 * If [element] is a [LibraryElement], [visitStatics] must be true. | |
| 371 * | |
| 372 * When visiting the instance fields of a class, the fields of its superclass | |
| 373 * are also visited if the class is instantiated. | |
| 374 * | |
| 375 * Invariant: [element] must be a declaration element. | |
| 376 */ | |
| 377 void visitFields(Element element, bool visitStatics, AcceptField f) { | |
| 378 assert(invariant(element, element.isDeclaration)); | |
| 379 | |
| 380 bool isClass = false; | |
| 381 bool isLibrary = false; | |
| 382 if (element.isClass) { | |
| 383 isClass = true; | |
| 384 } else if (element.isLibrary) { | |
| 385 isLibrary = true; | |
| 386 assert(invariant(element, visitStatics)); | |
| 387 } else { | |
| 388 throw new SpannableAssertionFailure( | |
| 389 element, 'Expected a ClassElement or a LibraryElement.'); | |
| 390 } | |
| 391 | |
| 392 // If the class is never instantiated we still need to set it up for | |
| 393 // inheritance purposes, but we can simplify its JavaScript constructor. | |
| 394 bool isInstantiated = | |
| 395 compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | |
| 396 | |
| 397 void visitField(Element holder, FieldElement field) { | |
| 398 assert(invariant(element, field.isDeclaration)); | |
| 399 | |
| 400 // Keep track of whether or not we're dealing with a field mixin | |
| 401 // into a native class. | |
| 402 bool isMixinNativeField = | |
| 403 isClass && element.isNative && holder.isMixinApplication; | |
| 404 | |
| 405 // See if we can dynamically create getters and setters. | |
| 406 // We can only generate getters and setters for [element] since | |
| 407 // the fields of super classes could be overwritten with getters or | |
| 408 // setters. | |
| 409 bool needsGetter = false; | |
| 410 bool needsSetter = false; | |
| 411 if (isLibrary || isMixinNativeField || holder == element) { | |
| 412 needsGetter = fieldNeedsGetter(field); | |
| 413 needsSetter = fieldNeedsSetter(field); | |
| 414 } | |
| 415 | |
| 416 if ((isInstantiated && !holder.isNative) | |
| 417 || needsGetter | |
| 418 || needsSetter) { | |
| 419 jsAst.Name accessorName = namer.fieldAccessorName(field); | |
| 420 jsAst.Name fieldName = namer.fieldPropertyName(field); | |
| 421 bool needsCheckedSetter = false; | |
| 422 if (compiler.enableTypeAssertions | |
| 423 && needsSetter | |
| 424 && !canAvoidGeneratedCheckedSetter(field)) { | |
| 425 needsCheckedSetter = true; | |
| 426 needsSetter = false; | |
| 427 } | |
| 428 // Getters and setters with suffixes will be generated dynamically. | |
| 429 f(field, fieldName, accessorName, needsGetter, needsSetter, | |
| 430 needsCheckedSetter); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 if (isLibrary) { | |
| 435 LibraryElement library = element; | |
| 436 library.implementation.forEachLocalMember((Element member) { | |
| 437 if (member.isField) visitField(library, member); | |
| 438 }); | |
| 439 } else if (visitStatics) { | |
| 440 ClassElement cls = element; | |
| 441 cls.implementation.forEachStaticField(visitField); | |
| 442 } else { | |
| 443 ClassElement cls = element; | |
| 444 // TODO(kasperl): We should make sure to only emit one version of | |
| 445 // overridden fields. Right now, we rely on the ordering so the | |
| 446 // fields pulled in from mixins are replaced with the fields from | |
| 447 // the class definition. | |
| 448 | |
| 449 // If a class is not instantiated then we add the field just so we can | |
| 450 // generate the field getter/setter dynamically. Since this is only | |
| 451 // allowed on fields that are in [element] we don't need to visit | |
| 452 // superclasses for non-instantiated classes. | |
| 453 cls.implementation.forEachInstanceField( | |
| 454 visitField, includeSuperAndInjectedMembers: isInstantiated); | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 void recordMangledField(Element member, | |
| 459 jsAst.Name accessorName, | |
| 460 String memberName) { | |
| 461 if (!backend.shouldRetainGetter(member)) return; | |
| 462 String previousName; | |
| 463 if (member.isInstanceMember) { | |
| 464 previousName = emitter.mangledFieldNames.putIfAbsent( | |
| 465 namer.deriveGetterName(accessorName), | |
| 466 () => memberName); | |
| 467 } else { | |
| 468 previousName = emitter.mangledGlobalFieldNames.putIfAbsent( | |
| 469 accessorName, | |
| 470 () => memberName); | |
| 471 } | |
| 472 assert(invariant(member, previousName == memberName, | |
| 473 message: '$previousName != ${memberName}')); | |
| 474 } | |
| 475 | |
| 476 bool fieldNeedsGetter(VariableElement field) { | |
| 477 assert(field.isField); | |
| 478 if (fieldAccessNeverThrows(field)) return false; | |
| 479 if (backend.shouldRetainGetter(field)) return true; | |
| 480 return field.isClassMember && | |
| 481 compiler.codegenWorld.hasInvokedGetter(field, compiler.world); | |
| 482 } | |
| 483 | |
| 484 bool fieldNeedsSetter(VariableElement field) { | |
| 485 assert(field.isField); | |
| 486 if (fieldAccessNeverThrows(field)) return false; | |
| 487 if (field.isFinal || field.isConst) return false; | |
| 488 if (backend.shouldRetainSetter(field)) return true; | |
| 489 return field.isClassMember && | |
| 490 compiler.codegenWorld.hasInvokedSetter(field, compiler.world); | |
| 491 } | |
| 492 | |
| 493 static bool fieldAccessNeverThrows(VariableElement field) { | |
| 494 return | |
| 495 // We never access a field in a closure (a captured variable) without | |
| 496 // knowing that it is there. Therefore we don't need to use a getter | |
| 497 // (that will throw if the getter method is missing), but can always | |
| 498 // access the field directly. | |
| 499 field is ClosureFieldElement; | |
| 500 } | |
| 501 | |
| 502 bool canAvoidGeneratedCheckedSetter(VariableElement member) { | |
| 503 // We never generate accessors for top-level/static fields. | |
| 504 if (!member.isInstanceMember) return true; | |
| 505 DartType type = member.type; | |
| 506 return type.treatAsDynamic || (type.element == compiler.objectClass); | |
| 507 } | |
| 508 | |
| 509 void generateCheckedSetter(Element member, | |
| 510 jsAst.Name fieldName, | |
| 511 jsAst.Name accessorName, | |
| 512 ClassBuilder builder) { | |
| 513 jsAst.Expression code = backend.generatedCode[member]; | |
| 514 assert(code != null); | |
| 515 jsAst.Name setterName = namer.deriveSetterName(accessorName); | |
| 516 compiler.dumpInfoTask.registerElementAst(member, | |
| 517 builder.addProperty(setterName, code)); | |
| 518 generateReflectionDataForFieldGetterOrSetter( | |
| 519 member, setterName, builder, isGetter: false); | |
| 520 } | |
| 521 | |
| 522 void emitGetterForCSP(Element member, jsAst.Name fieldName, | |
| 523 jsAst.Name accessorName, | |
| 524 ClassBuilder builder) { | |
| 525 jsAst.Expression function = | |
| 526 _stubGenerator.generateGetter(member, fieldName); | |
| 527 | |
| 528 jsAst.Name getterName = namer.deriveGetterName(accessorName); | |
| 529 ClassElement cls = member.enclosingClass; | |
| 530 jsAst.Name className = namer.className(cls); | |
| 531 OutputUnit outputUnit = | |
| 532 compiler.deferredLoadTask.outputUnitForElement(member); | |
| 533 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 534 js('#.prototype.# = #', [className, getterName, function])); | |
| 535 if (backend.isAccessibleByReflection(member)) { | |
| 536 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 537 js('#.prototype.#.${namer.reflectableField} = 1', | |
| 538 [className, getterName])); | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 void emitSetterForCSP(Element member, jsAst.Name fieldName, | |
| 543 jsAst.Name accessorName, | |
| 544 ClassBuilder builder) { | |
| 545 jsAst.Expression function = | |
| 546 _stubGenerator.generateSetter(member, fieldName); | |
| 547 | |
| 548 jsAst.Name setterName = namer.deriveSetterName(accessorName); | |
| 549 ClassElement cls = member.enclosingClass; | |
| 550 jsAst.Name className = namer.className(cls); | |
| 551 OutputUnit outputUnit = | |
| 552 compiler.deferredLoadTask.outputUnitForElement(member); | |
| 553 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 554 js('#.prototype.# = #', [className, setterName, function])); | |
| 555 if (backend.isAccessibleByReflection(member)) { | |
| 556 emitter.cspPrecompiledFunctionFor(outputUnit).add( | |
| 557 js('#.prototype.#.${namer.reflectableField} = 1', | |
| 558 [className, setterName])); | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 void generateReflectionDataForFieldGetterOrSetter(Element member, | |
| 563 jsAst.Name name, | |
| 564 ClassBuilder builder, | |
| 565 {bool isGetter}) { | |
| 566 Selector selector = isGetter | |
| 567 ? new Selector.getter(member.name, member.library) | |
| 568 : new Selector.setter(member.name, member.library); | |
| 569 String reflectionName = emitter.getReflectionName(selector, name); | |
| 570 if (reflectionName != null) { | |
| 571 var reflectable = | |
| 572 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | |
| 573 builder.addPropertyByName('+$reflectionName', reflectable); | |
| 574 } | |
| 575 } | |
| 576 } | |
| OLD | NEW |