Skip to content

Commit 13ec074

Browse files
liamappelbeCommit Queue
authored and
Commit Queue
committed
[vm] Async FFI callbacks
More details about the design: https://docs.google.com/document/d/1QDjyY_6wOTOgURwpeYMKU9qEz0gKxx2MUrdruC6Kp6c/edit?usp=sharing Change-Id: Ie3985d86dca7f5010044ca46c33ca177588c0f69 Bug: #37022 CoreLibraryReviewExempt: Reviewed by vm and api groups. web and wasm groups not affected because FFI isn't on those platforms. TEST=async_void_function_callbacks_test.dart, ffi_callback_metadata_test.cc, other front end tests Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/305900 Commit-Queue: Liam Appelbe <[email protected]> Reviewed-by: Alexander Markov <[email protected]> Reviewed-by: Lasse Nielsen <[email protected]> Reviewed-by: Daco Harkes <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent d264cee commit 13ec074

File tree

86 files changed

+24066
-1467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+24066
-1467
lines changed

pkg/analyzer/lib/src/dart/error/ffi_code.g.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,8 @@ class FfiCode extends AnalyzerErrorCode {
320320
/// 0: the return type that should be 'void'.
321321
static const FfiCode MUST_RETURN_VOID = FfiCode(
322322
'MUST_RETURN_VOID',
323-
"The return type of the function passed to 'RawVoidCallback' must be "
324-
"'void' rather than '{0}'.",
323+
"The return type of the function passed to 'NativeCallable.listener' must "
324+
"be 'void' rather than '{0}'.",
325325
correctionMessage: "Try changing the return type to 'void'.",
326326
);
327327

pkg/analyzer/lib/src/generated/ffi_verifier.dart

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
3030
static const _dartFfiLibraryName = 'dart.ffi';
3131
static const _finalizableClassName = 'Finalizable';
3232
static const _isLeafParamName = 'isLeaf';
33-
static const _rawVoidCallback = 'RawVoidCallback';
33+
static const _nativeCallable = 'NativeCallable';
3434
static const _opaqueClassName = 'Opaque';
3535

3636
static const Set<String> _primitiveIntegerNativeTypesFixedSize = {
@@ -228,8 +228,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
228228
FfiCode.CREATION_OF_STRUCT_OR_UNION,
229229
node.constructorName,
230230
);
231-
} else if (class_.isRawVoidCallback) {
232-
_validateRawVoidCallback(node);
231+
} else if (class_.isNativeCallable) {
232+
_validateNativeCallable(node);
233233
}
234234

235235
super.visitInstanceCreationExpression(node);
@@ -1143,6 +1143,37 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
11431143
node.argumentList.arguments, S, typeArguments[0]);
11441144
}
11451145

1146+
/// Validate the invocation of the constructor `NativeCallable.listener(f)`.
1147+
void _validateNativeCallable(InstanceCreationExpression node) {
1148+
var argCount = node.argumentList.arguments.length;
1149+
if (argCount != 1) {
1150+
// There are other diagnostics reported against the invocation and the
1151+
// diagnostics generated below might be inaccurate, so don't report them.
1152+
return;
1153+
}
1154+
1155+
var typeArg = (node.staticType as ParameterizedType).typeArguments[0];
1156+
if (!_isValidFfiNativeFunctionType(typeArg)) {
1157+
_errorReporter.reportErrorForNode(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
1158+
node.constructorName, [typeArg, _nativeCallable]);
1159+
return;
1160+
}
1161+
1162+
var f = node.argumentList.arguments[0];
1163+
var funcType = f.typeOrThrow;
1164+
if (!_validateCompatibleFunctionTypes(funcType, typeArg)) {
1165+
_errorReporter.reportErrorForNode(
1166+
FfiCode.MUST_BE_A_SUBTYPE, f, [funcType, typeArg, _nativeCallable]);
1167+
return;
1168+
}
1169+
1170+
// TODO(brianwilkerson) Validate that `f` is a top-level function.
1171+
var retType = (funcType as FunctionType).returnType;
1172+
if (retType is! VoidType) {
1173+
_errorReporter.reportErrorForNode(FfiCode.MUST_RETURN_VOID, f, [retType]);
1174+
}
1175+
}
1176+
11461177
/// Validate that none of the [annotations] are from `dart:ffi`.
11471178
void _validateNoAnnotations(NodeList<Annotation> annotations) {
11481179
for (Annotation annotation in annotations) {
@@ -1184,37 +1215,6 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
11841215
}
11851216
}
11861217

