Index: pkg/dev_compiler/lib/src/compiler/class_property_model.dart |
diff --git a/pkg/dev_compiler/lib/src/compiler/class_property_model.dart b/pkg/dev_compiler/lib/src/compiler/class_property_model.dart |
index 8319c828ffb49350736ae532ed78e5db0e0f86db..55b7677fc5ebad57b7090ebd31b80a576542273e 100644 |
--- a/pkg/dev_compiler/lib/src/compiler/class_property_model.dart |
+++ b/pkg/dev_compiler/lib/src/compiler/class_property_model.dart |
@@ -2,8 +2,9 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-import 'dart:collection' show HashSet; |
+import 'dart:collection' show HashMap, HashSet; |
+import 'package:analyzer/analyzer.dart' as analyzer; |
import 'package:analyzer/dart/ast/ast.dart' show Identifier; |
import 'package:analyzer/dart/element/element.dart'; |
import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; |
@@ -27,6 +28,15 @@ class ClassPropertyModel { |
/// The value property stores the symbol used for the field's storage slot. |
final virtualFields = <FieldElement, JS.TemporaryId>{}; |
+ /// Fields that have been overridden by the current class and must be |
+ /// virtualized. |
+ /// |
+ /// These fields are the elements of the super class, and not the |
+ /// overriding elements. |
+ /// |
+ /// These elements must point to real fields (not getters). |
+ final virtualizedFields = <FieldElement, JS.TemporaryId>{}; |
+ |
/// Static fields that are overridden, this does not matter for Dart but in |
/// JS we need to take care initializing these because JS classes inherit |
/// statics. |
@@ -44,6 +54,10 @@ class ClassPropertyModel { |
ClassPropertyModel.build( |
ClassElement classElem, Iterable<ExecutableElement> extensionMembers) { |
+ LibraryElement classLibrary = classElem.library; |
+ HashMap<String, FieldElementImpl> overriddenFields = new HashMap(); |
+ HashSet<String> alreadyVirtual = new HashSet(); |
+ |
// Visit superclasses to collect information about their fields/accessors. |
// This is expensive so we try to collect everything in one pass. |
for (var base in getSuperclasses(classElem)) { |
@@ -53,14 +67,43 @@ class ClassPropertyModel { |
var field = accessor.variable; |
var name = field.name; |
+ |
// Ignore private names from other libraries. |
if (Identifier.isPrivateName(name) && |
- accessor.library != classElem.library) { |
+ accessor.library != classLibrary) { |
continue; |
} |
- if (field.getter?.isAbstract == false) inheritedGetters.add(name); |
- if (field.setter?.isAbstract == false) inheritedSetters.add(name); |
+ // Ignore abstract getters and setters. |
+ if (!(field.getter?.isAbstract == false || |
+ field.setter?.isAbstract == false)) { |
+ continue; |
+ } |
+ |
+ if (!field.isStatic && !alreadyVirtual.contains(name)) { |
+ // Synthetic field means there is a getter or setter and the member |
+ // is therefore already virtual. |
+ if (field.isSynthetic) { |
+ alreadyVirtual.add(name); |
+ } else if (field is FieldElementImpl) { |
+ if (overriddenFields.containsKey(name)) { |
+ // The field is already overridden by a super class. |
+ // class A { int x; } // Second time the field is added. |
+ // class B extends A { int x; } // First time the field is added. |
+ // class C extends B { ... }; |
+ alreadyVirtual.add(name); |
+ } else { |
+ overriddenFields[name] = field; |
+ } |
+ } |
+ } |
+ |
+ if (field.getter?.isAbstract == false) { |
+ inheritedGetters.add(name); |
+ } |
+ if (field.setter?.isAbstract == false) { |
+ inheritedSetters.add(name); |
+ } |
} |
} |
@@ -77,12 +120,21 @@ class ClassPropertyModel { |
var field = accessor.variable; |
var name = field.name; |
+ |
+ if (!field.isStatic && |
+ !alreadyVirtual.contains(name) && |
+ overriddenFields.containsKey(name)) { |
+ JS.TemporaryId id = new JS.TemporaryId(name); |
+ var superField = overriddenFields[name]; |
+ virtualizedFields[superField] = id; |
+ alreadyVirtual.add(name); |
+ } |
+ |
// Is it a field? |
if (!field.isSynthetic && field is FieldElementImpl) { |
if (inheritedGetters.contains(name) || |
inheritedSetters.contains(name) || |
- extensionNames.contains(name) || |
- field.isVirtual) { |
+ extensionNames.contains(name)) { |
if (field.isStatic) { |
staticFieldOverrides.add(field); |
} else { |
@@ -91,5 +143,38 @@ class ClassPropertyModel { |
} |
} |
} |
+ |
+ // Run through all bodies and find all `super.x` accesses. Then take their |
+ // names (`x`) and see if they need to be virtualized. |
+ for (var name in SuperFinder.accessedSuperProperties(classElem)) { |
+ if (!alreadyVirtual.contains(name) && |
+ overriddenFields.containsKey(name)) { |
+ JS.TemporaryId id = new JS.TemporaryId(name); |
+ var superField = overriddenFields[name]; |
+ virtualizedFields[superField] = id; |
+ } |
+ } |
+ } |
+} |
+ |
+class SuperFinder extends analyzer.GeneralizingAstVisitor<Object> { |
+ final accessed = new HashSet<String>(); |
+ |
+ static Iterable<String> accessedSuperProperties(ClassElement element) { |
+ // When compiling the SDK, only one out of ~50 classes has any references to |
+ // super at all, so we avoid searching the method bodies in the common case. |
+ if (!element.hasReferenceToSuper) return const <String>[]; |
+ SuperFinder finder = new SuperFinder(); |
+ element.computeNode().accept(finder); |
+ return finder.accessed; |
+ } |
+ |
+ @override |
+ analyzer.PropertyAccess visitPropertyAccess(analyzer.PropertyAccess node) { |
+ if (node.realTarget is analyzer.SuperExpression) { |
+ var name = node.propertyName.name; |
+ accessed.add(name); |
+ } |
+ return node; |
} |
} |