[fasta] Generate noSuchMethod forwarders for abstract accessors
Fixes #32722
Bug: http://dartbug.com/32722
Change-Id: I5a2b29c766a82e06949028dccb69680c685e1ba4
Reviewed-on: https://dart-review.googlesource.com/62151
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Samir Jindel <sjindel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index 930a322..ebb64ec 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -432,6 +432,49 @@
cloned.parent = cls;
}
+ void addNoSuchMethodForwarderGetterForField(Member noSuchMethod,
+ KernelTarget target, Field field, ClassHierarchy hierarchy) {
+ Substitution substitution = Substitution.fromSupertype(
+ hierarchy.getClassAsInstanceOf(cls, field.enclosingClass));
+ Procedure getter = new Procedure(
+ field.name,
+ ProcedureKind.Getter,
+ new FunctionNode(null,
+ typeParameters: <TypeParameter>[],
+ positionalParameters: <VariableDeclaration>[],
+ namedParameters: <VariableDeclaration>[],
+ requiredParameterCount: 0,
+ returnType: substitution.substituteType(field.type)),
+ fileUri: field.fileUri)
+ ..fileOffset = field.fileOffset;
+ transformProcedureToNoSuchMethodForwarder(noSuchMethod, target, getter);
+ cls.procedures.add(getter);
+ getter.parent = cls;
+ }
+
+ void addNoSuchMethodForwarderSetterForField(Member noSuchMethod,
+ KernelTarget target, Field field, ClassHierarchy hierarchy) {
+ Substitution substitution = Substitution.fromSupertype(
+ hierarchy.getClassAsInstanceOf(cls, field.enclosingClass));
+ Procedure setter = new Procedure(
+ field.name,
+ ProcedureKind.Setter,
+ new FunctionNode(null,
+ typeParameters: <TypeParameter>[],
+ positionalParameters: <VariableDeclaration>[
+ new VariableDeclaration("value",
+ type: substitution.substituteType(field.type))
+ ],
+ namedParameters: <VariableDeclaration>[],
+ requiredParameterCount: 1,
+ returnType: const VoidType()),
+ fileUri: field.fileUri)
+ ..fileOffset = field.fileOffset;
+ transformProcedureToNoSuchMethodForwarder(noSuchMethod, target, setter);
+ cls.procedures.add(setter);
+ setter.parent = cls;
+ }
+
/// Adds noSuchMethod forwarding stubs to this class. Returns `true` if the
/// class was modified.
bool addNoSuchMethodForwarders(
@@ -492,6 +535,14 @@
existingForwardersNames.add(member.name);
changed = true;
}
+ if (member is Field &&
+ ClassHierarchy.findMemberByName(concrete, member.name) == null &&
+ !existingForwardersNames.contains(member.name)) {
+ addNoSuchMethodForwarderGetterForField(
+ noSuchMethod, target, member, hierarchy);
+ existingForwardersNames.add(member.name);
+ changed = true;
+ }
}
List<Member> concreteSetters =
@@ -514,6 +565,15 @@
existingSetterForwardersNames.add(member.name);
changed = true;
}
+ if (member is Field &&
+ ClassHierarchy.findMemberByName(concreteSetters, member.name) ==
+ null &&
+ !existingSetterForwardersNames.contains(member.name)) {
+ addNoSuchMethodForwarderSetterForField(
+ noSuchMethod, target, member, hierarchy);
+ existingSetterForwardersNames.add(member.name);
+ changed = true;
+ }
}
return changed;
diff --git a/pkg/front_end/testcases/compile.status b/pkg/front_end/testcases/compile.status
index bdcce31..c0b01c00 100644
--- a/pkg/front_end/testcases/compile.status
+++ b/pkg/front_end/testcases/compile.status
@@ -130,3 +130,8 @@
co19_language_metadata_syntax_t04: RuntimeError # Fasta doesn't recover well
external_import: RuntimeError # Expected -- test uses import which doesn't exist.
+
+no_such_method_forwarders/abstract_accessors_from_field: Fail
+no_such_method_forwarders/abstract_accessors_from_field_arent_mixed_in: Fail
+no_such_method_forwarders/abstract_accessors_from_field_one_defined: Fail
+no_such_method_forwarders/abstract_accessors_from_field_with_substitution: Fail
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field.dart b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field.dart
new file mode 100644
index 0000000..fdaad64
--- /dev/null
+++ b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+// This test checks that noSuchMethod forwarders are generated for abstract
+// accessors implicitly declared via fields of abstract classes. The type
+// checks should be performed for the return values of getters and for the r.h.s
+// of assignments for setters.
+
+void expectTypeError(callback()) {
+ try {
+ callback();
+ throw 'Expected TypeError, did not occur';
+ } on TypeError {}
+}
+
+abstract class I {
+ int foo;
+}
+
+class A implements I {
+ dynamic noSuchMethod(i) => "bar";
+
+ // Should have noSuchMethod forwarders for the 'foo' getter and setter.
+}
+
+class B extends A {
+ // Should not have noSuchMethod forwarders for the 'foo' getter and setter.
+}
+
+main() {
+ var a = new A();
+ expectTypeError(() => a.foo);
+ expectTypeError(() => (a as dynamic).foo = "bar");
+}
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_arent_mixed_in.dart b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_arent_mixed_in.dart
new file mode 100644
index 0000000..cebe1ac
--- /dev/null
+++ b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_arent_mixed_in.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+// This test checks that noSuchMethod forwarders that were generated for
+// abstract accessors declared via field in an interface don't override concrete
+// getters and setters in the mixin application.
+
+int count = 0;
+
+abstract class A {
+ int foo;
+}
+
+class B implements A {
+ noSuchMethod(i) {
+ ++count;
+ return null;
+ }
+
+ // Should receive noSuchMethod forwarders for the 'foo' getter and setter.
+}
+
+class C extends Object with B {
+ // The getter and the setter below shouldn't be overridden with noSuchMethod
+ // forwarders.
+ int get foo => 42;
+ void set foo(int value) {}
+}
+
+main() {
+ var c = new C();
+ if (c.foo != 42) {
+ throw "Value mismatch: c.foo != 42.";
+ }
+ c.foo = 43;
+ if (count != 0) {
+ throw "Value mismatch: count != 0";
+ }
+}
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_one_defined.dart b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_one_defined.dart
new file mode 100644
index 0000000..74d8dee
--- /dev/null
+++ b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_one_defined.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+// This test checks that noSuchMethod forwarders are generated for abstract
+// accessors implicitly declared via fields of abstract classes in case when one
+// of the accessors is defined in a superclass.
+
+void expectTypeError(callback()) {
+ try {
+ callback();
+ throw 'Expected TypeError, did not occur';
+ } on TypeError {}
+}
+
+abstract class A {
+ int foo;
+}
+
+abstract class B implements A {
+ int get foo => 42;
+
+ noSuchMethod(i) => "bar";
+}
+
+class C extends B {
+ // Should receive a noSuchMethod forwarder for the 'foo' setter, but not for
+ // the 'foo' getter.
+}
+
+abstract class D implements A {
+ void set foo(int value) {}
+
+ noSuchMethod(i) => "bar";
+}
+
+class E extends D {
+ // Should receive a noSuchMethod forwarder for the 'foo' getter, but not for
+ // the 'foo' setter.
+}
+
+main() {
+ var c = new C();
+ expectTypeError(() => (c as dynamic).foo = "bar");
+
+ var e = new E();
+ expectTypeError(() => e.foo);
+}
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_with_substitution.dart b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_with_substitution.dart
new file mode 100644
index 0000000..8b3cd2c
--- /dev/null
+++ b/pkg/front_end/testcases/no_such_method_forwarders/abstract_accessors_from_field_with_substitution.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+// This test checks that noSuchMethod forwarders that are generated for abstract
+// accessors synthesized from a field of an interface have the proper type
+// substitution performed on the types of their parameters and on the return
+// type.
+
+void expectTypeError(callback()) {
+ try {
+ callback();
+ throw 'Expected TypeError, did not occur';
+ } on TypeError {}
+}
+
+abstract class A<X> {
+ List<X> foo;
+}
+
+class B implements A<int> {
+ dynamic noSuchMethod(i) => <dynamic>[];
+
+ // The noSuchMethod forwarder for the getter should return `List<int>`.
+
+ // The noSuchMethod forwarder for the setter should take `List<int>`.
+}
+
+main() {
+ var b = new B();
+ expectTypeError(() => b.foo);
+ expectTypeError(() => (b as dynamic).foo = <dynamic>[]);
+}
diff --git a/pkg/front_end/testcases/outline.status b/pkg/front_end/testcases/outline.status
index 1159e1b..1d24e3f 100644
--- a/pkg/front_end/testcases/outline.status
+++ b/pkg/front_end/testcases/outline.status
@@ -255,3 +255,8 @@
instantiate_to_bound/body_typedef_super_bounded_type: Fail # Issue 33444
instantiate_to_bound/typedef_super_bounded_type: Fail # Issue 33444
+
+no_such_method_forwarders/abstract_accessors_from_field: Fail
+no_such_method_forwarders/abstract_accessors_from_field_arent_mixed_in: Fail
+no_such_method_forwarders/abstract_accessors_from_field_one_defined: Fail
+no_such_method_forwarders/abstract_accessors_from_field_with_substitution: Fail
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index d0d42f8..638fc7d 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -223,3 +223,8 @@
co19_language_metadata_syntax_t04: RuntimeError # Fasta doesn't recover well
external_import: RuntimeError # The native extension to import doesn't exist. This is ok.
+
+no_such_method_forwarders/abstract_accessors_from_field: Fail
+no_such_method_forwarders/abstract_accessors_from_field_arent_mixed_in: Fail
+no_such_method_forwarders/abstract_accessors_from_field_one_defined: Fail
+no_such_method_forwarders/abstract_accessors_from_field_with_substitution: Fail
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index bd1548b..65b1564 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -530,7 +530,6 @@
mixin_supertype_subclass_test/05: MissingCompileTimeError
mixin_type_parameters_errors_test/03: MissingCompileTimeError
mixin_type_parameters_errors_test/04: MissingCompileTimeError
-mock_writable_final_field_test: RuntimeError # Issue 30847
mock_writable_final_private_field_test: RuntimeError
multiline_newline_test/06: MissingCompileTimeError
multiline_newline_test/06r: MissingCompileTimeError
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index f2ea23c..c456c91 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -124,7 +124,6 @@
mixin_illegal_superclass_test/28: MissingCompileTimeError
mixin_illegal_superclass_test/29: MissingCompileTimeError
mixin_illegal_superclass_test/30: MissingCompileTimeError
-mock_writable_final_private_field_test: RuntimeError
named_parameters_default_eq_test/none: RuntimeError
nested_generic_closure_test: RuntimeError
no_main_test/01: Crash
@@ -792,7 +791,6 @@
method_override_test: CompileTimeError # Issue 31616
mixin_illegal_super_use_test: Skip # Issues 24478 and 23773
mixin_illegal_superclass_test: Skip # Issues 24478 and 23773
-mock_writable_final_private_field_test: RuntimeError # Issue 30849
named_constructor_test/01: MissingRuntimeError # Fasta bug: Bad compilation of constructor reference.
named_parameters_default_eq_test/none: RuntimeError
nested_generic_closure_test: RuntimeError
@@ -1119,7 +1117,6 @@
method_override_test: CompileTimeError # Issue 31616
mixin_illegal_super_use_test: Skip # Issues 24478 and 23773
mixin_illegal_superclass_test: Skip # Issues 24478 and 23773
-mock_writable_final_private_field_test: RuntimeError # Issue 30849
named_parameters_default_eq_test/none: RuntimeError
nested_generic_closure_test: RuntimeError
no_main_test/01: Skip