| Index: pkg/dev_compiler/lib/src/compiler/property_model.dart
|
| diff --git a/pkg/dev_compiler/lib/src/compiler/property_model.dart b/pkg/dev_compiler/lib/src/compiler/property_model.dart
|
| index df5e354172c3acb5fd674e5d07058ea1e184a90f..86b15ab0cd0bfc7149c534633ec9d503706d3cae 100644
|
| --- a/pkg/dev_compiler/lib/src/compiler/property_model.dart
|
| +++ b/pkg/dev_compiler/lib/src/compiler/property_model.dart
|
| @@ -4,12 +4,13 @@
|
|
|
| import 'dart:collection' show HashMap, HashSet, Queue;
|
|
|
| -import 'package:analyzer/dart/ast/ast.dart' show Identifier;
|
| import 'package:analyzer/dart/element/element.dart';
|
| +import 'package:analyzer/dart/element/type.dart' show InterfaceType;
|
| import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl;
|
|
|
| import '../js_ast/js_ast.dart' as JS;
|
| import 'element_helpers.dart';
|
| +import 'extension_types.dart';
|
| import 'js_names.dart' as JS;
|
|
|
| /// Dart allows all fields to be overridden.
|
| @@ -184,8 +185,13 @@ class ClassPropertyModel {
|
| /// super.
|
| final inheritedSetters = new HashSet<String>();
|
|
|
| - ClassPropertyModel.build(VirtualFieldModel fieldModel, ClassElement classElem,
|
| - Iterable<ExecutableElement> extensionMembers) {
|
| + final mockMembers = <String, ExecutableElement>{};
|
| +
|
| + final extensionMembers = new Set<ExecutableElement>();
|
| + final mixinExtensionMembers = new Set<ExecutableElement>();
|
| +
|
| + ClassPropertyModel.build(ExtensionTypeSet extensionTypes,
|
| + VirtualFieldModel fieldModel, ClassElement classElem) {
|
| // 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)) {
|
| @@ -194,20 +200,26 @@ class ClassPropertyModel {
|
| if (accessor.correspondingGetter != null) continue;
|
|
|
| var field = accessor.variable;
|
| - var name = field.name;
|
| // Ignore private names from other libraries.
|
| - if (Identifier.isPrivateName(name) &&
|
| - accessor.library != classElem.library) {
|
| + if (field.isPrivate && accessor.library != classElem.library) {
|
| continue;
|
| }
|
|
|
| - if (field.getter?.isAbstract == false) inheritedGetters.add(name);
|
| - if (field.setter?.isAbstract == false) inheritedSetters.add(name);
|
| + if (field.getter?.isAbstract == false) inheritedGetters.add(field.name);
|
| + if (field.setter?.isAbstract == false) inheritedSetters.add(field.name);
|
| }
|
| }
|
|
|
| - var extensionNames =
|
| - new HashSet<String>.from(extensionMembers.map((e) => e.name));
|
| + _collectMockMembers(classElem.type);
|
| + _collectExtensionMembers(extensionTypes, classElem);
|
| +
|
| + var virtualAccessorNames = new HashSet<String>()
|
| + ..addAll(inheritedGetters)
|
| + ..addAll(inheritedSetters)
|
| + ..addAll(extensionMembers
|
| + .map((m) => m is PropertyAccessorElement ? m.variable.name : m.name))
|
| + ..addAll(mockMembers.values
|
| + .map((m) => m is PropertyAccessorElement ? m.variable.name : m.name));
|
|
|
| // Visit accessors in the current class, and see if they need to be
|
| // generated differently based on the inherited fields/accessors.
|
| @@ -221,9 +233,7 @@ class ClassPropertyModel {
|
| var name = field.name;
|
| // Is it a field?
|
| if (!field.isSynthetic && field is FieldElementImpl) {
|
| - if (inheritedGetters.contains(name) ||
|
| - inheritedSetters.contains(name) ||
|
| - extensionNames.contains(name) ||
|
| + if (virtualAccessorNames.contains(name) ||
|
| fieldModel.isVirtual(field)) {
|
| if (field.isStatic) {
|
| staticFieldOverrides.add(field);
|
| @@ -234,4 +244,87 @@ class ClassPropertyModel {
|
| }
|
| }
|
| }
|
| +
|
| + void _collectMockMembers(InterfaceType type) {
|
| + // TODO(jmesserly): every type with nSM will generate new stubs for all
|
| + // abstract members. For example:
|
| + //
|
| + // class C { m(); noSuchMethod(...) { ... } }
|
| + // class D extends C { m(); noSuchMethod(...) { ... } }
|
| + //
|
| + // We'll generate D.m even though it is not necessary.
|
| + //
|
| + // Doing better is a bit tricky, as our current codegen strategy for the
|
| + // mock methods encodes information about the number of arguments (and type
|
| + // arguments) that D expects.
|
| + var element = type.element;
|
| + if (!hasNoSuchMethod(element)) return;
|
| +
|
| + // Collect all unimplemented members.
|
| + //
|
| + // Initially, we track abstract and concrete members separately, then
|
| + // remove concrete from the abstract set. This is done because abstract
|
| + // members are allowed to "override" concrete ones in Dart.
|
| + // (In that case, it will still be treated as a concrete member and can be
|
| + // called at runtime.)
|
| + var concreteMembers = new HashSet<String>();
|
| +
|
| + void visit(InterfaceType type, bool isAbstract) {
|
| + if (type == null) return;
|
| + visit(type.superclass, isAbstract);
|
| + for (var m in type.mixins) visit(m, isAbstract);
|
| + for (var i in type.interfaces) visit(i, true);
|
| +
|
| + for (var m in [type.methods, type.accessors].expand((m) => m)) {
|
| + if (isAbstract || m.isAbstract) {
|
| + mockMembers[m.name] = m;
|
| + } else if (!m.isStatic) {
|
| + concreteMembers.add(m.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + visit(type, false);
|
| +
|
| + concreteMembers.forEach(mockMembers.remove);
|
| + }
|
| +
|
| + void _collectExtensionMembers(
|
| + ExtensionTypeSet extensionTypes, ClassElement element) {
|
| + if (extensionTypes.isNativeClass(element)) return;
|
| +
|
| + // Collect all extension types we implement.
|
| + var type = element.type;
|
| + var types = extensionTypes.collectNativeInterfaces(element);
|
| + if (types.isEmpty) return;
|
| +
|
| + // Collect all possible extension method names.
|
| + var possibleExtensions = new HashSet<String>();
|
| + for (var t in types) {
|
| + for (var m in [t.methods, t.accessors].expand((m) => m)) {
|
| + if (!m.isStatic && m.isPublic) possibleExtensions.add(m.name);
|
| + }
|
| + }
|
| +
|
| + // Collect all of extension methods this type and its mixins implement.
|
| +
|
| + void visitType(InterfaceType type, bool isMixin) {
|
| + for (var mixin in type.mixins) {
|
| + // If the mixin isn't native, make sure to visit it too, because those
|
| + // methods haven't been accounted for yet.
|
| + if (!extensionTypes.hasNativeSubtype(mixin)) visitType(mixin, true);
|
| + }
|
| + for (var m in [type.methods, type.accessors].expand((m) => m)) {
|
| + if (!m.isAbstract && possibleExtensions.contains(m.name)) {
|
| + (isMixin ? mixinExtensionMembers : extensionMembers).add(m);
|
| + }
|
| + }
|
| + }
|
| +
|
| + visitType(type, false);
|
| +
|
| + for (var m in mockMembers.values) {
|
| + if (possibleExtensions.contains(m.name)) extensionMembers.add(m);
|
| + }
|
| + }
|
| }
|
|
|