Skip to content

Commit 1ac77f5

Browse files
sstricklCommit Queue
authored and
Commit Queue
committed
[vm] Align entry point verification and the precompiler.
For entry-point pragma annotations, most of the time they are used with either no argument or with an argument that evaluates to either * false to denote the annotation should not take effect, or * null or true to denote the annotation should take effect. However, the user can also specify that only part of the operations on a member should be accessed from native code by using a string argument that is either 'call', 'set', or 'get'. The entry point verification in Invoke/InvokeGetter/InvokeSetter assumes that for getters and setters, the only valid string argument is 'get' or 'set', respectively. This is because those methods are called via `Dart_GetField`[0] and `Dart_SetField`, respectively, as if they were the getter or setter of a defined field. However, the precompiler previously assumed that the string argument 'call' was the only string argument that meant the link to a function's code object should be saved. Similarly, it assumed the string argument 'get' for functions meant that their implicit closure function should be saved, which ends up including getters. Furthermore, it did not do anything with setters annotated with the string argument 'set'. This means that the code link would not be saved for getters or setters that were annotated with the string argument expected by the entry point verifier. This CL aligns the precompiler to match the expectations of other parts of the codebase. It also changes TFA to report an error if a getter or setter is marked with the string argument 'call'. [0] `Dart_Invoke` can be called with the name of a getter that returns a closure, but doing so is semantically equivalent to calling `Dart_GetField` followed by `Dart_InvokeClosure`. TEST=vm/dart/entrypoint_verification_test Fixes: #59920 Change-Id: Ia2768bbaf9058bb14a1cdfb331eb85fa082a0e90 Cq-Include-Trybots: luci.dart.try:vm-aot-dwarf-linux-product-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-product-x64-try,vm-aot-mac-product-arm64-try,vm-aot-obfuscate-linux-release-x64-try,vm-linux-debug-x64-try,vm-linux-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404823 Reviewed-by: Alexander Markov <[email protected]> Commit-Queue: Tess Strickland <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent 862ca5b commit 1ac77f5

File tree

7 files changed

+378
-138
lines changed

7 files changed

+378
-138
lines changed

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

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,19 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
7676
return types ?? const [];
7777
}
7878

79+
static const _referenceToDocumentation =
80+
"See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/"
81+
"aot/entry_point_pragma.md.";
82+
7983
@override
8084
visitLibrary(Library library) {
8185
for (final type in entryPointTypesFromPragmas(library.annotations)) {
8286
if (type == PragmaEntryPointType.Default) {
8387
nativeCodeOracle.addLibraryReferencedFromNativeCode(library);
8488
} else {
85-
throw "Error: pragma entry-point definition on a library must evaluate "
86-
"to null. See entry_points_pragma.md.";
89+
throw "Error: The argument to an entry-point pragma annotation "
90+
"on a library must evaluate to null, true, or false.\n"
91+
"$_referenceToDocumentation";
8792
}
8893
}
8994
library.visitChildren(this);
@@ -101,8 +106,9 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
101106
entryPoints.addDynamicallyExtendableClass(klass);
102107
nativeCodeOracle.addClassReferencedFromNativeCode(klass);
103108
} else {
104-
throw "Error: pragma entry-point definition on a class must evaluate "
105-
"to null, true or false. See entry_points_pragma.md.";
109+
throw "Error: The argument to an entry-point pragma annotation "
110+
"on a class must evaluate to null, true, or false.\n"
111+
"$_referenceToDocumentation";
106112
}
107113
}
108114
klass.visitChildren(this);
@@ -119,34 +125,49 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
119125
: new DirectSelector(proc, callKind: ck));
120126
}
121127

