Add CFE support for noSuchMethod forwarders

Bug: https://github.com/dart-lang/sdk/issues/31424
Change-Id: Iaf3a8d6b090bda667a9a01c2d8413f8d5dd6d5a8
Reviewed-on: https://dart-review.googlesource.com/47780
Reviewed-by: Samir Jindel <sjindel@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
This commit is contained in:
Dmitry Stefantsov 2018-03-22 16:52:57 +00:00 committed by commit-bot@chromium.org
parent 014bd821e6
commit 4989976a81
6 changed files with 103 additions and 1 deletions

View file

@ -27,7 +27,9 @@ import 'package:kernel/ast.dart'
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/type_algebra.dart' show Substitution;
import 'package:kernel/clone.dart' show CloneWithoutBody;
import 'package:kernel/type_algebra.dart' show Substitution, getSubstitutionMap;
import 'package:kernel/type_environment.dart' show TypeEnvironment;
@ -51,6 +53,8 @@ import '../fasta_codes.dart'
templateOverrideTypeVariablesMismatch,
templateRedirectionTargetNotFound;
import '../names.dart' show noSuchMethodName;
import '../problems.dart' show unexpected, unhandled, unimplemented;
import '../type_inference/type_schema.dart' show UnknownType;
@ -284,6 +288,72 @@ abstract class KernelClassBuilder
});
}
// TODO(dmitryas): Find a better place for this routine.
static bool hasUserDefinedNoSuchMethod(
Class klass, ClassHierarchy hierarchy) {
Member noSuchMethod = hierarchy.getDispatchTarget(klass, noSuchMethodName);
// `Object` doesn't have a superclass reference.
return noSuchMethod != null &&
noSuchMethod.enclosingClass.superclass != null;
}
void addNoSuchMethodForwarderForProcedure(
Procedure procedure, ClassHierarchy hierarchy) {
CloneWithoutBody cloner = new CloneWithoutBody(
typeSubstitution: getSubstitutionMap(
hierarchy.getClassAsInstanceOf(cls, procedure.enclosingClass)));
Procedure cloned = cloner.clone(procedure);
cloned.isAbstract = true;
cloned.isNoSuchMethodForwarder = true;
String name = cloned.name.name;
cls.procedures.add(cloned);
cloned.parent = cls;
DillMemberBuilder memberBuilder = new DillMemberBuilder(cloned, this);
memberBuilder.next = scopeBuilder[name];
scopeBuilder.addMember(name, memberBuilder);
}
void addNoSuchMethodForwarders(ClassHierarchy hierarchy) {
if (!hasUserDefinedNoSuchMethod(cls, hierarchy)) {
return;
}
Set<Name> existingForwardersNames = new Set<Name>();
if (cls.superclass != null &&
hasUserDefinedNoSuchMethod(cls.superclass, hierarchy)) {
List<Member> concrete = hierarchy.getDispatchTargets(cls.superclass);
for (Member member in hierarchy.getInterfaceMembers(cls.superclass)) {
if (ClassHierarchy.findMemberByName(concrete, member.name) == null) {
existingForwardersNames.add(member.name);
}
}
}
if (cls.mixedInClass != null &&
hasUserDefinedNoSuchMethod(cls.mixedInClass, hierarchy)) {
List<Member> concrete = hierarchy.getDispatchTargets(cls.mixedInClass);
for (Member member in hierarchy.getInterfaceMembers(cls.mixedInClass)) {
if (ClassHierarchy.findMemberByName(concrete, member.name) == null) {
existingForwardersNames.add(member.name);
}
}
}
List<Member> concrete = hierarchy.getDispatchTargets(cls);
List<Member> declared = hierarchy.getDeclaredMembers(cls);
for (Member member in hierarchy.getInterfaceMembers(cls)) {
if (ClassHierarchy.findMemberByName(concrete, member.name) == null &&
!existingForwardersNames.contains(member.name)) {
if (ClassHierarchy.findMemberByName(declared, member.name) != null) {
Procedure procedure = member;
procedure.isNoSuchMethodForwarder = true;
} else {
addNoSuchMethodForwarderForProcedure(member, hierarchy);
}
}
}
}
Uri _getMemberUri(Member member) {
if (member is Field) return member.fileUri;
if (member is Procedure) return member.fileUri;

View file

@ -264,6 +264,9 @@ class KernelTarget extends TargetImplementation {
loader.performTopLevelInference(myClasses);
}
loader.checkOverrides(myClasses);
if (backendTarget.enableNoSuchMethodForwarders) {
loader.addNoSuchMethodForwarders(myClasses);
}
} on deprecated_InputError catch (e) {
ticker.logMs("Got deprecated_InputError");
handleInputError(e, isFullComponent: false);

View file

@ -632,6 +632,15 @@ class SourceLoader<L> extends Loader<L> {
ticker.logMs("Checked overrides");
}
void addNoSuchMethodForwarders(List<SourceClassBuilder> sourceClasses) {
for (SourceClassBuilder builder in sourceClasses) {
if (builder.library.loader == this) {
builder.addNoSuchMethodForwarders(hierarchy);
}
}
ticker.logMs("Added noSuchMethod forwarders");
}
void createTypeInferenceEngine() {
typeInferenceEngine =
new ShadowTypeInferenceEngine(instrumentation, target.strongMode);

View file

@ -76,6 +76,15 @@ abstract class Target {
/// promotion do not slow down compilation too much.
bool get disableTypeInference => false;
/// A derived class may change this to `true` to enable forwarders to
/// user-defined `noSuchMethod` that are generated for each abstract member
/// if such `noSuchMethod` is present.
///
/// The forwarders are abstract [Procedure]s with [isNoSuchMethodForwarder]
/// bit set. The implementation of the behavior of such forwarders is up
/// for the target backend.
bool get enableNoSuchMethodForwarders => false;
/// A derived class may change this to `true` to enable Flutter specific
/// "super-mixins" semantics.
///

View file

@ -25,6 +25,9 @@ class VmTarget extends Target {
@override
bool get strongMode => flags.strongMode;
@override
bool get enableNoSuchMethodForwarders => true;
@override
String get name => 'vm';

View file

@ -52,6 +52,9 @@ class B extends self::A {
method noSuchMethod(core::Invocation invocation) → dynamic {
return new self::T1::•();
}
abstract no-such-method-forwarder get bar() → dynamic;
abstract no-such-method-forwarder method foo() → dynamic;
abstract no-such-method-forwarder method bazz(dynamic a1, dynamic a2, dynamic a3, [dynamic a4, dynamic a5]) → dynamic;
}
class C extends core::Object {
synthetic constructor •() → void
@ -65,6 +68,9 @@ class D extends self::C implements self::A {
synthetic constructor •() → void
: super self::C::•()
;
abstract no-such-method-forwarder get bar() → dynamic;
abstract no-such-method-forwarder method foo() → dynamic;
abstract no-such-method-forwarder method bazz(dynamic a1, dynamic a2, dynamic a3, [dynamic a4, dynamic a5]) → dynamic;
}
class E extends core::Object implements self::A {
synthetic constructor •() → void
@ -75,6 +81,8 @@ class E extends core::Object implements self::A {
method noSuchMethod(core::Invocation invocation) → dynamic {
return new self::T4::•();
}
abstract no-such-method-forwarder get bar() → dynamic;
abstract no-such-method-forwarder method bazz(dynamic a1, dynamic a2, dynamic a3, [dynamic a4, dynamic a5]) → dynamic;
}
class F extends core::Object {
synthetic constructor •() → void