| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 class ClassEmitter extends CodeEmitterHelper { | 7 class ClassEmitter extends CodeEmitterHelper { |
| 8 | 8 |
| 9 ClassStubGenerator get _stubGenerator => | 9 ClassStubGenerator get _stubGenerator => |
| 10 new ClassStubGenerator(compiler, namer, backend); | 10 new ClassStubGenerator(compiler, namer, backend); |
| (...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 351 for (DartType interface in classElement.interfaces) { | 351 for (DartType interface in classElement.interfaces) { |
| 352 types.add(task.metadataCollector.reifyType(interface)); | 352 types.add(task.metadataCollector.reifyType(interface)); |
| 353 } | 353 } |
| 354 // TODO(herhut): Fix use of reflection name here. | 354 // TODO(herhut): Fix use of reflection name here. |
| 355 enclosingBuilder.addPropertyByName("+$reflectionName", | 355 enclosingBuilder.addPropertyByName("+$reflectionName", |
| 356 new jsAst.ArrayInitializer(types)); | 356 new jsAst.ArrayInitializer(types)); |
| 357 } | 357 } |
| 358 } | 358 } |
| 359 } | 359 } |
| 360 | 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, | 361 void recordMangledField(Element member, |
| 459 jsAst.Name accessorName, | 362 jsAst.Name accessorName, |
| 460 String memberName) { | 363 String memberName) { |
| 461 if (!backend.shouldRetainGetter(member)) return; | 364 if (!backend.shouldRetainGetter(member)) return; |
| 462 String previousName; | 365 String previousName; |
| 463 if (member.isInstanceMember) { | 366 if (member.isInstanceMember) { |
| 464 previousName = emitter.mangledFieldNames.putIfAbsent( | 367 previousName = emitter.mangledFieldNames.putIfAbsent( |
| 465 namer.deriveGetterName(accessorName), | 368 namer.deriveGetterName(accessorName), |
| 466 () => memberName); | 369 () => memberName); |
| 467 } else { | 370 } else { |
| 468 previousName = emitter.mangledGlobalFieldNames.putIfAbsent( | 371 previousName = emitter.mangledGlobalFieldNames.putIfAbsent( |
| 469 accessorName, | 372 accessorName, |
| 470 () => memberName); | 373 () => memberName); |
| 471 } | 374 } |
| 472 assert(invariant(member, previousName == memberName, | 375 assert(invariant(member, previousName == memberName, |
| 473 message: '$previousName != ${memberName}')); | 376 message: '$previousName != ${memberName}')); |
| 474 } | 377 } |
| 475 | 378 |
| 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, | 379 void generateCheckedSetter(Element member, |
| 510 jsAst.Name fieldName, | 380 jsAst.Name fieldName, |
| 511 jsAst.Name accessorName, | 381 jsAst.Name accessorName, |
| 512 ClassBuilder builder) { | 382 ClassBuilder builder) { |
| 513 jsAst.Expression code = backend.generatedCode[member]; | 383 jsAst.Expression code = backend.generatedCode[member]; |
| 514 assert(code != null); | 384 assert(code != null); |
| 515 jsAst.Name setterName = namer.deriveSetterName(accessorName); | 385 jsAst.Name setterName = namer.deriveSetterName(accessorName); |
| 516 compiler.dumpInfoTask.registerElementAst(member, | 386 compiler.dumpInfoTask.registerElementAst(member, |
| 517 builder.addProperty(setterName, code)); | 387 builder.addProperty(setterName, code)); |
| 518 generateReflectionDataForFieldGetterOrSetter( | 388 generateReflectionDataForFieldGetterOrSetter( |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 567 ? new Selector.getter(member.name, member.library) | 437 ? new Selector.getter(member.name, member.library) |
| 568 : new Selector.setter(member.name, member.library); | 438 : new Selector.setter(member.name, member.library); |
| 569 String reflectionName = emitter.getReflectionName(selector, name); | 439 String reflectionName = emitter.getReflectionName(selector, name); |
| 570 if (reflectionName != null) { | 440 if (reflectionName != null) { |
| 571 var reflectable = | 441 var reflectable = |
| 572 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | 442 js(backend.isAccessibleByReflection(member) ? '1' : '0'); |
| 573 builder.addPropertyByName('+$reflectionName', reflectable); | 443 builder.addPropertyByName('+$reflectionName', reflectable); |
| 574 } | 444 } |
| 575 } | 445 } |
| 576 } | 446 } |
| OLD | NEW |