122-
final defaultCallKind = proc.isGetter
123-
? CallKind.PropertyGet
124-
: (proc.isSetter ? CallKind.PropertySet : CallKind.Method);
125-
126128
for (final type in types) {
127129
switch (type) {
128130
case PragmaEntryPointType.CallOnly:
129-
addSelector(defaultCallKind);
131+
if (proc.isGetter) {
132+
throw "Error: The argument to an entry-point pragma annotation on "
133+
"a getter ($proc) must evaluate to null, true, false, or "
134+
"'get'.\n$_referenceToDocumentation";
135+
}
136+
if (proc.isSetter) {
137+
throw "Error: The argument to an entry-point pragma annotation on "
138+
"a setter ($proc) must evaluate to null, true, false, or "
139+
"'set'.\n$_referenceToDocumentation";
140+
}
141+
addSelector(CallKind.Method);
130142
break;
131143
case PragmaEntryPointType.SetterOnly:
132144
if (!proc.isSetter) {
133-
throw "Error: cannot generate a setter for a method or getter ($proc).";
145+
throw "Error: cannot generate a setter for a method or getter "
146+
"($proc).\n$_referenceToDocumentation";
134147
}
135148
addSelector(CallKind.PropertySet);
136149
break;
137150
case PragmaEntryPointType.GetterOnly:
138151
if (proc.isSetter) {
139-
throw "Error: cannot closurize a setter ($proc).";
152+
throw "Error: cannot closurize a setter ($proc).\n"
153+
"$_referenceToDocumentation";
140154
}
141155
if (proc.isFactory) {
142-
throw "Error: cannot closurize a factory ($proc).";
156+
throw "Error: cannot closurize a factory ($proc).\n"
157+
"$_referenceToDocumentation";
143158
}
144159
addSelector(CallKind.PropertyGet);
145160
break;
146161
case PragmaEntryPointType.Default:
147-
addSelector(defaultCallKind);
148-
if (!proc.isSetter && !proc.isGetter && !proc.isFactory) {
162+
if (proc.isGetter) {
149163
addSelector(CallKind.PropertyGet);
164+
} else if (proc.isSetter) {
165+
addSelector(CallKind.PropertySet);
166+
} else {
167+
addSelector(CallKind.Method);
168+
if (!proc.isFactory) {
169+
addSelector(CallKind.PropertyGet);
170+
}
150171
}
151172
break;
152173
case PragmaEntryPointType.Extendable:
@@ -165,8 +186,9 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
165186
for (final type in entryPointTypesFromPragmas(ctor.annotations)) {
166187
if (type != PragmaEntryPointType.Default &&
167188
type != PragmaEntryPointType.CallOnly) {
168-
throw "Error: pragma entry-point definition on a constructor ($ctor) must"
169-
"evaluate to null, true, false or 'call'. See entry_points_pragma.md.";
189+
throw "Error: The argument to an entry-point pragma annotation on a "
190+
"constructor ($ctor) must evaluate to null, true, false or "
191+
"'call'.\n$_referenceToDocumentation";
170192
}
171193
entryPoints
172194
.addRawCall(new DirectSelector(ctor, callKind: CallKind.Method));
@@ -192,21 +214,22 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
192214
addSelector(CallKind.PropertyGet);
193215
break;
194216
case PragmaEntryPointType.SetterOnly:
195-
if (field.isFinal) {
196-
throw "Error: can't use 'set' in entry-point pragma for final field "
197-
"$field";
217+
if (!field.hasSetter) {
218+
throw "Error: can't use 'set' in an entry-point pragma annotation "
219+
"for a field that has no setter ($field).\n"
220+
"$_referenceToDocumentation";
198221
}
199222
addSelector(CallKind.PropertySet);
200223
break;
201224
case PragmaEntryPointType.Default:
202225
addSelector(CallKind.PropertyGet);
203-
if (!field.isFinal) {
226+
if (field.hasSetter) {
204227
addSelector(CallKind.PropertySet);
205228
}
206229
break;
207230
case PragmaEntryPointType.CallOnly:
208-
throw "Error: can't generate invocation dispatcher for field $field"
209-
"through @pragma('vm:entry-point')";
231+
throw "Error: 'call' is not a valid entry-point pragma annotation "
232+
"argument for the field $field.\n$_referenceToDocumentation";
210233
case PragmaEntryPointType.Extendable:
211234
throw "Error: only class can be extendable";
212235
case PragmaEntryPointType.CanBeOverridden:

runtime/bin/entrypoints_verification_test.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,21 @@ bool IsTreeShaken(const char* name, Dart_Handle handle, const char* error) {
168168
Dart_Null())); \
169169
} while (false)
170170

171+
#define TEST_GETTERS(target) \
172+
do { \
173+
FAIL("get1", Dart_GetField(target, Dart_NewStringFromCString("get1"))); \
174+
CHECK(Dart_GetField(target, Dart_NewStringFromCString("get2"))); \
175+
CHECK(Dart_GetField(target, Dart_NewStringFromCString("get3"))); \
176+
} while (false)
177+
178+
#define TEST_SETTERS(target, value) \
179+
do { \
180+
FAIL("set1", \
181+
Dart_SetField(target, Dart_NewStringFromCString("set1"), value)); \
182+
CHECK(Dart_SetField(target, Dart_NewStringFromCString("set2"), value)); \
183+
CHECK(Dart_SetField(target, Dart_NewStringFromCString("set3"), value)); \
184+
} while (false)
185+
171186
DART_EXPORT void RunTests() {
172187
is_dartaotruntime = Dart_IsPrecompiledRuntime();
173188

@@ -261,4 +276,28 @@ DART_EXPORT void RunTests() {
261276

262277
fprintf(stderr, "\n\nTesting fields with instance target\n\n\n");
263278
TEST_FIELDS(D);
279+
280+
//////// Test actions against getter and setter functions.
281+
282+
fprintf(stderr, "\n\nTesting getters with library target\n\n\n");
283+
TEST_GETTERS(lib);
284+
285+
fprintf(stderr, "\n\nTesting getters with class target\n\n\n");
286+
TEST_GETTERS(F_class);
287+
288+
fprintf(stderr, "\n\nTesting getters with instance target\n\n\n");
289+
TEST_GETTERS(D);
290+
291+
Dart_Handle test_value =
292+
Dart_GetField(lib, Dart_NewStringFromCString("testValue"));
293+
CHECK(test_value);
294+
295+
fprintf(stderr, "\n\nTesting setters with library target\n\n\n");
296+
TEST_SETTERS(lib, test_value);
297+
298+
fprintf(stderr, "\n\nTesting setters with class target\n\n\n");
299+
TEST_SETTERS(F_class, test_value);
300+
301+
fprintf(stderr, "\n\nTesting setters with instance target\n\n\n");
302+
TEST_SETTERS(D, test_value);
264303
}

runtime/docs/compiler/aot/entry_point_pragma.md

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,92 @@ Note that `@pragma("vm:entry-point")` may be added to abstract classes -- in
5050
this case, their name will survive obfuscation, but they won't have any
5151
allocation stubs.
5252

53-
### Procedures
53+
### Getters
5454

55-
Any one of the following forms may be attached to a procedure (including
56-
getters, setters and constructors):
55+
Any one of the following forms may be attached to getters:
5756

5857
```dart
5958
@pragma("vm:entry-point")
6059
@pragma("vm:entry-point", true/false)
6160
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
6261
@pragma("vm:entry-point", "get")
62+
void get foo { ... }
63+
```
64+
65+
The `"get"` annotation allows retrieval of the getter value via
66+
`Dart_GetField`. `Dart_Invoke` can only be used with getters that return a
67+
closure value, in which case it is the same as retrieving the closure via
68+
`Dart_GetField` and then invoking the closure using `Dart_InvokeClosure`, so
69+
the "get" annotation is also needed for such uses.
70+
71+
If the second parameter is missing, `null` or `true`, it behaves the same
72+
as the `"get"` parameter.
73+
74+
Getters cannot be closurized.
75+
76+
### Setters
77+
78+
Any one of the following forms may be attached to setters:
79+
80+
```dart
81+
@pragma("vm:entry-point")
82+
@pragma("vm:entry-point", true/false)
83+
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
84+
@pragma("vm:entry-point", "set")
85+
void set foo(int value) { ... }
86+
```
87+
88+
The `"set"` annotation allows setting the value via `Dart_SetField`.
89+
90+
If the second parameter is missing, `null` or `true`, it behaves the same
91+
as the `"set"` parameter.
92+
93+
Setters cannot be closurized.
94+
95+
### Constructors
96+
97+
Any one of the following forms may be attached to constructors:
98+
99+
```dart
100+
@pragma("vm:entry-point")
101+
@pragma("vm:entry-point", true/false)
102+
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
63103
@pragma("vm:entry-point", "call")
64-
void foo() { ... }
104+
C(this.foo) { ... }
65105
```
66106

67-
If the second parameter is missing, `null` or `true`, the procedure (and its
68-
closurized form, excluding constructors and setters) will available for lookup
69-
and invocation directly from native or VM code.
107+
If the annotation is `"call"`, then the procedure is available for invocation
108+
(access via `Dart_Invoke`).
109+
110+
If the second parameter is missing, `null` or `true`, it behaves the same
111+
as the `"call"` parameter.
112+
113+
If the constructor is _generative_, the enclosing class must also be annotated
114+
for allocation from native or VM code.
115+
116+
Constructors cannot be closurized.
117+
118+
### Other Procedures
119+
120+
Any one of the following forms may be attached to other types of procedures:
121+
122+
```dart
123+
@pragma("vm:entry-point")
124+
@pragma("vm:entry-point", true/false)
125+
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
126+
@pragma("vm:entry-point", "get")
127+
@pragma("vm:entry-point", "call")
128+
void foo(int value) { ... }
129+
```
70130

71-
If the procedure is a *generative* constructor, the enclosing class must also be
72-
annotated for allocation from native or VM code.
131+
If the annotation is `"get"`, then the procedure is only available for
132+
closurization (access via `Dart_GetField`).
73133

74-
If the annotation is "get" or "call", the procedure will only be available for
75-
closurization (access via `Dart_GetField`) or invocation (access via
76-
`Dart_Invoke`).
134+
If the annotation is `"call"`, then the procedure is only available for
135+
invocation (access via `Dart_Invoke`).
77136

78-
"@pragma("vm:entry-point", "get") against constructors or setters is disallowed
79-
since they cannot be closurized.
137+
If the second parameter is missing, `null` or `true`, the procedure is available
138+
for both closurization and invocation.
80139

81140
### Fields
82141

@@ -95,11 +154,11 @@ int foo;
95154
If the second parameter is missing, `null` or `true`, the field is marked for
96155
native access and for non-static fields the corresponding getter and setter in
97156
the interface of the enclosing class are marked for native invocation. If the
98-
"get" or "set" parameter is used, only the getter or setter is marked. For
157+
`"get"` or `"set"` parameter is used, only the getter or setter is marked. For
99158
static fields, the implicit getter is always marked if the field is marked
100159
for native access.
101160

102-
A field containing a closure may only be invoked using Dart_Invoke if the
161+
A field containing a closure may only be invoked using `Dart_Invoke` if the
103162
getter is marked, in which case it is the same as retrieving the closure from
104-
the field using Dart_GetField and then invoking the closure using
105-
Dart_InvokeClosure.
163+
the field using `Dart_GetField` and then invoking the closure using
164+
`Dart_InvokeClosure`.

0 commit comments

Comments
 (0)