1187-
/// Validate the invocation of the static method `RawVoidCallback<T>(f)`.
1188-
void _validateRawVoidCallback(InstanceCreationExpression node) {
1189-
var argCount = node.argumentList.arguments.length;
1190-
if (argCount != 1) {
1191-
// There are other diagnostics reported against the invocation and the
1192-
// diagnostics generated below might be inaccurate, so don't report them.
1193-
return;
1194-
}
1195-
1196-
var typeArg = (node.staticType as ParameterizedType).typeArguments[0];
1197-
if (!_isValidFfiNativeFunctionType(typeArg)) {
1198-
_errorReporter.reportErrorForNode(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
1199-
node.constructorName, [typeArg, _rawVoidCallback]);
1200-
return;
1201-
}
1202-
1203-
var f = node.argumentList.arguments[0];
1204-
var funcType = f.typeOrThrow;
1205-
if (!_validateCompatibleFunctionTypes(funcType, typeArg)) {
1206-
_errorReporter.reportErrorForNode(
1207-
FfiCode.MUST_BE_A_SUBTYPE, f, [funcType, typeArg, _rawVoidCallback]);
1208-
return;
1209-
}
1210-
1211-
// TODO(brianwilkerson) Validate that `f` is a top-level function.
1212-
var retType = (funcType as FunctionType).returnType;
1213-
if (retType is! VoidType) {
1214-
_errorReporter.reportErrorForNode(FfiCode.MUST_RETURN_VOID, f, [retType]);
1215-
}
1216-
}
1217-
12181218
void _validateRefIndexed(IndexExpression node) {
12191219
var targetType = node.realTarget.staticType;
12201220
if (!_isValidFfiNativeType(targetType,
@@ -1472,6 +1472,14 @@ extension on Element? {
14721472
element.isFfiExtension;
14731473
}
14741474

1475+
/// Return `true` if this represents the class `NativeCallable`.
1476+
bool get isNativeCallable {
1477+
final element = this;
1478+
return element is ClassElement &&
1479+
element.name == FfiVerifier._nativeCallable &&
1480+
element.isFfiClass;
1481+
}
1482+
14751483
bool get isNativeFunctionPointerExtension {
14761484
final element = this;
14771485
return element is ExtensionElement &&
@@ -1501,14 +1509,6 @@ extension on Element? {
15011509
element.isFfiClass;
15021510
}
15031511

1504-
/// Return `true` if this represents the class `RawVoidCallback`.
1505-
bool get isRawVoidCallback {
1506-
final element = this;
1507-
return element is ClassElement &&
1508-
element.name == FfiVerifier._rawVoidCallback &&
1509-
element.isFfiClass;
1510-
}
1511-
15121512
/// Return `true` if this represents the class `Struct`.
15131513
bool get isStruct {
15141514
final element = this;

pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,10 +775,10 @@ final class Pointer<T extends NativeType> extends NativeType {
775775
776776
final Pointer<Never> nullptr = Pointer.fromAddress(0);
777777
778-
class RawVoidCallback<T extends Function> {
779-
RawVoidCallback(@DartRepresentationOf('T') Function callback) {}
778+
class NativeCallable<T extends Function> {
779+
NativeCallable.listener(@DartRepresentationOf('T') Function callback) {}
780780
781-
Pointer<NativeFunction<T>> get pointer;
781+
Pointer<NativeFunction<T>> get nativeFunction;
782782
783783
void close();
784784
}

pkg/analyzer/messages.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19016,7 +19016,7 @@ FfiCode:
1901619016
}
1901719017
```
1901819018
MUST_RETURN_VOID:
19019-
problemMessage: "The return type of the function passed to 'RawVoidCallback' must be 'void' rather than '{0}'."
19019+
problemMessage: "The return type of the function passed to 'NativeCallable.listener' must be 'void' rather than '{0}'."
1902019020
correctionMessage: "Try changing the return type to 'void'."
1902119021
comment: |-
1902219022
Parameters:
@@ -19026,12 +19026,12 @@ FfiCode:
1902619026
#### Description
1902719027

1902819028
The analyzer produces this diagnostic when you pass a function
19029-
that doesn't return `void` to the `RawVoidCallback` constructor.
19029+
that doesn't return `void` to the `NativeCallable.listener` constructor.
1903019030

19031-
`RawVoidCallback` creates a native callback that can be invoked
19032-
from any thread. The native code that invokes the callback sends a message
19033-
back to the isolate that created the callback, and doesn't wait for a
19034-
response. So it isn't possible to return a result from the callback.
19031+
`NativeCallable.listener` creates a native callable that can be invoked
19032+
from any thread. The native code that invokes the callable sends a message
19033+
back to the isolate that created the callable, and doesn't wait for a
19034+
response. So it isn't possible to return a result from the callable.
1903519035

1903619036
For more information about FFI, see [C interop using dart:ffi][ffi].
1903719037

@@ -19046,7 +19046,7 @@ FfiCode:
1904619046
int f(int i) => i * 2;
1904719047

1904819048
void g() {
19049-
RawVoidCallback<Int32 Function(Int32)>([!f!]);
19049+
NativeCallable<Int32 Function(Int32)>.listener([!f!]);
1905019050
}
1905119051
```
1905219052

@@ -19060,7 +19060,7 @@ FfiCode:
1906019060
void f(int i) => print(i * 2);
1906119061

1906219062
void g() {
19063-
RawVoidCallback<Void Function(Int32)>(f);
19063+
NativeCallable<Void Function(Int32)>.listener(f);
1906419064
}
1906519065
```
1906619066
NON_CONSTANT_TYPE_ARGUMENT:

pkg/analyzer/test/src/diagnostics/ffi_async_callback_test.dart

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,78 +9,79 @@ import '../dart/resolution/context_collection_resolution.dart';
99

1010
main() {
1111
defineReflectiveSuite(() {
12-
defineReflectiveTests(FfiRawVoidCallbacksMustReturnVoid);
12+
defineReflectiveTests(FfiNativeCallableListenersMustReturnVoid);
1313
});
1414
}
1515

1616
@reflectiveTest
17-
class FfiRawVoidCallbacksMustReturnVoid extends PubPackageResolutionTest {
18-
test_RawVoidCallback_inferred() async {
17+
class FfiNativeCallableListenersMustReturnVoid
18+
extends PubPackageResolutionTest {
19+
test_NativeCallable_listener_inferred() async {
1920
await assertErrorsInCode(r'''
2021
import 'dart:ffi';
2122
void f(int i) => i * 2;
2223
void g() {
23-
RawVoidCallback<Void Function(Int32)>? callback;
24-
callback = RawVoidCallback(f);
24+
NativeCallable<Void Function(Int32)>? callback;
25+
callback = NativeCallable.listener(f);
2526
callback.close();
2627
}
2728
''', []);
2829
}
2930

30-
test_RawVoidCallback_mustBeANativeFunctionType() async {
31+
test_NativeCallable_listener_mustBeANativeFunctionType() async {
3132
await assertErrorsInCode(r'''
3233
import 'dart:ffi';
3334
void f(int i) => i * 2;
3435
void g() {
35-
RawVoidCallback<void Function(int)>(f);
36+
NativeCallable<void Function(int)>.listener(f);
3637
}
3738
''', [
38-
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 56, 35),
39+
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 56, 43),
3940
]);
4041
}
4142

42-
test_RawVoidCallback_mustBeASubtype() async {
43+
test_NativeCallable_listener_mustBeASubtype() async {
4344
await assertErrorsInCode(r'''
4445
import 'dart:ffi';
4546
void f(int i) => i * 2;
4647
void g() {
47-
RawVoidCallback<Void Function(Double)>(f);
48+
NativeCallable<Void Function(Double)>.listener(f);
4849
}
4950
''', [
50-
error(FfiCode.MUST_BE_A_SUBTYPE, 95, 1),
51+
error(FfiCode.MUST_BE_A_SUBTYPE, 103, 1),
5152
]);
5253
}
5354

54-
test_RawVoidCallback_mustHaveTypeArgs() async {
55+
test_NativeCallable_listener_mustHaveTypeArgs() async {
5556
await assertErrorsInCode(r'''
5657
import 'dart:ffi';
5758
int f(int i) => i * 2;
5859
void g() {
59-
RawVoidCallback(f);
60+
NativeCallable.listener(f);
6061
}
6162
''', [
62-
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 55, 15),
63+
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 55, 23),
6364
]);
6465
}
6566

66-
test_RawVoidCallback_mustReturnVoid() async {
67+
test_NativeCallable_listener_mustReturnVoid() async {
6768
await assertErrorsInCode(r'''
6869
import 'dart:ffi';
6970
int f(int i) => i * 2;
7071
void g() {
71-
RawVoidCallback<Int32 Function(Int32)>(f);
72+
NativeCallable<Int32 Function(Int32)>.listener(f);
7273
}
7374
''', [
74-
error(FfiCode.MUST_RETURN_VOID, 94, 1),
75+
error(FfiCode.MUST_RETURN_VOID, 102, 1),
7576
]);
7677
}
7778

78-
test_RawVoidCallback_ok() async {
79+
test_NativeCallable_listener_ok() async {
7980
await assertErrorsInCode(r'''
8081
import 'dart:ffi';
8182
void f(int i) => i * 2;
8283
void g() {
83-
RawVoidCallback<Void Function(Int32)>(f);
84+
NativeCallable<Void Function(Int32)>.listener(f);
8485
}
8586
''', []);
8687
}

pkg/analyzer/tool/diagnostics/diagnostics.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13437,18 +13437,18 @@ class B extends A {
1343713437

1343813438
### must_return_void
1343913439

13440-
_The return type of the function passed to 'RawVoidCallback' must be 'void'
13441-
rather than '{0}'._
13440+
_The return type of the function passed to 'NativeCallable.listener' must be
13441+
'void' rather than '{0}'._
1344213442

1344313443
#### Description
1344413444

1344513445
The analyzer produces this diagnostic when you pass a function
13446-
that doesn't return `void` to the `RawVoidCallback` constructor.
13446+
that doesn't return `void` to the `NativeCallable.listener` constructor.
1344713447

13448-
`RawVoidCallback` creates a native callback that can be invoked
13449-
from any thread. The native code that invokes the callback sends a message
13450-
back to the isolate that created the callback, and doesn't wait for a
13451-
response. So it isn't possible to return a result from the callback.
13448+
`NativeCallable.listener` creates a native callable that can be invoked
13449+
from any thread. The native code that invokes the callable sends a message
13450+
back to the isolate that created the callable, and doesn't wait for a
13451+
response. So it isn't possible to return a result from the callable.
1345213452

1345313453
For more information about FFI, see [C interop using dart:ffi][ffi].
1345413454

@@ -13463,7 +13463,7 @@ import 'dart:ffi';
1346313463
int f(int i) => i * 2;
1346413464

1346513465
void g() {
13466-
RawVoidCallback<Int32 Function(Int32)>([!f!]);
13466+
NativeCallable<Int32 Function(Int32)>.listener([!f!]);
1346713467
}
1346813468
{% endprettify %}
1346913469

@@ -13477,7 +13477,7 @@ import 'dart:ffi';
1347713477
void f(int i) => print(i * 2);
1347813478

1347913479
void g() {
13480-
RawVoidCallback<Void Function(Int32)>(f);
13480+
NativeCallable<Void Function(Int32)>.listener(f);
1348113481
}
1348213482
{% endprettify %}
1348313483

pkg/dart2wasm/lib/ffi_native_transformer.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ void transformLibraries(
4444
'dart:_internal',
4545
'dart:typed_data',
4646
'dart:nativewrappers',
47-
'dart:_wasm'
47+
'dart:_wasm',
48+
'dart:isolate',
4849
]);
4950
final transformer = WasmFfiNativeTransformer(
5051
index, coreTypes, hierarchy, diagnosticReporter, referenceFromIndex);

pkg/front_end/lib/src/api_unstable/vm.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export '../fasta/fasta_codes.dart'
7878
noLength,
7979
templateCantHaveNamedParameters,
8080
templateCantHaveOptionalParameters,
81+
templateFfiNativeCallableListenerReturnVoid,
8182
templateFfiCompoundImplementsFinalizable,
8283
templateFfiDartTypeMismatch,
8384
templateFfiEmptyStruct,

pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,34 @@ Message _withArgumentsFfiExpectedNoExceptionalReturn(
11141114
arguments: {'type': _type});
11151115
}
11161116

1117+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1118+
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
1119+
templateFfiNativeCallableListenerReturnVoid = const Template<
1120+
Message Function(DartType _type, bool isNonNullableByDefault)>(
1121+
problemMessageTemplate:
1122+
r"""The return type of the function passed to NativeCallable.listener must be void rather than '#type'.""",
1123+
withArguments: _withArgumentsFfiNativeCallableListenerReturnVoid);
1124+
1125+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1126+
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>
1127+
codeFfiNativeCallableListenerReturnVoid =
1128+
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
1129+
"FfiNativeCallableListenerReturnVoid",
1130+
);
1131+
1132+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1133+
Message _withArgumentsFfiNativeCallableListenerReturnVoid(
1134+
DartType _type, bool isNonNullableByDefault) {
1135+
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
1136+
List<Object> typeParts = labeler.labelType(_type);
1137+
String type = typeParts.join();
1138+
return new Message(codeFfiNativeCallableListenerReturnVoid,
1139+
problemMessage:
1140+
"""The return type of the function passed to NativeCallable.listener must be void rather than '${type}'.""" +
1141+
labeler.originMessages,
1142+
arguments: {'type': _type});
1143+
}
1144+
11171145
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
11181146
const Template<
11191147
Message Function(

pkg/front_end/messages.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ FfiNativeUnexpectedNumberOfParametersWithReceiver/analyzerCode: Fail
401401
FfiNotStatic/analyzerCode: Fail
402402
FfiPackedAnnotation/analyzerCode: Fail
403403
FfiPackedAnnotationAlignment/analyzerCode: Fail
404+
FfiNativeCallableListenerReturnVoid/analyzerCode: Fail
404405
FfiSizeAnnotation/analyzerCode: Fail
405406
FfiSizeAnnotationDimensions/analyzerCode: Fail
406407
FfiStructAnnotation/analyzerCode: Fail

0 commit comments

Comments
 (0)