Skip to content

Commit 5671730

Browse files
fishythefishcommit-bot@chromium.org
authored andcommitted
[dart2js] Pass type arguments to callable properties even with
--omit-implicit-checks. When the default check policy is "trusted" instead of "emitted", we skip checking every function during the RTI need computation. This works because if the type arguments are needed for some other reason, like the literal being used in the body, we see the type use and go back and update the RTI need of the function. We can't do this for callable properties because we don't know which function will be assigned at runtime, so we need to conservatively provide type arguments no matter what. We could optimize this slightly by only providing type arguments if we know a type use occurs in a function which is assignable to the type of the property, but I expect this to save little for the amount of overhead during compilation. We also update the dependency computation to ensure that if a callable property needs type arguments, then they're available for the enclosing context to pass along. Bug: #41449 Fixes: #41449 Change-Id: I98d9024dfa64cbfe33bd43172ffa905a8537649e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154284 Reviewed-by: Stephen Adams <[email protected]> Commit-Queue: Mayank Patke <[email protected]>
1 parent 4c74ebb commit 5671730

File tree

3 files changed

+129
-4
lines changed

3 files changed

+129
-4
lines changed

pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,15 @@ class MethodNode extends CallableNode {
167167
}
168168
}
169169

170+
bool _isProperty(Entity entity) =>
171+
entity is MemberEntity && (entity.isField || entity.isGetter);
172+
170173
class CallablePropertyNode extends CallableNode {
171174
final MemberEntity property;
172175
final DartType type;
173176

174-
CallablePropertyNode(this.property, this.type);
177+
CallablePropertyNode(this.property, this.type)
178+
: assert(_isProperty(property));
175179

176180
@override
177181
Entity get entity => property;
@@ -181,8 +185,6 @@ class CallablePropertyNode extends CallableNode {
181185

182186
@override
183187
bool selectorApplies(Selector selector, BuiltWorld world) {
184-
if (world.annotationsData.getParameterCheckPolicy(property).isTrusted)
185-
return false;
186188
if (property.memberName != selector.memberName) return false;
187189
if (type is FunctionType &&
188190
!selector.callStructure
@@ -305,6 +307,8 @@ class TypeVariableTests {
305307
Iterable<RtiNode> dependencies;
306308
if (entity is ClassEntity) {
307309
dependencies = _classes[entity]?.dependencies;
310+
} else if (_isProperty(entity)) {
311+
dependencies = _callableProperties[entity]?.dependencies;
308312
} else {
309313
dependencies = _methods[entity]?.dependencies;
310314
}
@@ -1031,6 +1035,8 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder {
10311035
});
10321036
} else if (entity is FunctionEntity) {
10331037
methodsNeedingTypeArguments.add(entity);
1038+
} else if (_isProperty(entity)) {
1039+
// Do nothing. We just need to visit the dependencies.
10341040
} else {
10351041
localFunctionsNeedingTypeArguments.add(entity);
10361042
}
@@ -1139,6 +1145,9 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder {
11391145
localFunctionsUsingTypeVariableLiterals
11401146
.forEach(potentiallyNeedTypeArguments);
11411147

1148+
typeVariableTests._callableProperties.keys
1149+
.forEach(potentiallyNeedTypeArguments);
1150+
11421151
if (closedWorld.isMemberUsed(
11431152
closedWorld.commonElements.invocationTypeArgumentGetter)) {
11441153
// If `Invocation.typeArguments` is live, mark all user-defined
@@ -1317,7 +1326,7 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder {
13171326
typeVariableTests
13181327
.forEachAppliedSelector((Selector selector, Set<Entity> targets) {
13191328
for (Entity target in targets) {
1320-
if (target is MemberEntity && (target.isField || target.isGetter) ||
1329+
if (_isProperty(target) ||
13211330
methodsNeedingTypeArguments.contains(target) ||
13221331
localFunctionsNeedingTypeArguments.contains(target)) {
13231332
selectorsNeedingTypeArguments.add(selector);

tests/dart2js/41449c_test.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// dart2jsOptions=-O4
6+
7+
// Regression test for passing type parameters through call-through stub.
8+
//
9+
// We use an abstract class with two implementations to avoid the optimizer
10+
// 'inlining' the call-through stub, so we are testing that the stub itself
11+
// passes through the type parameters.
12+
13+
import 'package:expect/expect.dart';
14+
15+
abstract class AAA {
16+
dynamic get foo;
17+
}
18+
19+
class B1 implements AAA {
20+
final dynamic foo;
21+
B1(this.foo);
22+
}
23+
24+
class B2 implements AAA {
25+
final dynamic _arr;
26+
B2(foo) : _arr = [foo];
27+
dynamic get foo => _arr.first;
28+
}
29+
30+
class B3 implements AAA {
31+
final dynamic __foo;
32+
B3(this.__foo);
33+
dynamic get _foo => __foo;
34+
dynamic get foo => _foo;
35+
}
36+
37+
@pragma('dart2js:noInline')
38+
test1<T>(AAA a, String expected) {
39+
// call-through getter 'foo' with one type argument.
40+
Expect.equals(expected, a.foo<T>());
41+
}
42+
43+
@pragma('dart2js:noInline')
44+
test2<U, V>(AAA a, String expected) {
45+
// call-through getter 'foo' with two type arguments.
46+
Expect.equals(expected, a.foo<U, V>());
47+
}
48+
49+
main() {
50+
test1<int>(B1(<P>() => '$P'), 'int');
51+
test1<num>(B2(<Q>() => '$Q'), 'num');
52+
test1<double>(B3(<R>() => '$R'), 'double');
53+
54+
test2<int, num>(B1(<A, B>() => '$A $B'), 'int num');
55+
test2<num, int>(B2(<X, Y>() => '$X $Y'), 'num int');
56+
test2<double, String>(B3(<C, D>() => '$C $D'), 'double String');
57+
}

tests/dart2js_2/41449c_test.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// dart2jsOptions=-O4
6+
7+
// @dart = 2.7
8+
9+
// Regression test for passing type parameters through call-through stub.
10+
//
11+
// We use an abstract class with two implementations to avoid the optimizer
12+
// 'inlining' the call-through stub, so we are testing that the stub itself
13+
// passes through the type parameters.
14+
15+
import 'package:expect/expect.dart';
16+
17+
abstract class AAA {
18+
dynamic get foo;
19+
}
20+
21+
class B1 implements AAA {
22+
final dynamic foo;
23+
B1(this.foo);
24+
}
25+
26+
class B2 implements AAA {
27+
final dynamic _arr;
28+
B2(foo) : _arr = [foo];
29+
dynamic get foo => _arr.first;
30+
}
31+
32+
class B3 implements AAA {
33+
final dynamic __foo;
34+
B3(this.__foo);
35+
dynamic get _foo => __foo;
36+
dynamic get foo => _foo;
37+
}
38+
39+
@pragma('dart2js:noInline')
40+
test1<T>(AAA a, String expected) {
41+
// call-through getter 'foo' with one type argument.
42+
Expect.equals(expected, a.foo<T>());
43+
}
44+
45+
@pragma('dart2js:noInline')
46+
test2<U, V>(AAA a, String expected) {
47+
// call-through getter 'foo' with two type arguments.
48+
Expect.equals(expected, a.foo<U, V>());
49+
}
50+
51+
main() {
52+
test1<int>(B1(<P>() => '$P'), 'int');
53+
test1<num>(B2(<Q>() => '$Q'), 'num');
54+
test1<double>(B3(<R>() => '$R'), 'double');
55+
56+
test2<int, num>(B1(<A, B>() => '$A $B'), 'int num');
57+
test2<num, int>(B2(<X, Y>() => '$X $Y'), 'num int');
58+
test2<double, String>(B3(<C, D>() => '$C $D'), 'double String');
59+
}

0 commit comments

Comments
 (0)