diff --git a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart index 8259c10ae77b..086d55aa321e 100644 --- a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart +++ b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart @@ -167,11 +167,15 @@ class MethodNode extends CallableNode { } } +bool _isProperty(Entity entity) => + entity is MemberEntity && (entity.isField || entity.isGetter); + class CallablePropertyNode extends CallableNode { final MemberEntity property; final DartType type; - CallablePropertyNode(this.property, this.type); + CallablePropertyNode(this.property, this.type) + : assert(_isProperty(property)); @override Entity get entity => property; @@ -181,8 +185,6 @@ class CallablePropertyNode extends CallableNode { @override bool selectorApplies(Selector selector, BuiltWorld world) { - if (world.annotationsData.getParameterCheckPolicy(property).isTrusted) - return false; if (property.memberName != selector.memberName) return false; if (type is FunctionType && !selector.callStructure @@ -305,6 +307,8 @@ class TypeVariableTests { Iterable dependencies; if (entity is ClassEntity) { dependencies = _classes[entity]?.dependencies; + } else if (_isProperty(entity)) { + dependencies = _callableProperties[entity]?.dependencies; } else { dependencies = _methods[entity]?.dependencies; } @@ -1031,6 +1035,8 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder { }); } else if (entity is FunctionEntity) { methodsNeedingTypeArguments.add(entity); + } else if (_isProperty(entity)) { + // Do nothing. We just need to visit the dependencies. } else { localFunctionsNeedingTypeArguments.add(entity); } @@ -1139,6 +1145,9 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder { localFunctionsUsingTypeVariableLiterals .forEach(potentiallyNeedTypeArguments); + typeVariableTests._callableProperties.keys + .forEach(potentiallyNeedTypeArguments); + if (closedWorld.isMemberUsed( closedWorld.commonElements.invocationTypeArgumentGetter)) { // If `Invocation.typeArguments` is live, mark all user-defined @@ -1317,7 +1326,7 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder { typeVariableTests .forEachAppliedSelector((Selector selector, Set targets) { for (Entity target in targets) { - if (target is MemberEntity && (target.isField || target.isGetter) || + if (_isProperty(target) || methodsNeedingTypeArguments.contains(target) || localFunctionsNeedingTypeArguments.contains(target)) { selectorsNeedingTypeArguments.add(selector); diff --git a/tests/dart2js/41449c_test.dart b/tests/dart2js/41449c_test.dart new file mode 100644 index 000000000000..083143470f6e --- /dev/null +++ b/tests/dart2js/41449c_test.dart @@ -0,0 +1,57 @@ +// Copyright (c) 2020, 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. +// +// dart2jsOptions=-O4 + +// Regression test for passing type parameters through call-through stub. +// +// We use an abstract class with two implementations to avoid the optimizer +// 'inlining' the call-through stub, so we are testing that the stub itself +// passes through the type parameters. + +import 'package:expect/expect.dart'; + +abstract class AAA { + dynamic get foo; +} + +class B1 implements AAA { + final dynamic foo; + B1(this.foo); +} + +class B2 implements AAA { + final dynamic _arr; + B2(foo) : _arr = [foo]; + dynamic get foo => _arr.first; +} + +class B3 implements AAA { + final dynamic __foo; + B3(this.__foo); + dynamic get _foo => __foo; + dynamic get foo => _foo; +} + +@pragma('dart2js:noInline') +test1(AAA a, String expected) { + // call-through getter 'foo' with one type argument. + Expect.equals(expected, a.foo()); +} + +@pragma('dart2js:noInline') +test2(AAA a, String expected) { + // call-through getter 'foo' with two type arguments. + Expect.equals(expected, a.foo()); +} + +main() { + test1(B1(

() => '$P'), 'int'); + test1(B2(() => '$Q'), 'num'); + test1(B3(() => '$R'), 'double'); + + test2(B1(() => '$A $B'), 'int num'); + test2(B2(() => '$X $Y'), 'num int'); + test2(B3(() => '$C $D'), 'double String'); +} diff --git a/tests/dart2js_2/41449c_test.dart b/tests/dart2js_2/41449c_test.dart new file mode 100644 index 000000000000..3b0ee51079e8 --- /dev/null +++ b/tests/dart2js_2/41449c_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2020, 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. +// +// dart2jsOptions=-O4 + +// @dart = 2.7 + +// Regression test for passing type parameters through call-through stub. +// +// We use an abstract class with two implementations to avoid the optimizer +// 'inlining' the call-through stub, so we are testing that the stub itself +// passes through the type parameters. + +import 'package:expect/expect.dart'; + +abstract class AAA { + dynamic get foo; +} + +class B1 implements AAA { + final dynamic foo; + B1(this.foo); +} + +class B2 implements AAA { + final dynamic _arr; + B2(foo) : _arr = [foo]; + dynamic get foo => _arr.first; +} + +class B3 implements AAA { + final dynamic __foo; + B3(this.__foo); + dynamic get _foo => __foo; + dynamic get foo => _foo; +} + +@pragma('dart2js:noInline') +test1(AAA a, String expected) { + // call-through getter 'foo' with one type argument. + Expect.equals(expected, a.foo()); +} + +@pragma('dart2js:noInline') +test2(AAA a, String expected) { + // call-through getter 'foo' with two type arguments. + Expect.equals(expected, a.foo()); +} + +main() { + test1(B1(

() => '$P'), 'int'); + test1(B2(() => '$Q'), 'num'); + test1(B3(() => '$R'), 'double'); + + test2(B1(() => '$A $B'), 'int num'); + test2(B2(() => '$X $Y'), 'num int'); + test2(B3(() => '$C $D'), 'double String'); +}