[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:
committed by
commit-bot@chromium.org
parent
28ed0d743d
commit
cf2966ea0d
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
+40
@@ -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";
|
||||
}
|
||||
}
|
||||
+48
@@ -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);
|
||||
}
|
||||
+33
@@ -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>[]);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user