[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>
This commit is contained in:
Dmitry Stefantsov
2018-06-27 13:38:07 +00:00
committed by commit-bot@chromium.org
parent 28ed0d743d
commit cf2966ea0d
10 changed files with 231 additions and 4 deletions
@@ -432,6 +432,49 @@ abstract class KernelClassBuilder
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 @@ abstract class KernelClassBuilder
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 @@ abstract class KernelClassBuilder
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;
+5
View File
@@ -130,3 +130,8 @@ incomplete_field_formal_parameter: Fail # Fasta doesn't recover well
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
@@ -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");
}
@@ -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";
}
}
@@ -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);
}
@@ -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>[]);
}
+5
View File
@@ -255,3 +255,8 @@ rasta/type_with_parse_error: Fail
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
+5
View File
@@ -223,3 +223,8 @@ incomplete_field_formal_parameter: Fail # Fasta doesn't recover well
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
@@ -530,7 +530,6 @@ mixin_supertype_subclass_test/02: MissingCompileTimeError
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
@@ -124,7 +124,6 @@ mixin_illegal_superclass_test/27: MissingCompileTimeError
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_override6_test/03: MissingCompileTimeError
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_override6_test/03: MissingCompileTimeError
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