Skip to content

Commit 0c9067c

Browse files
alexmarkovCommit Queue
authored and
Commit Queue
committed
[vm,aot] Preliminary support for dynamic interface in AOT
TEST=runtime/tests/vm/dart/dynamic_module_pragmas_il_test.dart Change-Id: I6efd24f55726db858711d5f77beabf5659e288a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371563 Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent 1e5f965 commit 0c9067c

20 files changed

+527
-35
lines changed

pkg/vm/lib/transformations/dynamic_interface_annotator.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ InstanceConstant pragmaConstant(CoreTypes coreTypes, String pragmaName) {
4343
});
4444
}
4545

46-
void parseAndAnnotate(YamlList items, String pragmaName, Uri baseUri,
46+
void parseAndAnnotate(YamlList? items, String pragmaName, Uri baseUri,
4747
Component component, CoreTypes coreTypes, LibraryIndex libraryIndex,
4848
{required bool allowMembers, bool onlyInstanceMembers = false}) {
49+
if (items == null) return;
4950
final pragma = pragmaConstant(coreTypes, pragmaName);
50-
5151
for (final item in items) {
5252
final nodes = findNodes(item, baseUri, libraryIndex, component,
5353
allowMembers: allowMembers, onlyInstanceMembers: onlyInstanceMembers);

pkg/vm/lib/transformations/type_flow/analysis.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,10 @@ final class _DispatchableInvocation extends _Invocation {
760760
ConeType receiver,
761761
Map<Member, _ReceiverTypeBuilder> targets,
762762
TypeFlowAnalysis typeFlowAnalysis) {
763+
if (kPrintTrace) {
764+
tracePrint(
765+
"Collecting targets for dynamically extendable receiver $receiver");
766+
}
763767
final cls = receiver.cls as _TFClassImpl;
764768
// Collect possible targets among dynamically extendable
765769
// subtypes as they may have allocated subtypes at run time.
@@ -770,6 +774,10 @@ final class _DispatchableInvocation extends _Invocation {
770774
Member? target = extendableSubtype.getDispatchTarget(selector);
771775
if (target != null) {
772776
if (areArgumentsValidFor(target)) {
777+
if (kPrintTrace) {
778+
tracePrint(
779+
"Found target $target in a dynamically extendable subtype $extendableSubtype");
780+
}
773781
// Overwrite previously added receiver type builder.
774782
targets[target] = receiverTypeBuilder;
775783
isDynamicallyOverridden = isDynamicallyOverridden ||
@@ -779,13 +787,19 @@ final class _DispatchableInvocation extends _Invocation {
779787
assert(selector is DynamicSelector);
780788
_recordMismatchedDynamicInvocation(target, typeFlowAnalysis);
781789
}
790+
} else {
791+
isDynamicallyOverridden = true;
782792
}
783793
}
784794
if (selector is DynamicSelector) {
785795
targets[noSuchMethodMarker] = receiverTypeBuilder;
786796
isDynamicallyOverridden = true;
787797
}
788-
return isDynamicallyOverridden && !selector.name.isPrivate;
798+
if (kPrintTrace) {
799+
tracePrint(
800+
"isDynamicallyOverridden = $isDynamicallyOverridden, isPrivate = ${selector.name.isPrivate}");
801+
}
802+
return !isDynamicallyOverridden || selector.name.isPrivate;
789803
}
790804

791805
void _recordMismatchedDynamicInvocation(
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
// Copyright (c) 2024, 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+
// Test for @pragma('dyn-module:extendable') and @pragma('dyn-module:can-be-overridden').
6+
7+
import 'package:vm/testing/il_matchers.dart';
8+
9+
@pragma('vm:never-inline')
10+
void myprint(Object message) {
11+
print(message);
12+
}
13+
14+
abstract class A1 {
15+
void foo();
16+
void bar();
17+
void baz();
18+
}
19+
20+
class B1 extends A1 {
21+
void foo() {
22+
myprint('B1.foo');
23+
}
24+
25+
void bar() {
26+
myprint('B1.bar');
27+
}
28+
29+
@pragma('vm:never-inline')
30+
void baz() {
31+
myprint('B1.baz');
32+
}
33+
}
34+
35+
class C1 extends B1 {
36+
@pragma('vm:never-inline')
37+
void baz() {
38+
myprint('C1.baz');
39+
}
40+
}
41+
42+
@pragma('vm:never-inline')
43+
@pragma('vm:testing:print-flow-graph')
44+
void callA1(A1 obj) {
45+
obj.foo();
46+
obj.bar();
47+
obj.baz();
48+
}
49+
50+
@pragma('vm:never-inline')
51+
@pragma('vm:testing:print-flow-graph')
52+
void testIsA1(obj) {
53+
myprint(obj is A1);
54+
myprint(obj is B1);
55+
myprint(obj is C1);
56+
}
57+
58+
abstract class A2 {
59+
void foo();
60+
void bar();
61+
void baz();
62+
}
63+
64+
@pragma('dyn-module:extendable')
65+
class B2 extends A2 {
66+
void foo() {
67+
myprint('B2.foo');
68+
}
69+
70+
@pragma('dyn-module:can-be-overridden')
71+
void bar() {
72+
myprint('B2.bar');
73+
}
74+
75+
@pragma('vm:never-inline')
76+
void baz() {
77+
myprint('B2.baz');
78+
}
79+
}
80+
81+
class C2 extends B2 {
82+
@pragma('vm:never-inline')
83+
void baz() {
84+
myprint('C2.baz');
85+
}
86+
}
87+
88+
class D2 extends B2 {}
89+
90+
@pragma('vm:never-inline')
91+
@pragma('vm:testing:print-flow-graph')
92+
void callA2(A2 obj) {
93+
obj.foo();
94+
obj.bar();
95+
obj.baz();
96+
}
97+
98+
@pragma('vm:never-inline')
99+
@pragma('vm:testing:print-flow-graph')
100+
void testIsA2(obj) {
101+
myprint(obj is A2);
102+
myprint(obj is B2);
103+
myprint(obj is C2);
104+
}
105+
106+
List objs = [Object(), B1(), C1(), B2(), C2()];
107+
108+
main() {
109+
for (final obj in objs) {
110+
testIsA1(obj);
111+
testIsA2(obj);
112+
if (obj is A1) {
113+
callA1(obj);
114+
}
115+
if (obj is A2) {
116+
callA2(obj);
117+
}
118+
}
119+
}
120+
121+
void matchIL$callA1(FlowGraph graph) {
122+
graph.dump();
123+
graph.match([
124+
match.block('Graph', []),
125+
match.block('Function', [
126+
'obj' << match.Parameter(index: 0),
127+
match.CheckStackOverflow(),
128+
match.MoveArgument(match.any),
129+
match.StaticCall(match.any),
130+
match.MoveArgument(match.any),
131+
match.StaticCall(match.any),
132+
'cid' << match.LoadClassId('obj'),
133+
match.MoveArgument('obj'),
134+
match.DispatchTableCall('cid'),
135+
match.DartReturn(match.any),
136+
]),
137+
]);
138+
}
139+
140+
void matchIL$callA2(FlowGraph graph) {
141+
graph.dump();
142+
graph.match([
143+
match.block('Graph', []),
144+
match.block('Function', [
145+
'obj' << match.Parameter(index: 0),
146+
match.CheckStackOverflow(),
147+
match.MoveArgument(match.any),
148+
match.StaticCall(match.any),
149+
'cid1' << match.LoadClassId('obj'),
150+
match.Branch(match.TestRange('cid1'), ifTrue: 'B7', ifFalse: 'B8'),
151+
]),
152+
'B7' <<
153+
match.block('Target', [
154+
match.MoveArgument('obj'),
155+
match.DispatchTableCall('cid1'),
156+
match.Goto('B9'),
157+
]),
158+
'B8' <<
159+
match.block('Target', [
160+
match.MoveArgument('obj'),
161+
match.InstanceCall('obj'),
162+
match.Goto('B9'),
163+
]),
164+
'B9' <<
165+
match.block('Join', [
166+
'cid2' << match.LoadClassId('obj'),
167+
match.Branch(match.TestRange('cid2'), ifTrue: 'B10', ifFalse: 'B11'),
168+
]),
169+
'B10' <<
170+
match.block('Target', [
171+
match.MoveArgument('obj'),
172+
match.DispatchTableCall('cid2'),
173+
match.Goto('B12'),
174+
]),
175+
'B11' <<
176+
match.block('Target', [
177+
match.MoveArgument('obj'),
178+
match.InstanceCall('obj'),
179+
match.Goto('B12'),
180+
]),
181+
'B12' <<
182+
match.block('Join', [
183+
match.DartReturn(match.any),
184+
]),
185+
]);
186+
}
187+
188+
void matchIL$testIsA1(FlowGraph graph) {
189+
graph.dump();
190+
graph.match([
191+
match.block('Graph', []),
192+
match.block('Function', [
193+
'obj' << match.Parameter(index: 0),
194+
match.CheckStackOverflow(),
195+
'cid' << match.LoadClassId('obj'),
196+
'test1' << match.TestRange('cid'),
197+
match.MoveArgument('test1'),
198+
match.StaticCall('test1'),
199+
match.MoveArgument('test1'),
200+
match.StaticCall('test1'),
201+
'test2' << match.EqualityCompare('cid', match.any, kind: '=='),
202+
match.MoveArgument('test2'),
203+
match.StaticCall('test2'),
204+
match.DartReturn(match.any),
205+
]),
206+
]);
207+
}
208+
209+
void matchIL$testIsA2(FlowGraph graph) {
210+
graph.dump();
211+
graph.match([
212+
match.block('Graph', []),
213+
match.block('Function', [
214+
'obj' << match.Parameter(index: 0),
215+
match.CheckStackOverflow(),
216+
'test1' << match.InstanceOf('obj', match.any, match.any),
217+
match.MoveArgument('test1'),
218+
match.StaticCall('test1'),
219+
'test2' << match.InstanceOf('obj', match.any, match.any),
220+
match.MoveArgument('test2'),
221+
match.StaticCall('test2'),
222+
'cid' << match.LoadClassId('obj'),
223+
'test3' << match.EqualityCompare('cid', match.any, kind: '=='),
224+
match.MoveArgument('test3'),
225+
match.StaticCall('test3'),
226+
match.DartReturn(match.any),
227+
]),
228+
]);
229+
}

runtime/vm/class_finalizer.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,9 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
612612
if (is_future_subtype && !cls.is_abstract()) {
613613
MarkClassCanBeFuture(zone, cls);
614614
}
615+
if (cls.is_dynamically_extendable()) {
616+
MarkClassHasDynamicallyExtendableSubtypes(zone, cls);
617+
}
615618

616619
RegisterClassInHierarchy(zone, cls);
617620
#endif // defined(DART_PRECOMPILED_RUNTIME)
@@ -678,6 +681,26 @@ void ClassFinalizer::MarkClassCanBeFuture(Zone* zone, const Class& cls) {
678681
MarkClassCanBeFuture(zone, base);
679682
}
680683
}
684+
685+
void ClassFinalizer::MarkClassHasDynamicallyExtendableSubtypes(
686+
Zone* zone,
687+
const Class& cls) {
688+
if (cls.has_dynamically_extendable_subtypes()) return;
689+
690+
cls.set_has_dynamically_extendable_subtypes(true);
691+
692+
Class& base = Class::Handle(zone, cls.SuperClass());
693+
if (!base.IsNull()) {
694+
MarkClassHasDynamicallyExtendableSubtypes(zone, base);
695+
}
696+
auto& interfaces = Array::Handle(zone, cls.interfaces());
697+
auto& type = AbstractType::Handle(zone);
698+
for (intptr_t i = 0; i < interfaces.Length(); ++i) {
699+
type ^= interfaces.At(i);
700+
base = type.type_class();
701+
MarkClassHasDynamicallyExtendableSubtypes(zone, base);
702+
}
703+
}
681704
#endif // defined(DART_PRECOMPILED_RUNTIME)
682705

683706
void ClassFinalizer::FinalizeClass(const Class& cls) {

runtime/vm/class_finalizer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class ClassFinalizer : public AllStatic {
5757

5858
// Mark [cls], its superclass and superinterfaces as can_be_future().
5959
static void MarkClassCanBeFuture(Zone* zone, const Class& cls);
60+
61+
// Mark [cls] and all its superclasses and superinterfaces as
62+
// has_dynamically_extendable_subtypes().
63+
static void MarkClassHasDynamicallyExtendableSubtypes(Zone* zone,
64+
const Class& cls);
6065
#endif // !defined(DART_PRECOMPILED_RUNTIME)
6166

6267
// Ensures members of the class are loaded, class layout is finalized and size

0 commit comments

Comments
 (0)