mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:43:57 +00:00
[vm/ffi] Closure callbacks for sync callbacks
Bug: https://github.com/dart-lang/sdk/issues/52689 Change-Id: I54be397cfbf8519fe5b5a51b793fe46d602124d9 Fixes: https://github.com/dart-lang/sdk/issues/52689 Bug: https://github.com/dart-lang/sdk/issues/53096 TEST=isolate_local_function_callbacks_test.dart, plus generated tests and additions to existing tests CoreLibraryReviewExempt: The isolate and FFI packages are VM-only Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/317060 Commit-Queue: Liam Appelbe <liama@google.com> Reviewed-by: Liam Appelbe <liama@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
parent
33fb1e3456
commit
e8d7425c4e
|
@ -204,12 +204,13 @@ class FfiCode extends AnalyzerErrorCode {
|
|||
hasPublishedDocs: true,
|
||||
);
|
||||
|
||||
/// No parameters.
|
||||
/// Parameters:
|
||||
/// 0: the name of the method
|
||||
static const FfiCode INVALID_EXCEPTION_VALUE = FfiCode(
|
||||
'INVALID_EXCEPTION_VALUE',
|
||||
"The method 'Pointer.fromFunction' can't have an exceptional return value "
|
||||
"(the second argument) when the return type of the function is either "
|
||||
"'void', 'Handle' or 'Pointer'.",
|
||||
"The method {0} can't have an exceptional return value (the second "
|
||||
"argument) when the return type of the function is either 'void', "
|
||||
"'Handle' or 'Pointer'.",
|
||||
correctionMessage: "Try removing the exceptional return value.",
|
||||
hasPublishedDocs: true,
|
||||
);
|
||||
|
@ -264,12 +265,13 @@ class FfiCode extends AnalyzerErrorCode {
|
|||
hasPublishedDocs: true,
|
||||
);
|
||||
|
||||
/// No parameters.
|
||||
/// Parameters:
|
||||
/// 0: the name of the method
|
||||
static const FfiCode MISSING_EXCEPTION_VALUE = FfiCode(
|
||||
'MISSING_EXCEPTION_VALUE',
|
||||
"The method 'Pointer.fromFunction' must have an exceptional return value "
|
||||
"(the second argument) when the return type of the function is neither "
|
||||
"'void', 'Handle', nor 'Pointer'.",
|
||||
"The method {0} must have an exceptional return value (the second "
|
||||
"argument) when the return type of the function is neither 'void', "
|
||||
"'Handle', nor 'Pointer'.",
|
||||
correctionMessage: "Try adding an exceptional return value.",
|
||||
hasPublishedDocs: true,
|
||||
);
|
||||
|
|
|
@ -1074,12 +1074,12 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
R.isHandle ||
|
||||
R.isCompoundSubtype) {
|
||||
if (argCount != 1) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.INVALID_EXCEPTION_VALUE, node.argumentList.arguments[1]);
|
||||
_errorReporter.reportErrorForNode(FfiCode.INVALID_EXCEPTION_VALUE,
|
||||
node.argumentList.arguments[1], ['fromFunction']);
|
||||
}
|
||||
} else if (argCount != 2) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.MISSING_EXCEPTION_VALUE, node.methodName);
|
||||
FfiCode.MISSING_EXCEPTION_VALUE, node.methodName, ['fromFunction']);
|
||||
} else {
|
||||
Expression e = node.argumentList.arguments[1];
|
||||
var eType = e.typeOrThrow;
|
||||
|
@ -1143,10 +1143,15 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
node.argumentList.arguments, S, typeArguments[0]);
|
||||
}
|
||||
|
||||
/// Validate the invocation of the constructor `NativeCallable.listener(f)`.
|
||||
/// Validate the invocation of the constructor `NativeCallable.listener(f)`
|
||||
/// or `NativeCallable.isolateLocal(f)`.
|
||||
void _validateNativeCallable(InstanceCreationExpression node) {
|
||||
final name = node.constructorName.name?.toString() ?? '';
|
||||
final isolateLocal = name == 'isolateLocal';
|
||||
|
||||
// listener takes 1 arg, isolateLocal takes 1 or 2.
|
||||
var argCount = node.argumentList.arguments.length;
|
||||
if (argCount != 1) {
|
||||
if (!(argCount == 1 || (isolateLocal && argCount == 2))) {
|
||||
// There are other diagnostics reported against the invocation and the
|
||||
// diagnostics generated below might be inaccurate, so don't report them.
|
||||
return;
|
||||
|
@ -1167,10 +1172,38 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO(brianwilkerson) Validate that `f` is a top-level function.
|
||||
var retType = (funcType as FunctionType).returnType;
|
||||
if (retType is! VoidType) {
|
||||
_errorReporter.reportErrorForNode(FfiCode.MUST_RETURN_VOID, f, [retType]);
|
||||
var natRetType = (typeArg as FunctionType).returnType;
|
||||
if (isolateLocal) {
|
||||
if (retType is VoidType ||
|
||||
natRetType.isPointer ||
|
||||
natRetType.isHandle ||
|
||||
natRetType.isCompoundSubtype) {
|
||||
if (argCount != 1) {
|
||||
_errorReporter.reportErrorForNode(FfiCode.INVALID_EXCEPTION_VALUE,
|
||||
node.argumentList.arguments[1], [name]);
|
||||
}
|
||||
} else if (argCount != 2) {
|
||||
_errorReporter
|
||||
.reportErrorForNode(FfiCode.MISSING_EXCEPTION_VALUE, node, [name]);
|
||||
} else {
|
||||
var e = (node.argumentList.arguments[1] as NamedExpression).expression;
|
||||
var eType = e.typeOrThrow;
|
||||
if (!_validateCompatibleNativeType(eType, natRetType,
|
||||
checkCovariance: true)) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.MUST_BE_A_SUBTYPE, e, [eType, natRetType, name]);
|
||||
}
|
||||
if (!_isConst(e)) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, e, ['exceptionalReturn']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (retType is! VoidType) {
|
||||
_errorReporter
|
||||
.reportErrorForNode(FfiCode.MUST_RETURN_VOID, f, [retType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -779,7 +779,11 @@ final class Pointer<T extends NativeType> extends NativeType {
|
|||
final Pointer<Never> nullptr = Pointer.fromAddress(0);
|
||||
|
||||
class NativeCallable<T extends Function> {
|
||||
NativeCallable.listener(@DartRepresentationOf('T') Function callback) {}
|
||||
NativeCallable.isolateLocal(
|
||||
@DartRepresentationOf("T") Function callback,
|
||||
{Object? exceptionalReturn}) {}
|
||||
|
||||
NativeCallable.listener(@DartRepresentationOf("T") Function callback) {}
|
||||
|
||||
Pointer<NativeFunction<T>> get nativeFunction;
|
||||
|
||||
|
|
|
@ -18135,7 +18135,8 @@ FfiCode:
|
|||
argument whose value isn't a constant expression.
|
||||
|
||||
The analyzer also produces this diagnostic when the value of the
|
||||
`exceptionalReturn` argument of `Pointer.fromFunction`.
|
||||
`exceptionalReturn` argument of `Pointer.fromFunction` or
|
||||
`NativeCallable.isolateLocal`.
|
||||
|
||||
For more information about FFI, see [C interop using dart:ffi][ffi].
|
||||
|
||||
|
@ -18591,15 +18592,18 @@ FfiCode:
|
|||
}
|
||||
```
|
||||
INVALID_EXCEPTION_VALUE:
|
||||
problemMessage: "The method 'Pointer.fromFunction' can't have an exceptional return value (the second argument) when the return type of the function is either 'void', 'Handle' or 'Pointer'."
|
||||
problemMessage: "The method {0} can't have an exceptional return value (the second argument) when the return type of the function is either 'void', 'Handle' or 'Pointer'."
|
||||
correctionMessage: Try removing the exceptional return value.
|
||||
comment: No parameters.
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the name of the method
|
||||
hasPublishedDocs: true
|
||||
documentation: |-
|
||||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when an invocation of the method
|
||||
`Pointer.fromFunction` has a second argument (the exceptional return
|
||||
`Pointer.fromFunction` or `NativeCallable.isolateLocal`
|
||||
has a second argument (the exceptional return
|
||||
value) and the type to be returned from the invocation is either `void`,
|
||||
`Handle` or `Pointer`.
|
||||
|
||||
|
@ -18888,15 +18892,18 @@ FfiCode:
|
|||
}
|
||||
```
|
||||
MISSING_EXCEPTION_VALUE:
|
||||
problemMessage: "The method 'Pointer.fromFunction' must have an exceptional return value (the second argument) when the return type of the function is neither 'void', 'Handle', nor 'Pointer'."
|
||||
problemMessage: "The method {0} must have an exceptional return value (the second argument) when the return type of the function is neither 'void', 'Handle', nor 'Pointer'."
|
||||
correctionMessage: Try adding an exceptional return value.
|
||||
comment: No parameters.
|
||||
comment: |-
|
||||
Parameters:
|
||||
0: the name of the method
|
||||
hasPublishedDocs: true
|
||||
documentation: |-
|
||||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when an invocation of the method
|
||||
`Pointer.fromFunction` doesn't have a second argument (the exceptional
|
||||
`Pointer.fromFunction` or `NativeCallable.isolateLocal`
|
||||
doesn't have a second argument (the exceptional
|
||||
return value) when the type to be returned from the invocation is neither
|
||||
`void`, `Handle`, nor `Pointer`.
|
||||
|
||||
|
@ -19028,7 +19035,8 @@ FfiCode:
|
|||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when an invocation of either
|
||||
`Pointer.fromFunction` or `DynamicLibrary.lookupFunction` has a type
|
||||
`Pointer.fromFunction`, `DynamicLibrary.lookupFunction`,
|
||||
or a `NativeCallable` constructor, has a type
|
||||
argument(whether explicit or inferred) that isn't a native function type.
|
||||
|
||||
For more information about FFI, see [C interop using dart:ffi][ffi].
|
||||
|
@ -19079,7 +19087,8 @@ FfiCode:
|
|||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic in two cases:
|
||||
- In an invocation of `Pointer.fromFunction` where the type argument
|
||||
- In an invocation of `Pointer.fromFunction`, or a
|
||||
`NativeCallable` constructor where the type argument
|
||||
(whether explicit or inferred) isn't a supertype of the type of the
|
||||
function passed as the first argument to the method.
|
||||
- In an invocation of `DynamicLibrary.lookupFunction` where the first type
|
||||
|
|
|
@ -16,6 +16,123 @@ main() {
|
|||
@reflectiveTest
|
||||
class FfiNativeCallableListenersMustReturnVoid
|
||||
extends PubPackageResolutionTest {
|
||||
test_NativeCallable_isolateLocal_argumentMustBeAConstant() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
int e = 123;
|
||||
NativeCallable<Int32 Function(Int32)>.isolateLocal(f, exceptionalReturn: e);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 143, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_exceptionMustBeASubtype() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Int32 Function(Int32)>.isolateLocal(f, exceptionalReturn: '?');
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.MUST_BE_A_SUBTYPE, 128, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_inferred() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Int32 Function(Int32)>? callback;
|
||||
callback = NativeCallable.isolateLocal(f, exceptionalReturn: 4);
|
||||
callback.close();
|
||||
}
|
||||
''', []);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_invalidExceptionValue() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
void f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Void Function(Int32)>.isolateLocal(f, exceptionalReturn: 4);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.INVALID_EXCEPTION_VALUE, 109, 20),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_missingExceptionValue() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Int32 Function(Int32)>.isolateLocal(f);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.MISSING_EXCEPTION_VALUE, 55, 53),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_mustBeANativeFunctionType() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<int Function(int)>.isolateLocal(f, exceptionalReturn: 4);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 55, 46),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_mustBeASubtype() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Int32 Function(Double)>.isolateLocal(f, exceptionalReturn: 4);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.MUST_BE_A_SUBTYPE, 107, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_mustHaveTypeArgs() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable.isolateLocal(f, exceptionalReturn: 4);
|
||||
}
|
||||
''', [
|
||||
error(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, 55, 27),
|
||||
]);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_ok() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
int f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Int32 Function(Int32)>.isolateLocal(f, exceptionalReturn: 4);
|
||||
}
|
||||
''', []);
|
||||
}
|
||||
|
||||
test_NativeCallable_isolateLocal_okVoid() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
void f(int i) => i * 2;
|
||||
void g() {
|
||||
NativeCallable<Void Function(Int32)>.isolateLocal(f);
|
||||
}
|
||||
''', []);
|
||||
}
|
||||
|
||||
test_NativeCallable_listener_inferred() async {
|
||||
await assertErrorsInCode(r'''
|
||||
import 'dart:ffi';
|
||||
|
|
|
@ -726,7 +726,8 @@ The analyzer produces this diagnostic when an invocation of either
|
|||
argument whose value isn't a constant expression.
|
||||
|
||||
The analyzer also produces this diagnostic when the value of the
|
||||
`exceptionalReturn` argument of `Pointer.fromFunction`.
|
||||
`exceptionalReturn` argument of `Pointer.fromFunction` or
|
||||
`NativeCallable.isolateLocal`.
|
||||
|
||||
For more information about FFI, see [C interop using dart:ffi][ffi].
|
||||
|
||||
|
@ -8907,13 +8908,14 @@ dependencies:
|
|||
|
||||
### invalid_exception_value
|
||||
|
||||
_The method 'Pointer.fromFunction' can't have an exceptional return value (the
|
||||
second argument) when the return type of the function is either 'void', 'Handle' or 'Pointer'._
|
||||
_The method {0} can't have an exceptional return value (the second argument)
|
||||
when the return type of the function is either 'void', 'Handle' or 'Pointer'._
|
||||
|
||||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when an invocation of the method
|
||||
`Pointer.fromFunction` has a second argument (the exceptional return
|
||||
`Pointer.fromFunction` or `NativeCallable.isolateLocal`
|
||||
has a second argument (the exceptional return
|
||||
value) and the type to be returned from the invocation is either `void`,
|
||||
`Handle` or `Pointer`.
|
||||
|
||||
|
@ -11898,13 +11900,14 @@ void f(E e) {
|
|||
|
||||
### missing_exception_value
|
||||
|
||||
_The method 'Pointer.fromFunction' must have an exceptional return value (the
|
||||
second argument) when the return type of the function is neither 'void', 'Handle', nor 'Pointer'._
|
||||
_The method {0} must have an exceptional return value (the second argument) when
|
||||
the return type of the function is neither 'void', 'Handle', nor 'Pointer'._
|
||||
|
||||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when an invocation of the method
|
||||
`Pointer.fromFunction` doesn't have a second argument (the exceptional
|
||||
`Pointer.fromFunction` or `NativeCallable.isolateLocal`
|
||||
doesn't have a second argument (the exceptional
|
||||
return value) when the type to be returned from the invocation is neither
|
||||
`void`, `Handle`, nor `Pointer`.
|
||||
|
||||
|
@ -13022,7 +13025,8 @@ _The type '{0}' given to '{1}' must be a valid 'dart:ffi' native function type._
|
|||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when an invocation of either
|
||||
`Pointer.fromFunction` or `DynamicLibrary.lookupFunction` has a type
|
||||
`Pointer.fromFunction`, `DynamicLibrary.lookupFunction`,
|
||||
or a `NativeCallable` constructor, has a type
|
||||
argument(whether explicit or inferred) that isn't a native function type.
|
||||
|
||||
For more information about FFI, see [C interop using dart:ffi][ffi].
|
||||
|
@ -13068,7 +13072,8 @@ _The type '{0}' must be a subtype of '{1}' for '{2}'._
|
|||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic in two cases:
|
||||
- In an invocation of `Pointer.fromFunction` where the type argument
|
||||
- In an invocation of `Pointer.fromFunction`, or a
|
||||
`NativeCallable` constructor where the type argument
|
||||
(whether explicit or inferred) isn't a supertype of the type of the
|
||||
function passed as the first argument to the method.
|
||||
- In an invocation of `DynamicLibrary.lookupFunction` where the first type
|
||||
|
|
|
@ -246,10 +246,11 @@ class FfiTransformer extends Transformer {
|
|||
final Procedure fromAddressInternal;
|
||||
final Procedure libraryLookupMethod;
|
||||
final Procedure abiMethod;
|
||||
final Procedure pointerFromFunctionProcedure;
|
||||
final Procedure pointerAsyncFromFunctionProcedure;
|
||||
final Procedure createNativeCallableListenerProcedure;
|
||||
final Procedure nativeCallbackFunctionProcedure;
|
||||
final Procedure nativeAsyncCallbackFunctionProcedure;
|
||||
final Procedure createNativeCallableIsolateLocalProcedure;
|
||||
final Procedure nativeIsolateLocalCallbackFunctionProcedure;
|
||||
final Map<NativeType, Procedure> loadMethods;
|
||||
final Map<NativeType, Procedure> loadUnalignedMethods;
|
||||
final Map<NativeType, Procedure> storeMethods;
|
||||
|
@ -272,8 +273,10 @@ class FfiTransformer extends Transformer {
|
|||
final Procedure checkAbiSpecificIntegerMappingFunction;
|
||||
final Class rawRecvPortClass;
|
||||
final Class nativeCallableClass;
|
||||
final Constructor nativeCallableListenerConstructor;
|
||||
final Constructor nativeCallablePrivateConstructor;
|
||||
final Procedure nativeCallableIsolateLocalConstructor;
|
||||
final Constructor nativeCallablePrivateIsolateLocalConstructor;
|
||||
final Procedure nativeCallableListenerConstructor;
|
||||
final Constructor nativeCallablePrivateListenerConstructor;
|
||||
final Field nativeCallablePortField;
|
||||
final Field nativeCallablePointerField;
|
||||
|
||||
|
@ -464,14 +467,17 @@ class FfiTransformer extends Transformer {
|
|||
libraryLookupMethod =
|
||||
index.getProcedure('dart:ffi', 'DynamicLibrary', 'lookup'),
|
||||
abiMethod = index.getTopLevelProcedure('dart:ffi', '_abi'),
|
||||
pointerFromFunctionProcedure =
|
||||
index.getTopLevelProcedure('dart:ffi', '_pointerFromFunction'),
|
||||
pointerAsyncFromFunctionProcedure =
|
||||
index.getTopLevelProcedure('dart:ffi', '_pointerAsyncFromFunction'),
|
||||
createNativeCallableListenerProcedure = index.getTopLevelProcedure(
|
||||
'dart:ffi', '_createNativeCallableListener'),
|
||||
createNativeCallableIsolateLocalProcedure = index.getTopLevelProcedure(
|
||||
'dart:ffi', '_createNativeCallableIsolateLocal'),
|
||||
nativeCallbackFunctionProcedure =
|
||||
index.getTopLevelProcedure('dart:ffi', '_nativeCallbackFunction'),
|
||||
nativeAsyncCallbackFunctionProcedure = index.getTopLevelProcedure(
|
||||
'dart:ffi', '_nativeAsyncCallbackFunction'),
|
||||
nativeIsolateLocalCallbackFunctionProcedure =
|
||||
index.getTopLevelProcedure(
|
||||
'dart:ffi', '_nativeIsolateLocalCallbackFunction'),
|
||||
nativeTypesClasses = nativeTypeClassNames.map((nativeType, name) =>
|
||||
MapEntry(nativeType, index.getClass('dart:ffi', name))),
|
||||
classNativeTypes = nativeTypeClassNames.map((nativeType, name) =>
|
||||
|
@ -529,14 +535,18 @@ class FfiTransformer extends Transformer {
|
|||
'dart:ffi', "_checkAbiSpecificIntegerMapping"),
|
||||
rawRecvPortClass = index.getClass('dart:isolate', 'RawReceivePort'),
|
||||
nativeCallableClass = index.getClass('dart:ffi', 'NativeCallable'),
|
||||
nativeCallableIsolateLocalConstructor =
|
||||
index.getProcedure('dart:ffi', 'NativeCallable', 'isolateLocal'),
|
||||
nativeCallablePrivateIsolateLocalConstructor =
|
||||
index.getConstructor('dart:ffi', '_NativeCallableIsolateLocal', ''),
|
||||
nativeCallableListenerConstructor =
|
||||
index.getConstructor('dart:ffi', 'NativeCallable', 'listener'),
|
||||
nativeCallablePrivateConstructor =
|
||||
index.getConstructor('dart:ffi', 'NativeCallable', '_'),
|
||||
index.getProcedure('dart:ffi', 'NativeCallable', 'listener'),
|
||||
nativeCallablePrivateListenerConstructor =
|
||||
index.getConstructor('dart:ffi', '_NativeCallableListener', ''),
|
||||
nativeCallablePortField =
|
||||
index.getField('dart:ffi', 'NativeCallable', '_port'),
|
||||
index.getField('dart:ffi', '_NativeCallableListener', '_port'),
|
||||
nativeCallablePointerField =
|
||||
index.getField('dart:ffi', 'NativeCallable', '_pointer') {
|
||||
index.getField('dart:ffi', '_NativeCallableBase', '_pointer') {
|
||||
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
|
||||
coreTypes, Nullability.nonNullable);
|
||||
voidType = nativeTypesClasses[NativeType.kVoid]!
|
||||
|
|
|
@ -134,43 +134,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
target.name != Name("#fromTypedDataBase")) {
|
||||
diagnosticReporter.report(messageFfiCreateOfStructOrUnion,
|
||||
node.fileOffset, 1, node.location?.file);
|
||||
} else if (target == nativeCallableListenerConstructor) {
|
||||
try {
|
||||
final DartType nativeType = InterfaceType(nativeFunctionClass,
|
||||
currentLibrary.nonNullable, [node.arguments.types[0]]);
|
||||
final Expression func = node.arguments.positional[0];
|
||||
final DartType dartType = func.getStaticType(staticTypeContext!);
|
||||
|
||||
ensureNativeTypeValid(nativeType, node);
|
||||
ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
|
||||
final funcType = dartType as FunctionType;
|
||||
|
||||
// Check return type.
|
||||
if (funcType.returnType != VoidType()) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiNativeCallableListenerReturnVoid.withArguments(
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
func.fileOffset,
|
||||
1,
|
||||
func.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
final replacement = _replaceNativeCallableListenerConstructor(node);
|
||||
|
||||
final compoundClasses = funcType.positionalParameters
|
||||
.whereType<InterfaceType>()
|
||||
.map((t) => t.classNode)
|
||||
.where((c) =>
|
||||
c.superclass == structClass || c.superclass == unionClass)
|
||||
.toList();
|
||||
return _invokeCompoundConstructors(replacement, compoundClasses);
|
||||
} on FfiStaticTypeError {
|
||||
// It's OK to swallow the exception because the diagnostics issued will
|
||||
// cause compilation to fail. By continuing, we can report more
|
||||
// diagnostics before compilation ends.
|
||||
}
|
||||
}
|
||||
return super.visitConstructorInvocation(node);
|
||||
}
|
||||
|
@ -354,90 +317,33 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
fileOffset: node.fileOffset,
|
||||
);
|
||||
} else if (target == fromFunctionMethod) {
|
||||
return _verifyAndReplaceNativeCallableIsolateLocal(node,
|
||||
fromFunction: true);
|
||||
} else if (target == nativeCallableIsolateLocalConstructor) {
|
||||
return _verifyAndReplaceNativeCallableIsolateLocal(node);
|
||||
} else if (target == nativeCallableListenerConstructor) {
|
||||
final DartType nativeType = InterfaceType(nativeFunctionClass,
|
||||
currentLibrary.nonNullable, [node.arguments.types[0]]);
|
||||
final Expression func = node.arguments.positional[0];
|
||||
final DartType dartType = func.getStaticType(staticTypeContext!);
|
||||
|
||||
_ensureIsStaticFunction(func, fromFunctionMethod.name.text);
|
||||
|
||||
ensureNativeTypeValid(nativeType, node);
|
||||
ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
|
||||
final funcType = dartType as FunctionType;
|
||||
|
||||
// Check `exceptionalReturn`'s type.
|
||||
final Class expectedReturnClass =
|
||||
((node.arguments.types[0] as FunctionType).returnType
|
||||
as InterfaceType)
|
||||
.classNode;
|
||||
final NativeType? expectedReturn = getType(expectedReturnClass);
|
||||
|
||||
if (expectedReturn == NativeType.kVoid ||
|
||||
expectedReturn == NativeType.kPointer ||
|
||||
expectedReturn == NativeType.kHandle ||
|
||||
expectedReturnClass.superclass == structClass ||
|
||||
expectedReturnClass.superclass == unionClass) {
|
||||
if (node.arguments.positional.length > 1) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiExpectedNoExceptionalReturn.withArguments(
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location?.file);
|
||||
return node;
|
||||
}
|
||||
node.arguments.positional.add(NullLiteral()..parent = node);
|
||||
} else {
|
||||
// The exceptional return value is not optional for other return
|
||||
// types.
|
||||
if (node.arguments.positional.length < 2) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiExpectedExceptionalReturn.withArguments(
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
final Expression exceptionalReturn = node.arguments.positional[1];
|
||||
|
||||
// The exceptional return value must be a constant so that it be
|
||||
// referenced by precompiled trampoline's object pool.
|
||||
if (exceptionalReturn is! BasicLiteral &&
|
||||
!(exceptionalReturn is ConstantExpression &&
|
||||
exceptionalReturn.constant is PrimitiveConstant)) {
|
||||
diagnosticReporter.report(messageFfiExpectedConstant,
|
||||
node.fileOffset, 1, node.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Moreover it may not be null.
|
||||
if (exceptionalReturn is NullLiteral ||
|
||||
(exceptionalReturn is ConstantExpression &&
|
||||
exceptionalReturn.constant is NullConstant)) {
|
||||
diagnosticReporter.report(messageFfiExceptionalReturnNull,
|
||||
node.fileOffset, 1, node.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
final DartType returnType =
|
||||
exceptionalReturn.getStaticType(staticTypeContext!);
|
||||
|
||||
if (!env.isSubtypeOf(returnType, funcType.returnType,
|
||||
SubtypeCheckMode.ignoringNullabilities)) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiDartTypeMismatch.withArguments(returnType,
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
exceptionalReturn.fileOffset,
|
||||
1,
|
||||
exceptionalReturn.location?.file);
|
||||
return node;
|
||||
}
|
||||
// Check return type.
|
||||
if (funcType.returnType != VoidType()) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiNativeCallableListenerReturnVoid.withArguments(
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
func.fileOffset,
|
||||
1,
|
||||
func.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
final replacement = _replaceFromFunction(node);
|
||||
final replacement = _replaceNativeCallableListenerConstructor(node);
|
||||
|
||||
final compoundClasses = funcType.positionalParameters
|
||||
.whereType<InterfaceType>()
|
||||
|
@ -558,8 +464,8 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
// compile-time and run-time aspects of creating the closure:
|
||||
//
|
||||
// final dynamic _#ffiCallback0 = Pointer.fromFunction<T>(f, e) =>
|
||||
// _pointerFromFunction<NativeFunction<T>>(
|
||||
// _nativeCallbackFunction<T>(f, e));
|
||||
// _createNativeCallableIsolateLocal<NativeFunction<T>>(
|
||||
// _nativeCallbackFunction<T>(f, e), null, false);
|
||||
//
|
||||
// ... _#ffiCallback0 ...
|
||||
//
|
||||
|
@ -568,7 +474,8 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
//
|
||||
// Creating this closure requires a runtime call, so we save the result in a
|
||||
// synthetic top-level field to avoid recomputing it.
|
||||
Expression _replaceFromFunction(StaticInvocation node) {
|
||||
Expression _replaceFromFunction(
|
||||
StaticInvocation node, Expression exceptionalReturn) {
|
||||
final nativeFunctionType = InterfaceType(
|
||||
nativeFunctionClass, currentLibrary.nonNullable, node.arguments.types);
|
||||
var name = Name("_#ffiCallback${callbackCount++}", currentLibrary);
|
||||
|
@ -577,9 +484,16 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
type: InterfaceType(
|
||||
pointerClass, currentLibrary.nonNullable, [nativeFunctionType]),
|
||||
initializer: StaticInvocation(
|
||||
pointerFromFunctionProcedure,
|
||||
createNativeCallableIsolateLocalProcedure,
|
||||
Arguments([
|
||||
StaticInvocation(nativeCallbackFunctionProcedure, node.arguments)
|
||||
StaticInvocation(
|
||||
nativeCallbackFunctionProcedure,
|
||||
Arguments([
|
||||
node.arguments.positional[0],
|
||||
exceptionalReturn,
|
||||
], types: node.arguments.types)),
|
||||
NullLiteral(),
|
||||
BoolLiteral(false),
|
||||
], types: [
|
||||
nativeFunctionType
|
||||
])),
|
||||
|
@ -592,14 +506,65 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
return StaticGet(field);
|
||||
}
|
||||
|
||||
// NativeCallable<T>.isolateLocal(target, exceptionalReturn) calls become:
|
||||
// isStaticFunction is false:
|
||||
// _NativeCallableIsolateLocal<T>(
|
||||
// _createNativeCallableIsolateLocal<NativeFunction<T>>(
|
||||
// _nativeIsolateLocalCallbackFunction<T>(exceptionalReturn),
|
||||
// target,
|
||||
// true));
|
||||
// isStaticFunction is true:
|
||||
// _NativeCallableIsolateLocal<T>(
|
||||
// _createNativeCallableIsolateLocal<NativeFunction<T>>(
|
||||
// _nativeCallbackFunction<T>(target, exceptionalReturn),
|
||||
// null,
|
||||
// true);
|
||||
Expression _replaceNativeCallableIsolateLocalConstructor(
|
||||
StaticInvocation node,
|
||||
Expression exceptionalReturn,
|
||||
bool isStaticFunction) {
|
||||
final nativeFunctionType = InterfaceType(
|
||||
nativeFunctionClass, currentLibrary.nonNullable, node.arguments.types);
|
||||
final target = node.arguments.positional[0];
|
||||
late StaticInvocation pointerValue;
|
||||
if (isStaticFunction) {
|
||||
pointerValue = StaticInvocation(
|
||||
createNativeCallableIsolateLocalProcedure,
|
||||
Arguments([
|
||||
StaticInvocation(
|
||||
nativeCallbackFunctionProcedure,
|
||||
Arguments([
|
||||
target,
|
||||
exceptionalReturn,
|
||||
], types: node.arguments.types)),
|
||||
NullLiteral(),
|
||||
BoolLiteral(true),
|
||||
], types: [
|
||||
nativeFunctionType,
|
||||
]));
|
||||
} else {
|
||||
pointerValue = StaticInvocation(
|
||||
createNativeCallableIsolateLocalProcedure,
|
||||
Arguments([
|
||||
StaticInvocation(nativeIsolateLocalCallbackFunctionProcedure,
|
||||
Arguments([exceptionalReturn], types: node.arguments.types)),
|
||||
target,
|
||||
BoolLiteral(true),
|
||||
], types: [
|
||||
nativeFunctionType,
|
||||
]));
|
||||
}
|
||||
return ConstructorInvocation(nativeCallablePrivateIsolateLocalConstructor,
|
||||
Arguments([pointerValue], types: node.arguments.types));
|
||||
}
|
||||
|
||||
// NativeCallable<T>.listener(target) calls become:
|
||||
// void _handler(List args) => target(args[0], args[1], ...)
|
||||
// final _callback = NativeCallable<T>._(_handler, debugName);
|
||||
// _callback._pointer = _pointerAsyncFromFunction<NativeFunction<T>>(
|
||||
// final _callback = _NativeCallableListener<T>(_handler, debugName);
|
||||
// _callback._pointer = _createNativeCallableListener<NativeFunction<T>>(
|
||||
// _nativeAsyncCallbackFunction<T>(), _callback._rawPort);
|
||||
// expression result: _callback;
|
||||
Expression _replaceNativeCallableListenerConstructor(
|
||||
ConstructorInvocation node) {
|
||||
Expression _replaceNativeCallableListenerConstructor(StaticInvocation node) {
|
||||
final nativeFunctionType = InterfaceType(
|
||||
nativeFunctionClass, currentLibrary.nonNullable, node.arguments.types);
|
||||
final listType = InterfaceType(listClass, currentLibrary.nonNullable);
|
||||
|
@ -612,7 +577,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
..fileOffset = node.fileOffset;
|
||||
final targetArgs = <Expression>[];
|
||||
for (int i = 0; i < targetType.positionalParameters.length; ++i) {
|
||||
// Do we need an `as` expression?
|
||||
targetArgs.add(InstanceInvocation(InstanceAccessKind.Instance,
|
||||
VariableGet(args), listElementAt.name, Arguments([IntLiteral(i)]),
|
||||
interfaceTarget: listElementAt,
|
||||
|
@ -630,10 +594,10 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
positionalParameters: [args], returnType: VoidType())
|
||||
..fileOffset = node.fileOffset;
|
||||
|
||||
// final _callback = NativeCallable<T>._(_handler, debugName);
|
||||
// final _callback = NativeCallable<T>._listener(_handler, debugName);
|
||||
final nativeCallable = VariableDeclaration.forValue(
|
||||
ConstructorInvocation(
|
||||
nativeCallablePrivateConstructor,
|
||||
nativeCallablePrivateListenerConstructor,
|
||||
Arguments([
|
||||
FunctionExpression(handler),
|
||||
StringLiteral('NativeCallable($target)'),
|
||||
|
@ -644,10 +608,10 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
isFinal: true)
|
||||
..fileOffset = node.fileOffset;
|
||||
|
||||
// _callback._pointer = _pointerAsyncFromFunction<NativeFunction<T>>(
|
||||
// _callback._pointer = _createNativeCallableListener<NativeFunction<T>>(
|
||||
// _nativeAsyncCallbackFunction<T>(), _callback._rawPort);
|
||||
final pointerValue = StaticInvocation(
|
||||
pointerAsyncFromFunctionProcedure,
|
||||
createNativeCallableListenerProcedure,
|
||||
Arguments([
|
||||
StaticInvocation(nativeAsyncCallbackFunctionProcedure,
|
||||
Arguments([], types: [targetType])),
|
||||
|
@ -675,6 +639,121 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
VariableGet(nativeCallable));
|
||||
}
|
||||
|
||||
Expression _verifyAndReplaceNativeCallableIsolateLocal(StaticInvocation node,
|
||||
{bool fromFunction = false}) {
|
||||
final DartType nativeType = InterfaceType(nativeFunctionClass,
|
||||
currentLibrary.nonNullable, [node.arguments.types[0]]);
|
||||
final Expression func = node.arguments.positional[0];
|
||||
final DartType dartType = func.getStaticType(staticTypeContext!);
|
||||
|
||||
final isStaticFunction = _isStaticFunction(func);
|
||||
if (fromFunction && !isStaticFunction) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiNotStatic.withArguments(fromFunctionMethod.name.text),
|
||||
func.fileOffset,
|
||||
1,
|
||||
func.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
ensureNativeTypeValid(nativeType, node);
|
||||
ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
|
||||
final funcType = dartType as FunctionType;
|
||||
|
||||
// Check `exceptionalReturn`'s type.
|
||||
final Class expectedReturnClass =
|
||||
((node.arguments.types[0] as FunctionType).returnType as InterfaceType)
|
||||
.classNode;
|
||||
final NativeType? expectedReturn = getType(expectedReturnClass);
|
||||
|
||||
Expression exceptionalReturn = NullLiteral();
|
||||
bool hasExceptionalReturn = false;
|
||||
if (fromFunction) {
|
||||
if (node.arguments.positional.length > 1) {
|
||||
exceptionalReturn = node.arguments.positional[1];
|
||||
hasExceptionalReturn = true;
|
||||
}
|
||||
} else {
|
||||
if (node.arguments.named.isNotEmpty) {
|
||||
exceptionalReturn = node.arguments.named[0].value;
|
||||
hasExceptionalReturn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedReturn == NativeType.kVoid ||
|
||||
expectedReturn == NativeType.kPointer ||
|
||||
expectedReturn == NativeType.kHandle ||
|
||||
expectedReturnClass.superclass == structClass ||
|
||||
expectedReturnClass.superclass == unionClass) {
|
||||
if (hasExceptionalReturn) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiExpectedNoExceptionalReturn.withArguments(
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location?.file);
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
// The exceptional return value is not optional for other return types.
|
||||
if (!hasExceptionalReturn) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiExpectedExceptionalReturn.withArguments(
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
// The exceptional return value must be a constant so that it can be
|
||||
// referenced by precompiled trampoline's object pool.
|
||||
if (exceptionalReturn is! BasicLiteral &&
|
||||
!(exceptionalReturn is ConstantExpression &&
|
||||
exceptionalReturn.constant is PrimitiveConstant)) {
|
||||
diagnosticReporter.report(messageFfiExpectedConstant, node.fileOffset,
|
||||
1, node.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Moreover it may not be null.
|
||||
if (exceptionalReturn is NullLiteral ||
|
||||
(exceptionalReturn is ConstantExpression &&
|
||||
exceptionalReturn.constant is NullConstant)) {
|
||||
diagnosticReporter.report(messageFfiExceptionalReturnNull,
|
||||
node.fileOffset, 1, node.location?.file);
|
||||
return node;
|
||||
}
|
||||
|
||||
final DartType returnType =
|
||||
exceptionalReturn.getStaticType(staticTypeContext!);
|
||||
|
||||
if (!env.isSubtypeOf(returnType, funcType.returnType,
|
||||
SubtypeCheckMode.ignoringNullabilities)) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiDartTypeMismatch.withArguments(returnType,
|
||||
funcType.returnType, currentLibrary.isNonNullableByDefault),
|
||||
exceptionalReturn.fileOffset,
|
||||
1,
|
||||
exceptionalReturn.location?.file);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
final replacement = fromFunction
|
||||
? _replaceFromFunction(node, exceptionalReturn)
|
||||
: _replaceNativeCallableIsolateLocalConstructor(
|
||||
node, exceptionalReturn, isStaticFunction);
|
||||
|
||||
final compoundClasses = funcType.positionalParameters
|
||||
.whereType<InterfaceType>()
|
||||
.map((t) => t.classNode)
|
||||
.where((c) => c.superclass == structClass || c.superclass == unionClass)
|
||||
.toList();
|
||||
return _invokeCompoundConstructors(replacement, compoundClasses);
|
||||
}
|
||||
|
||||
Expression _replaceGetRef(StaticInvocation node) {
|
||||
final dartType = node.arguments.types[0];
|
||||
final clazz = (dartType as InterfaceType).classNode;
|
||||
|
@ -882,16 +961,9 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
..fileOffset = node.fileOffset);
|
||||
}
|
||||
|
||||
void _ensureIsStaticFunction(Expression node, String methodName) {
|
||||
if ((node is StaticGet && node.target is Procedure) ||
|
||||
(node is ConstantExpression &&
|
||||
node.constant is StaticTearOffConstant)) {
|
||||
return;
|
||||
}
|
||||
diagnosticReporter.report(templateFfiNotStatic.withArguments(methodName),
|
||||
node.fileOffset, 1, node.location?.file);
|
||||
throw FfiStaticTypeError();
|
||||
}
|
||||
bool _isStaticFunction(Expression node) =>
|
||||
(node is StaticGet && node.target is Procedure) ||
|
||||
(node is ConstantExpression && node.constant is StaticTearOffConstant);
|
||||
|
||||
/// Returns the class that should not be implemented or extended.
|
||||
///
|
||||
|
|
|
@ -7,6 +7,12 @@ import 'dart:ffi';
|
|||
void main() {
|
||||
testNativeCallableListener();
|
||||
testNativeCallableListenerClosure();
|
||||
testNativeCallableIsolateLocalVoid();
|
||||
testNativeCallableIsolateLocalVoidClosure();
|
||||
testNativeCallableIsolateLocalPointer();
|
||||
testNativeCallableIsolateLocalPointerClosure();
|
||||
testNativeCallableIsolateLocalInt();
|
||||
testNativeCallableIsolateLocalIntClosure();
|
||||
}
|
||||
|
||||
void printInt(int i) => print(i);
|
||||
|
@ -24,3 +30,53 @@ void testNativeCallableListenerClosure() {
|
|||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalVoid() {
|
||||
final callback = NativeCallable<Void Function(Int32)>.isolateLocal(printInt);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalVoidClosure() {
|
||||
int j = 123;
|
||||
void closure(int i) => print(i + j);
|
||||
final callback = NativeCallable<Void Function(Int32)>.isolateLocal(closure);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
Pointer intToPointer(int i) => Pointer.fromAddress(i);
|
||||
|
||||
void testNativeCallableIsolateLocalPointer() {
|
||||
final callback =
|
||||
NativeCallable<Pointer Function(Int32)>.isolateLocal(intToPointer);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalPointerClosure() {
|
||||
int j = 123;
|
||||
Pointer closure(int i) => Pointer.fromAddress(i + j);
|
||||
final callback =
|
||||
NativeCallable<Pointer Function(Int32)>.isolateLocal(closure);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
int negateInt(int i) => -i;
|
||||
|
||||
void testNativeCallableIsolateLocalInt() {
|
||||
final callback = NativeCallable<Int Function(Int32)>.isolateLocal(negateInt,
|
||||
exceptionalReturn: 123);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalIntClosure() {
|
||||
int j = 123;
|
||||
int closure(int i) => i + j;
|
||||
final callback = NativeCallable<Int Function(Int32)>.isolateLocal(closure,
|
||||
exceptionalReturn: 123);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
|
|
@ -9,32 +9,83 @@ import "dart:ffi";
|
|||
static method main() → void {
|
||||
self::testNativeCallableListener();
|
||||
self::testNativeCallableListenerClosure();
|
||||
self::testNativeCallableIsolateLocalVoid();
|
||||
self::testNativeCallableIsolateLocalVoidClosure();
|
||||
self::testNativeCallableIsolateLocalPointer();
|
||||
self::testNativeCallableIsolateLocalPointerClosure();
|
||||
self::testNativeCallableIsolateLocalInt();
|
||||
self::testNativeCallableIsolateLocalIntClosure();
|
||||
}
|
||||
static method printInt(core::int i) → void
|
||||
return [@vm.inferred-type.metadata=dart.core::Null? (value: null)] core::print(i);
|
||||
static method testNativeCallableListener() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = block {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t1 = new ffi::NativeCallable::_<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t1 = new ffi::_NativeCallableListener::•<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
#C1(args.{core::List::[]}(0){(core::int) → dynamic}){(ffi::Int32) → ffi::Void};
|
||||
, "NativeCallable(ConstantExpression(printInt))");
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] [@vm.direct-call.metadata=dart.ffi::NativeCallable._pointer] #t1.{ffi::NativeCallable::_pointer} = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_pointerAsyncFromFunction<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), [@vm.direct-call.metadata=dart.ffi::NativeCallable._port] [@vm.inferred-type.metadata=dart.isolate::_RawReceivePort] #t1.{ffi::NativeCallable::_port}{iso::RawReceivePort});
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] [@vm.direct-call.metadata=dart.ffi::_NativeCallableBase._pointer] #t1.{ffi::_NativeCallableBase::_pointer} = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableListener<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), [@vm.direct-call.metadata=dart.ffi::_NativeCallableListener._port] [@vm.inferred-type.metadata=dart.isolate::_RawReceivePort] #t1.{ffi::_NativeCallableListener::_port}{iso::RawReceivePort});
|
||||
} =>#t1;
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::NativeCallable.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::NativeCallable.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableListener.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableListenerClosure() → void {
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi (value: 123)] core::int j = 123;
|
||||
function closure(core::int i) → void
|
||||
return core::print([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(j){(core::num) → core::int});
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = block {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::NativeCallable::_<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::_NativeCallableListener::•<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
closure(args.{core::List::[]}(0){(core::int) → dynamic}){(ffi::Int32) → ffi::Void};
|
||||
, "NativeCallable(VariableGetImpl(closure))");
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] [@vm.direct-call.metadata=dart.ffi::NativeCallable._pointer] #t2.{ffi::NativeCallable::_pointer} = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_pointerAsyncFromFunction<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), [@vm.direct-call.metadata=dart.ffi::NativeCallable._port] [@vm.inferred-type.metadata=dart.isolate::_RawReceivePort] #t2.{ffi::NativeCallable::_port}{iso::RawReceivePort});
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] [@vm.direct-call.metadata=dart.ffi::_NativeCallableBase._pointer] #t2.{ffi::_NativeCallableBase::_pointer} = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableListener<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), [@vm.direct-call.metadata=dart.ffi::_NativeCallableListener._port] [@vm.inferred-type.metadata=dart.isolate::_RawReceivePort] #t2.{ffi::_NativeCallableListener::_port}{iso::RawReceivePort});
|
||||
} =>#t2;
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::NativeCallable.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::NativeCallable.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableListener.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalVoid() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeCallbackFunction<(ffi::Int32) → ffi::Void>(#C1, null), null, true));
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableIsolateLocal.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalVoidClosure() → void {
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi (value: 123)] core::int j = 123;
|
||||
function closure(core::int i) → void
|
||||
return core::print([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(j){(core::num) → core::int});
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Void>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeIsolateLocalCallbackFunction<(ffi::Int32) → ffi::Void>(null), closure, true));
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableIsolateLocal.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method intToPointer(core::int i) → ffi::Pointer<ffi::NativeType>
|
||||
return [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::Pointer::fromAddress<ffi::NativeType>(i);
|
||||
static method testNativeCallableIsolateLocalPointer() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Pointer<ffi::NativeType>> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>(ffi::_nativeCallbackFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>(#C2, null), null, true));
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableIsolateLocal.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalPointerClosure() → void {
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi (value: 123)] core::int j = 123;
|
||||
function closure(core::int i) → ffi::Pointer<ffi::NativeType>
|
||||
return ffi::Pointer::fromAddress<ffi::NativeType>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(j){(core::num) → core::int});
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Pointer<ffi::NativeType>> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>(ffi::_nativeIsolateLocalCallbackFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>(null), closure, true));
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableIsolateLocal.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
[@vm.unboxing-info.metadata=(b)->i]static method negateInt(core::int i) → core::int
|
||||
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.unary-] [@vm.inferred-type.metadata=int (skip check)] i.{core::int::unary-}(){() → core::int};
|
||||
static method testNativeCallableIsolateLocalInt() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Int> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Int>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>(ffi::_nativeCallbackFunction<(ffi::Int32) → ffi::Int>(#C3, 123), null, true));
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableIsolateLocal.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalIntClosure() → void {
|
||||
[@vm.inferred-type.metadata=dart.core::_Smi (value: 123)] core::int j = 123;
|
||||
function closure(core::int i) → core::int
|
||||
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=!? (skip check)] i.{core::num::+}(j){(core::num) → core::int};
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Int> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Int>([@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>(ffi::_nativeIsolateLocalCallbackFunction<(ffi::Int32) → ffi::Int>(123), closure, true));
|
||||
core::print([@vm.direct-call.metadata=dart.ffi::_NativeCallableBase.nativeFunction] [@vm.inferred-type.metadata=dart.ffi::Pointer] callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>});
|
||||
[@vm.direct-call.metadata=dart.ffi::_NativeCallableIsolateLocal.close] [@vm.inferred-type.metadata=!? (skip check)] callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
constants {
|
||||
#C1 = static-tearoff self::printInt
|
||||
#C2 = static-tearoff self::intToPointer
|
||||
#C3 = static-tearoff self::negateInt
|
||||
}
|
||||
|
|
|
@ -9,15 +9,21 @@ import "dart:ffi";
|
|||
static method main() → void {
|
||||
self::testNativeCallableListener();
|
||||
self::testNativeCallableListenerClosure();
|
||||
self::testNativeCallableIsolateLocalVoid();
|
||||
self::testNativeCallableIsolateLocalVoidClosure();
|
||||
self::testNativeCallableIsolateLocalPointer();
|
||||
self::testNativeCallableIsolateLocalPointerClosure();
|
||||
self::testNativeCallableIsolateLocalInt();
|
||||
self::testNativeCallableIsolateLocalIntClosure();
|
||||
}
|
||||
static method printInt(core::int i) → void
|
||||
return core::print(i);
|
||||
static method testNativeCallableListener() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = block {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t1 = new ffi::NativeCallable::_<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t1 = new ffi::_NativeCallableListener::•<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
#C1(args.{core::List::[]}(0){(core::int) → dynamic}){(ffi::Int32) → ffi::Void};
|
||||
, "NativeCallable(ConstantExpression(printInt))");
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] #t1.{ffi::NativeCallable::_pointer} = ffi::_pointerAsyncFromFunction<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), #t1.{ffi::NativeCallable::_port}{iso::RawReceivePort});
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] #t1.{ffi::_NativeCallableBase::_pointer} = ffi::_createNativeCallableListener<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), #t1.{ffi::_NativeCallableListener::_port}{iso::RawReceivePort});
|
||||
} =>#t1;
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
|
@ -27,14 +33,59 @@ static method testNativeCallableListenerClosure() → void {
|
|||
function closure(core::int i) → void
|
||||
return core::print(i.{core::num::+}(j){(core::num) → core::int});
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = block {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::NativeCallable::_<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::_NativeCallableListener::•<(ffi::Int32) → ffi::Void>((final core::List<dynamic> args) → void
|
||||
closure(args.{core::List::[]}(0){(core::int) → dynamic}){(ffi::Int32) → ffi::Void};
|
||||
, "NativeCallable(VariableGetImpl(closure))");
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] #t2.{ffi::NativeCallable::_pointer} = ffi::_pointerAsyncFromFunction<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), #t2.{ffi::NativeCallable::_port}{iso::RawReceivePort});
|
||||
[@vm.call-site-attributes.metadata=receiverType:dart.ffi::NativeCallable<dart.ffi::Void Function(dart.ffi::Int32)>] #t2.{ffi::_NativeCallableBase::_pointer} = ffi::_createNativeCallableListener<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeAsyncCallbackFunction<(ffi::Int32) → ffi::Void>(), #t2.{ffi::_NativeCallableListener::_port}{iso::RawReceivePort});
|
||||
} =>#t2;
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalVoid() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Void>(ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeCallbackFunction<(ffi::Int32) → ffi::Void>(#C1, null), null, true));
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalVoidClosure() → void {
|
||||
core::int j = 123;
|
||||
function closure(core::int i) → void
|
||||
return core::print(i.{core::num::+}(j){(core::num) → core::int});
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Void>(ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>(ffi::_nativeIsolateLocalCallbackFunction<(ffi::Int32) → ffi::Void>(null), closure, true));
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method intToPointer(core::int i) → ffi::Pointer<ffi::NativeType>
|
||||
return ffi::Pointer::fromAddress<ffi::NativeType>(i);
|
||||
static method testNativeCallableIsolateLocalPointer() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Pointer<ffi::NativeType>> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>(ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>(ffi::_nativeCallbackFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>(#C2, null), null, true));
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalPointerClosure() → void {
|
||||
core::int j = 123;
|
||||
function closure(core::int i) → ffi::Pointer<ffi::NativeType>
|
||||
return ffi::Pointer::fromAddress<ffi::NativeType>(i.{core::num::+}(j){(core::num) → core::int});
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Pointer<ffi::NativeType>> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>(ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>(ffi::_nativeIsolateLocalCallbackFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>(null), closure, true));
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Pointer<ffi::NativeType>>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method negateInt(core::int i) → core::int
|
||||
return i.{core::int::unary-}(){() → core::int};
|
||||
static method testNativeCallableIsolateLocalInt() → void {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Int> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Int>(ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>(ffi::_nativeCallbackFunction<(ffi::Int32) → ffi::Int>(#C3, 123), null, true));
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableIsolateLocalIntClosure() → void {
|
||||
core::int j = 123;
|
||||
function closure(core::int i) → core::int
|
||||
return i.{core::num::+}(j){(core::num) → core::int};
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Int> callback = new ffi::_NativeCallableIsolateLocal::•<(ffi::Int32) → ffi::Int>(ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>(ffi::_nativeIsolateLocalCallbackFunction<(ffi::Int32) → ffi::Int>(123), closure, true));
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Int>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
constants {
|
||||
#C1 = static-tearoff self::printInt
|
||||
#C2 = static-tearoff self::intToPointer
|
||||
#C3 = static-tearoff self::negateInt
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ final class Struct12 extends ffi::Struct {
|
|||
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
|
||||
;
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer]static final field ffi::Pointer<ffi::NativeFunction<(self::Struct3) → ffi::Int32>> _#ffiCallback0 = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_pointerFromFunction<ffi::NativeFunction<(self::Struct3) → ffi::Int32>>(ffi::_nativeCallbackFunction<(self::Struct3) → ffi::Int32>(#C16, 0))/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer]static final field ffi::Pointer<ffi::NativeFunction<() → self::Struct7>> _#ffiCallback1 = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_pointerFromFunction<ffi::NativeFunction<() → self::Struct7>>(ffi::_nativeCallbackFunction<() → self::Struct7>(#C17, null))/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer]static final field ffi::Pointer<ffi::NativeFunction<(self::Struct3) → ffi::Int32>> _#ffiCallback0 = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<(self::Struct3) → ffi::Int32>>(ffi::_nativeCallbackFunction<(self::Struct3) → ffi::Int32>(#C16, 0), null, false)/*isLegacy*/;
|
||||
[@vm.inferred-type.metadata=dart.ffi::Pointer]static final field ffi::Pointer<ffi::NativeFunction<() → self::Struct7>> _#ffiCallback1 = [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_createNativeCallableIsolateLocal<ffi::NativeFunction<() → self::Struct7>>(ffi::_nativeCallbackFunction<() → self::Struct7>(#C17, null), null, false)/*isLegacy*/;
|
||||
static method main() → void {
|
||||
self::testLookupFunctionReturn();
|
||||
self::testLookupFunctionArgument();
|
||||
|
|
|
@ -1231,4 +1231,13 @@ DART_EXPORT void CallFunctionOnNewThreadNonBlocking(int64_t response_id,
|
|||
thread.detach();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Tests for isolate local callbacks.
|
||||
|
||||
DART_EXPORT void CallTwoIntFunction(int32_t (*fn)(int32_t, int32_t),
|
||||
int32_t a,
|
||||
int32_t b) {
|
||||
fn(a, b);
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -38,25 +38,38 @@ DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 2) {
|
|||
UNREACHABLE();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_pointerFromFunction, 1, 1) {
|
||||
const auto& function = Function::CheckedHandle(zone, arguments->NativeArg0());
|
||||
return Pointer::New(isolate->CreateSyncFfiCallback(zone, function));
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_pointerAsyncFromFunction, 1, 2) {
|
||||
const auto& function = Function::CheckedHandle(zone, arguments->NativeArg0());
|
||||
DEFINE_NATIVE_ENTRY(Ffi_createNativeCallableListener, 1, 2) {
|
||||
const auto& send_function =
|
||||
Function::CheckedHandle(zone, arguments->NativeArg0());
|
||||
const auto& port =
|
||||
ReceivePort::CheckedHandle(zone, arguments->NativeArgAt(1));
|
||||
return Pointer::New(
|
||||
isolate->CreateAsyncFfiCallback(zone, function, port.Id()));
|
||||
isolate->CreateAsyncFfiCallback(zone, send_function, port.Id()));
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_deleteAsyncFunctionPointer, 1, 1) {
|
||||
DEFINE_NATIVE_ENTRY(Ffi_createNativeCallableIsolateLocal, 1, 3) {
|
||||
const auto& trampoline =
|
||||
Function::CheckedHandle(zone, arguments->NativeArg0());
|
||||
const auto& target = Closure::CheckedHandle(zone, arguments->NativeArgAt(1));
|
||||
const bool keep_isolate_alive =
|
||||
Bool::CheckedHandle(zone, arguments->NativeArgAt(2)).value();
|
||||
return Pointer::New(isolate->CreateIsolateLocalFfiCallback(
|
||||
zone, trampoline, target, keep_isolate_alive));
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_deleteNativeCallable, 1, 1) {
|
||||
const auto& pointer = Pointer::CheckedHandle(zone, arguments->NativeArg0());
|
||||
isolate->DeleteFfiCallback(pointer.NativeAddress());
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_updateNativeCallableKeepIsolateAliveCounter, 1, 1) {
|
||||
const int64_t delta =
|
||||
Integer::CheckedHandle(zone, arguments->NativeArg0()).AsInt64Value();
|
||||
isolate->UpdateNativeCallableKeepIsolateAliveCounter(delta);
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(DartNativeApiFunctionPointer, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, name_dart, arguments->NativeArgAt(0));
|
||||
const char* name = name_dart.ToCString();
|
||||
|
|
|
@ -2094,7 +2094,7 @@ class FfiTrampolineDataSerializationCluster : public SerializationCluster {
|
|||
AutoTraceObject(data);
|
||||
WriteFromTo(data);
|
||||
s->Write<int32_t>(data->untag()->callback_id_);
|
||||
s->Write<uint8_t>(data->untag()->trampoline_kind_);
|
||||
s->Write<uint8_t>(data->untag()->ffi_function_kind_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2123,7 +2123,7 @@ class FfiTrampolineDataDeserializationCluster : public DeserializationCluster {
|
|||
FfiTrampolineData::InstanceSize());
|
||||
d.ReadFromTo(data);
|
||||
data->untag()->callback_id_ = d.Read<int32_t>();
|
||||
data->untag()->trampoline_kind_ = d.Read<uint8_t>();
|
||||
data->untag()->ffi_function_kind_ = d.Read<uint8_t>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -366,9 +366,10 @@ namespace dart {
|
|||
V(VMService_AddUserTagsToStreamableSampleList, 1) \
|
||||
V(VMService_RemoveUserTagsFromStreamableSampleList, 1) \
|
||||
V(Ffi_asFunctionInternal, 2) \
|
||||
V(Ffi_pointerFromFunction, 1) \
|
||||
V(Ffi_pointerAsyncFromFunction, 2) \
|
||||
V(Ffi_deleteAsyncFunctionPointer, 1) \
|
||||
V(Ffi_createNativeCallableListener, 2) \
|
||||
V(Ffi_createNativeCallableIsolateLocal, 3) \
|
||||
V(Ffi_deleteNativeCallable, 1) \
|
||||
V(Ffi_updateNativeCallableKeepIsolateAliveCounter, 1) \
|
||||
V(Ffi_dl_open, 1) \
|
||||
V(Ffi_dl_close, 1) \
|
||||
V(Ffi_dl_lookup, 2) \
|
||||
|
|
|
@ -425,7 +425,7 @@ struct CanonicalFfiCallbackFunctionTraits {
|
|||
f1.FfiCSignature() == f2.FfiCSignature() &&
|
||||
f1.FfiCallbackExceptionalReturn() ==
|
||||
f2.FfiCallbackExceptionalReturn() &&
|
||||
f1.GetFfiTrampolineKind() == f2.GetFfiTrampolineKind());
|
||||
f1.GetFfiFunctionKind() == f2.GetFfiFunctionKind());
|
||||
}
|
||||
static bool ReportStats() { return false; }
|
||||
};
|
||||
|
|
|
@ -3451,7 +3451,7 @@ void PrecompileParsedFunctionHelper::FinalizeCompilation(
|
|||
}
|
||||
|
||||
if (function.IsFfiTrampoline() &&
|
||||
function.GetFfiTrampolineKind() != FfiTrampolineKind::kCall) {
|
||||
function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
|
||||
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -832,10 +832,10 @@ void FlowGraphSerializer::WriteTrait<const Function&>::Write(
|
|||
return;
|
||||
}
|
||||
case UntaggedFunction::kFfiTrampoline: {
|
||||
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiTrampolineKind()));
|
||||
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiFunctionKind()));
|
||||
s->Write<const FunctionType&>(
|
||||
FunctionType::Handle(zone, x.FfiCSignature()));
|
||||
if (x.GetFfiTrampolineKind() != FfiTrampolineKind::kCall) {
|
||||
if (x.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
|
||||
s->Write<const Function&>(
|
||||
Function::Handle(zone, x.FfiCallbackTarget()));
|
||||
s->Write<const Instance&>(
|
||||
|
@ -922,10 +922,10 @@ const Function& FlowGraphDeserializer::ReadTrait<const Function&>::Read(
|
|||
target.GetDynamicInvocationForwarder(name));
|
||||
}
|
||||
case UntaggedFunction::kFfiTrampoline: {
|
||||
const FfiTrampolineKind kind =
|
||||
static_cast<FfiTrampolineKind>(d->Read<uint8_t>());
|
||||
const FfiFunctionKind kind =
|
||||
static_cast<FfiFunctionKind>(d->Read<uint8_t>());
|
||||
const FunctionType& c_signature = d->Read<const FunctionType&>();
|
||||
if (kind != FfiTrampolineKind::kCall) {
|
||||
if (kind != FfiFunctionKind::kCall) {
|
||||
const Function& callback_target = d->Read<const Function&>();
|
||||
const Instance& exceptional_return = d->Read<const Instance&>();
|
||||
return Function::ZoneHandle(
|
||||
|
|
|
@ -47,7 +47,7 @@ FunctionPtr TrampolineFunction(const String& name,
|
|||
|
||||
function.SetFfiCSignature(c_signature);
|
||||
function.SetFfiIsLeaf(is_leaf);
|
||||
function.SetFfiTrampolineKind(FfiTrampolineKind::kCall);
|
||||
function.SetFfiFunctionKind(FfiFunctionKind::kCall);
|
||||
|
||||
return function.ptr();
|
||||
}
|
||||
|
|
|
@ -15,10 +15,28 @@ namespace compiler {
|
|||
|
||||
namespace ffi {
|
||||
|
||||
const String& NativeCallbackFunctionName(Thread* thread,
|
||||
Zone* zone,
|
||||
const Function& dart_target,
|
||||
FfiFunctionKind kind) {
|
||||
switch (kind) {
|
||||
case FfiFunctionKind::kAsyncCallback:
|
||||
return Symbols::FfiAsyncCallback();
|
||||
case FfiFunctionKind::kIsolateLocalClosureCallback:
|
||||
return Symbols::FfiIsolateLocalCallback();
|
||||
case FfiFunctionKind::kIsolateLocalStaticCallback:
|
||||
return String::Handle(
|
||||
zone, Symbols::FromConcat(thread, Symbols::FfiCallback(),
|
||||
String::Handle(zone, dart_target.name())));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
||||
const Function& dart_target,
|
||||
const Instance& exceptional_return,
|
||||
FfiTrampolineKind kind) {
|
||||
FfiFunctionKind kind) {
|
||||
Thread* const thread = Thread::Current();
|
||||
Zone* const zone = thread->zone();
|
||||
Function& function = Function::Handle(zone);
|
||||
|
@ -29,11 +47,7 @@ FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
|||
// 'dart:ffi' library. Note that these functions will never be invoked by
|
||||
// Dart, so they may have duplicate names.
|
||||
const auto& name =
|
||||
kind == FfiTrampolineKind::kSyncCallback
|
||||
? String::Handle(zone, Symbols::FromConcat(
|
||||
thread, Symbols::FfiCallback(),
|
||||
String::Handle(zone, dart_target.name())))
|
||||
: Symbols::FfiAsyncCallback();
|
||||
NativeCallbackFunctionName(thread, zone, dart_target, kind);
|
||||
const Library& lib = Library::Handle(zone, Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
|
||||
auto& signature = FunctionType::Handle(zone, FunctionType::New());
|
||||
|
@ -50,7 +64,7 @@ FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
|||
// the body.
|
||||
function.SetFfiCSignature(c_signature);
|
||||
function.SetFfiCallbackTarget(dart_target);
|
||||
function.SetFfiTrampolineKind(kind);
|
||||
function.SetFfiFunctionKind(kind);
|
||||
|
||||
// We need to load the exceptional return value as a constant in the generated
|
||||
// function. Even though the FE ensures that it is a constant, it could still
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace ffi {
|
|||
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
||||
const Function& dart_target,
|
||||
const Instance& exceptional_return,
|
||||
FfiTrampolineKind kind);
|
||||
FfiFunctionKind kind);
|
||||
|
||||
// Builds a mapping from `callback-id` to code object / ...
|
||||
//
|
||||
|
|
|
@ -3372,9 +3372,13 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
|
|||
case MethodRecognizer::kFfiAsFunctionInternal:
|
||||
return BuildFfiAsFunctionInternal();
|
||||
case MethodRecognizer::kFfiNativeCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(FfiTrampolineKind::kSyncCallback);
|
||||
return BuildFfiNativeCallbackFunction(
|
||||
FfiFunctionKind::kIsolateLocalStaticCallback);
|
||||
case MethodRecognizer::kFfiNativeIsolateLocalCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(
|
||||
FfiFunctionKind::kIsolateLocalClosureCallback);
|
||||
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(FfiTrampolineKind::kAsyncCallback);
|
||||
return BuildFfiNativeCallbackFunction(FfiFunctionKind::kAsyncCallback);
|
||||
case MethodRecognizer::kFfiLoadAbiSpecificInt:
|
||||
return BuildLoadAbiSpecificInt(/*at_index=*/false);
|
||||
case MethodRecognizer::kFfiLoadAbiSpecificIntAtIndex:
|
||||
|
@ -6239,20 +6243,25 @@ Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
|
|||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
|
||||
FfiTrampolineKind kind) {
|
||||
FfiFunctionKind kind) {
|
||||
// The call-site must look like this (guaranteed by the FE which inserts it):
|
||||
//
|
||||
// FfiTrampolineKind::kSyncCallback:
|
||||
// FfiFunctionKind::kIsolateLocalStaticCallback:
|
||||
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
|
||||
//
|
||||
// FfiTrampolineKind::kAsyncCallback:
|
||||
// FfiFunctionKind::kAsyncCallback:
|
||||
// _nativeAsyncCallbackFunction<NativeSignatureType>()
|
||||
//
|
||||
// The FE also guarantees that both arguments are constants.
|
||||
// FfiFunctionKind::kIsolateLocalClosureCallback:
|
||||
// _nativeIsolateLocalCallbackFunction<NativeSignatureType>(
|
||||
// exceptionalReturn)
|
||||
//
|
||||
// The FE also guarantees that the arguments are constants.
|
||||
|
||||
// Target, and for kSync callbacks, the exceptional return.
|
||||
const bool has_target = kind == FfiFunctionKind::kIsolateLocalStaticCallback;
|
||||
const bool has_exceptional_return = kind != FfiFunctionKind::kAsyncCallback;
|
||||
const intptr_t expected_argc =
|
||||
kind == FfiTrampolineKind::kSyncCallback ? 2 : 0;
|
||||
static_cast<int>(has_target) + static_cast<int>(has_exceptional_return);
|
||||
|
||||
const intptr_t argc = ReadUInt(); // Read argument count.
|
||||
ASSERT(argc == expected_argc);
|
||||
|
@ -6273,8 +6282,9 @@ Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
|
|||
// Read target expression and extract the target function.
|
||||
Function& target = Function::Handle(Z, Function::null());
|
||||
Instance& exceptional_return = Instance::ZoneHandle(Z, Instance::null());
|
||||
if (kind == FfiTrampolineKind::kSyncCallback) {
|
||||
// Build first positional argument (target).
|
||||
|
||||
if (has_target) {
|
||||
// Build target argument.
|
||||
code += BuildExpression();
|
||||
Definition* target_def = B->Peek();
|
||||
ASSERT(target_def->IsConstant());
|
||||
|
@ -6285,8 +6295,10 @@ Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
|
|||
ASSERT(!target.IsNull() && target.IsImplicitClosureFunction());
|
||||
target = target.parent_function();
|
||||
code += Drop();
|
||||
}
|
||||
|
||||
// Build second positional argument (exceptionalReturn).
|
||||
if (has_exceptional_return) {
|
||||
// Build exceptionalReturn argument.
|
||||
code += BuildExpression();
|
||||
Definition* exceptional_return_def = B->Peek();
|
||||
ASSERT(exceptional_return_def->IsConstant());
|
||||
|
|
|
@ -394,7 +394,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
|||
|
||||
// Build FG for '_nativeCallbackFunction'. Reads an Arguments from the
|
||||
// Kernel buffer and pushes the resulting Function object.
|
||||
Fragment BuildFfiNativeCallbackFunction(FfiTrampolineKind kind);
|
||||
Fragment BuildFfiNativeCallbackFunction(FfiFunctionKind kind);
|
||||
|
||||
// Piece of a StringConcatenation.
|
||||
// Represents either a StringLiteral, or a Reader offset to the expression.
|
||||
|
|
|
@ -984,6 +984,7 @@ bool FlowGraphBuilder::IsRecognizedMethodForFlowGraph(
|
|||
case MethodRecognizer::kFfiLoadPointer:
|
||||
case MethodRecognizer::kFfiNativeCallbackFunction:
|
||||
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
|
||||
case MethodRecognizer::kFfiNativeIsolateLocalCallbackFunction:
|
||||
case MethodRecognizer::kFfiStoreInt8:
|
||||
case MethodRecognizer::kFfiStoreInt16:
|
||||
case MethodRecognizer::kFfiStoreInt32:
|
||||
|
@ -1267,7 +1268,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
|||
body += IntConstant(static_cast<int64_t>(compiler::ffi::TargetAbi()));
|
||||
break;
|
||||
case MethodRecognizer::kFfiNativeCallbackFunction:
|
||||
case MethodRecognizer::kFfiNativeAsyncCallbackFunction: {
|
||||
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
|
||||
case MethodRecognizer::kFfiNativeIsolateLocalCallbackFunction: {
|
||||
const auto& error = String::ZoneHandle(
|
||||
Z, Symbols::New(thread_,
|
||||
"This function should be handled on call site."));
|
||||
|
@ -4739,12 +4741,13 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
|
|||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
||||
const Function& function) {
|
||||
switch (function.GetFfiTrampolineKind()) {
|
||||
case FfiTrampolineKind::kSyncCallback:
|
||||
switch (function.GetFfiFunctionKind()) {
|
||||
case FfiFunctionKind::kIsolateLocalStaticCallback:
|
||||
case FfiFunctionKind::kIsolateLocalClosureCallback:
|
||||
return BuildGraphOfSyncFfiCallback(function);
|
||||
case FfiTrampolineKind::kAsyncCallback:
|
||||
case FfiFunctionKind::kAsyncCallback:
|
||||
return BuildGraphOfAsyncFfiCallback(function);
|
||||
case FfiTrampolineKind::kCall:
|
||||
case FfiFunctionKind::kCall:
|
||||
return BuildGraphOfFfiNative(function);
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -4966,6 +4969,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfSyncFfiCallback(
|
|||
RELEASE_ASSERT(error == nullptr);
|
||||
RELEASE_ASSERT(marshaller_ptr != nullptr);
|
||||
const auto& marshaller = *marshaller_ptr;
|
||||
const bool is_closure = function.GetFfiFunctionKind() ==
|
||||
FfiFunctionKind::kIsolateLocalClosureCallback;
|
||||
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
|
@ -4985,19 +4990,46 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfSyncFfiCallback(
|
|||
Fragment body = TryCatch(try_handler_index);
|
||||
++try_depth_;
|
||||
|
||||
LocalVariable* closure = nullptr;
|
||||
if (is_closure) {
|
||||
// Load and unwrap closure persistent handle.
|
||||
body += LoadThread();
|
||||
body +=
|
||||
LoadUntagged(compiler::target::Thread::unboxed_runtime_arg_offset());
|
||||
body += RawLoadField(compiler::target::PersistentHandle::ptr_offset());
|
||||
closure = MakeTemporary();
|
||||
}
|
||||
|
||||
// Box and push the arguments.
|
||||
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
|
||||
body += LoadNativeArg(marshaller, i);
|
||||
}
|
||||
|
||||
// Call the target.
|
||||
//
|
||||
// TODO(36748): Determine the hot-reload semantics of callbacks and update the
|
||||
// rebind-rule accordingly.
|
||||
body += StaticCall(TokenPosition::kNoSource,
|
||||
Function::ZoneHandle(Z, function.FfiCallbackTarget()),
|
||||
marshaller.num_args(), Array::empty_array(),
|
||||
ICData::kNoRebind);
|
||||
if (is_closure) {
|
||||
// Call the target. The +1 in the argument count is because the closure
|
||||
// itself is the first argument.
|
||||
const intptr_t argument_count = marshaller.num_args() + 1;
|
||||
body += LoadLocal(closure);
|
||||
if (!FLAG_precompiled_mode) {
|
||||
// The ClosureCallInstr() takes one explicit input (apart from arguments).
|
||||
// It uses it to find the target address (in AOT from
|
||||
// Closure::entry_point, in JIT from Closure::function_::entry_point).
|
||||
body += LoadNativeField(Slot::Closure_function());
|
||||
}
|
||||
body +=
|
||||
ClosureCall(Function::null_function(), TokenPosition::kNoSource,
|
||||
/*type_args_len=*/0, argument_count, Array::null_array());
|
||||
} else {
|
||||
// Call the target.
|
||||
//
|
||||
// TODO(36748): Determine the hot-reload semantics of callbacks and update
|
||||
// the rebind-rule accordingly.
|
||||
body += StaticCall(TokenPosition::kNoSource,
|
||||
Function::ZoneHandle(Z, function.FfiCallbackTarget()),
|
||||
marshaller.num_args(), Array::empty_array(),
|
||||
ICData::kNoRebind);
|
||||
}
|
||||
|
||||
if (marshaller.IsVoid(compiler::ffi::kResultIndex)) {
|
||||
body += Drop();
|
||||
body += IntConstant(0);
|
||||
|
|
|
@ -395,7 +395,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
case UntaggedFunction::kFfiTrampoline: {
|
||||
needs_expr_temp_ = true;
|
||||
// Callbacks and calls with handles need try/catch variables.
|
||||
if ((function.GetFfiTrampolineKind() != FfiTrampolineKind::kCall ||
|
||||
if ((function.GetFfiFunctionKind() != FfiFunctionKind::kCall ||
|
||||
function.FfiCSignatureContainsHandles())) {
|
||||
++depth_.try_;
|
||||
AddTryVariables();
|
||||
|
|
|
@ -477,7 +477,7 @@ CodePtr CompileParsedFunctionHelper::FinalizeCompilation(
|
|||
}
|
||||
|
||||
if (function.IsFfiTrampoline() &&
|
||||
function.GetFfiTrampolineKind() != FfiTrampolineKind::kCall) {
|
||||
function.GetFfiFunctionKind() != FfiFunctionKind::kCall) {
|
||||
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
|
||||
}
|
||||
|
||||
|
|
|
@ -271,6 +271,8 @@ namespace dart {
|
|||
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3fe722bc) \
|
||||
V(::, _nativeAsyncCallbackFunction, FfiNativeAsyncCallbackFunction, \
|
||||
0xbec4b7b9) \
|
||||
V(::, _nativeIsolateLocalCallbackFunction, \
|
||||
FfiNativeIsolateLocalCallbackFunction, 0x03e1a51f) \
|
||||
V(::, _nativeEffect, NativeEffect, 0x536f42b1) \
|
||||
V(::, _loadAbiSpecificInt, FfiLoadAbiSpecificInt, 0x77f96053) \
|
||||
V(::, _loadAbiSpecificIntAtIndex, FfiLoadAbiSpecificIntAtIndex, 0x6a964295) \
|
||||
|
|
|
@ -710,6 +710,11 @@ class LocalHandle : public AllStatic {
|
|||
static word InstanceSize();
|
||||
};
|
||||
|
||||
class PersistentHandle : public AllStatic {
|
||||
public:
|
||||
static word ptr_offset();
|
||||
};
|
||||
|
||||
class Pointer : public AllStatic {
|
||||
public:
|
||||
static word type_arguments_offset();
|
||||
|
|
|
@ -285,6 +285,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0xc;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x8;
|
||||
|
@ -999,6 +1000,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -1713,6 +1715,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0xc;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x8;
|
||||
|
@ -2426,6 +2429,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -3144,6 +3148,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -3859,6 +3864,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -4574,6 +4580,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0xc;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x8;
|
||||
|
@ -5289,6 +5296,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -5996,6 +6004,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0xc;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x8;
|
||||
|
@ -6702,6 +6711,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -7408,6 +7418,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0xc;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x8;
|
||||
|
@ -8113,6 +8124,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -8823,6 +8835,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -9530,6 +9543,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -10237,6 +10251,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0xc;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x8;
|
||||
|
@ -10944,6 +10959,7 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word OneByteString_data_offset = 0x10;
|
||||
static constexpr dart::compiler::target::word PersistentHandle_ptr_offset = 0x0;
|
||||
static constexpr dart::compiler::target::word PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word Pointer_type_arguments_offset =
|
||||
0x10;
|
||||
|
@ -11685,6 +11701,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0xc;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x8;
|
||||
|
@ -12474,6 +12492,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -13270,6 +13290,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -14062,6 +14084,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -14854,6 +14878,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -15648,6 +15674,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0xc;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x8;
|
||||
|
@ -16438,6 +16466,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -17220,6 +17250,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0xc;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x8;
|
||||
|
@ -18000,6 +18032,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -18787,6 +18821,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -19570,6 +19606,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -20353,6 +20391,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
@ -21138,6 +21178,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x238;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0xc;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x4;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x8;
|
||||
|
@ -21919,6 +21961,8 @@ static constexpr dart::compiler::target::word
|
|||
AOT_ObjectStore_suspend_state_yield_async_star_offset = 0x470;
|
||||
static constexpr dart::compiler::target::word AOT_OneByteString_data_offset =
|
||||
0x10;
|
||||
static constexpr dart::compiler::target::word AOT_PersistentHandle_ptr_offset =
|
||||
0x0;
|
||||
static constexpr dart::compiler::target::word AOT_PointerBase_data_offset = 0x8;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_Pointer_type_arguments_offset = 0x10;
|
||||
|
|
|
@ -211,6 +211,7 @@
|
|||
FIELD(ObjectStore, suspend_state_suspend_sync_star_at_start_offset) \
|
||||
FIELD(ObjectStore, suspend_state_yield_async_star_offset) \
|
||||
FIELD(OneByteString, data_offset) \
|
||||
FIELD(PersistentHandle, ptr_offset) \
|
||||
FIELD(PointerBase, data_offset) \
|
||||
FIELD(Pointer, type_arguments_offset) \
|
||||
FIELD(ReceivePort, send_port_offset) \
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "vm/ffi_callback_metadata.h"
|
||||
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/dart_api_state.h"
|
||||
#include "vm/flag_list.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/runtime_entry.h"
|
||||
|
@ -157,7 +158,7 @@ FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateMetadataEntry(
|
|||
Isolate* target_isolate,
|
||||
TrampolineType trampoline_type,
|
||||
uword target_entry_point,
|
||||
Dart_Port send_port,
|
||||
uint64_t context,
|
||||
Metadata** list_head) {
|
||||
MutexLocker locker(&lock_);
|
||||
EnsureFreeListNotEmptyLocked();
|
||||
|
@ -174,7 +175,7 @@ FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateMetadataEntry(
|
|||
next_entry->list_prev_ = entry;
|
||||
}
|
||||
*entry = Metadata(target_isolate, trampoline_type, target_entry_point,
|
||||
send_port, nullptr, next_entry);
|
||||
context, nullptr, next_entry);
|
||||
*list_head = entry;
|
||||
return TrampolineOfMetadata(entry);
|
||||
}
|
||||
|
@ -190,15 +191,28 @@ void FfiCallbackMetadata::AddToFreeListLocked(Metadata* entry) {
|
|||
free_list_tail_->free_list_next_ = entry;
|
||||
free_list_tail_ = entry;
|
||||
}
|
||||
entry->context_ = 0;
|
||||
entry->target_isolate_ = nullptr;
|
||||
entry->free_list_next_ = nullptr;
|
||||
}
|
||||
|
||||
void FfiCallbackMetadata::DeleteCallbackLocked(Metadata* entry) {
|
||||
ASSERT(lock_.IsOwnedByCurrentThread());
|
||||
if (entry->trampoline_type_ != TrampolineType::kAsync &&
|
||||
entry->context_ != 0) {
|
||||
ASSERT(entry->target_isolate_ != nullptr);
|
||||
auto* api_state = entry->target_isolate_->group()->api_state();
|
||||
ASSERT(api_state != nullptr);
|
||||
api_state->FreePersistentHandle(entry->closure_handle());
|
||||
}
|
||||
AddToFreeListLocked(entry);
|
||||
}
|
||||
|
||||
void FfiCallbackMetadata::DeleteAllCallbacks(Metadata** list_head) {
|
||||
MutexLocker locker(&lock_);
|
||||
for (Metadata* entry = *list_head; entry != nullptr;) {
|
||||
Metadata* next = entry->list_next();
|
||||
AddToFreeListLocked(entry);
|
||||
DeleteCallbackLocked(entry);
|
||||
entry = next;
|
||||
}
|
||||
*list_head = nullptr;
|
||||
|
@ -220,7 +234,7 @@ void FfiCallbackMetadata::DeleteCallback(Trampoline trampoline,
|
|||
if (next != nullptr) {
|
||||
next->list_prev_ = prev;
|
||||
}
|
||||
AddToFreeListLocked(entry);
|
||||
DeleteCallbackLocked(entry);
|
||||
}
|
||||
|
||||
uword FfiCallbackMetadata::GetEntryPoint(Zone* zone, const Function& function) {
|
||||
|
@ -231,12 +245,44 @@ uword FfiCallbackMetadata::GetEntryPoint(Zone* zone, const Function& function) {
|
|||
return code.EntryPoint();
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateSyncFfiCallback(
|
||||
PersistentHandle* FfiCallbackMetadata::CreatePersistentHandle(
|
||||
Isolate* isolate,
|
||||
const Closure& closure) {
|
||||
auto* api_state = isolate->group()->api_state();
|
||||
ASSERT(api_state != nullptr);
|
||||
auto* handle = api_state->AllocatePersistentHandle();
|
||||
handle->set_ptr(closure);
|
||||
return handle;
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline
|
||||
FfiCallbackMetadata::CreateIsolateLocalFfiCallback(Isolate* isolate,
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
const Closure& closure,
|
||||
Metadata** list_head) {
|
||||
if (closure.IsNull()) {
|
||||
// If the closure is null, it means the target is a static function, so is
|
||||
// baked into the trampoline and is an ordinary sync callback.
|
||||
ASSERT(function.GetFfiFunctionKind() ==
|
||||
FfiFunctionKind::kIsolateLocalStaticCallback);
|
||||
return CreateSyncFfiCallbackImpl(isolate, zone, function, nullptr,
|
||||
list_head);
|
||||
} else {
|
||||
ASSERT(function.GetFfiFunctionKind() ==
|
||||
FfiFunctionKind::kIsolateLocalClosureCallback);
|
||||
return CreateSyncFfiCallbackImpl(isolate, zone, function,
|
||||
CreatePersistentHandle(isolate, closure),
|
||||
list_head);
|
||||
}
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateSyncFfiCallbackImpl(
|
||||
Isolate* isolate,
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
PersistentHandle* closure,
|
||||
Metadata** list_head) {
|
||||
ASSERT(function.GetFfiTrampolineKind() == FfiTrampolineKind::kSyncCallback);
|
||||
TrampolineType trampoline_type = TrampolineType::kSync;
|
||||
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
|
@ -252,20 +298,20 @@ FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateSyncFfiCallback(
|
|||
#endif
|
||||
|
||||
return CreateMetadataEntry(isolate, trampoline_type,
|
||||
GetEntryPoint(zone, function), ILLEGAL_PORT,
|
||||
list_head);
|
||||
GetEntryPoint(zone, function),
|
||||
reinterpret_cast<uint64_t>(closure), list_head);
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateAsyncFfiCallback(
|
||||
Isolate* isolate,
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
const Function& send_function,
|
||||
Dart_Port send_port,
|
||||
Metadata** list_head) {
|
||||
ASSERT(function.GetFfiTrampolineKind() == FfiTrampolineKind::kAsyncCallback);
|
||||
ASSERT(send_function.GetFfiFunctionKind() == FfiFunctionKind::kAsyncCallback);
|
||||
return CreateMetadataEntry(isolate, TrampolineType::kAsync,
|
||||
GetEntryPoint(zone, function), send_port,
|
||||
list_head);
|
||||
GetEntryPoint(zone, send_function),
|
||||
static_cast<uint64_t>(send_port), list_head);
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline FfiCallbackMetadata::TrampolineOfMetadata(
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
class PersistentHandle;
|
||||
|
||||
// Stores metadata related to FFI callbacks (Dart functions that are assigned a
|
||||
// function pointer that can be invoked by native code). This is essentially a
|
||||
// map from trampoline pointer to Metadata, with some logic to assign and memory
|
||||
|
@ -37,10 +39,8 @@ class FfiCallbackMetadata {
|
|||
|
||||
enum class TrampolineType : uint8_t {
|
||||
kSync = 0,
|
||||
kAsync = 1,
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
kSyncStackDelta4 = 2,
|
||||
#endif
|
||||
kSyncStackDelta4 = 1, // Only used by TARGET_ARCH_IA32
|
||||
kAsync = 2,
|
||||
};
|
||||
|
||||
enum RuntimeFunctions {
|
||||
|
@ -55,12 +55,6 @@ class FfiCallbackMetadata {
|
|||
// Returns the FfiCallbackMetadata singleton.
|
||||
static FfiCallbackMetadata* Instance();
|
||||
|
||||
// Creates a sync callback trampoline for the given function.
|
||||
Trampoline CreateSyncFfiCallback(Isolate* isolate,
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
Metadata** list_head);
|
||||
|
||||
// Creates an async callback trampoline for the given function and associates
|
||||
// it with the send_port.
|
||||
Trampoline CreateAsyncFfiCallback(Isolate* isolate,
|
||||
|
@ -69,6 +63,13 @@ class FfiCallbackMetadata {
|
|||
Dart_Port send_port,
|
||||
Metadata** list_head);
|
||||
|
||||
// Creates an isolate local callback trampoline for the given function.
|
||||
Trampoline CreateIsolateLocalFfiCallback(Isolate* isolate,
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
const Closure& closure,
|
||||
Metadata** list_head);
|
||||
|
||||
// Deletes a single trampoline.
|
||||
void DeleteCallback(Trampoline trampoline, Metadata** list_head);
|
||||
|
||||
|
@ -87,7 +88,9 @@ class FfiCallbackMetadata {
|
|||
// safe because Instructions objects are never moved by the GC.
|
||||
uword target_entry_point_;
|
||||
|
||||
Dart_Port send_port_;
|
||||
// For async callbacks, this is the send port. For sync callbacks this
|
||||
// is a persistent handle to the callback's closure, or null.
|
||||
uint64_t context_;
|
||||
|
||||
// Links in the Isolate's list of callbacks.
|
||||
Metadata* list_prev_;
|
||||
|
@ -101,13 +104,13 @@ class FfiCallbackMetadata {
|
|||
Metadata(Isolate* target_isolate,
|
||||
TrampolineType trampoline_type,
|
||||
uword target_entry_point,
|
||||
Dart_Port send_port,
|
||||
uint64_t context,
|
||||
Metadata* list_prev,
|
||||
Metadata* list_next)
|
||||
: target_isolate_(target_isolate),
|
||||
trampoline_type_(trampoline_type),
|
||||
target_entry_point_(target_entry_point),
|
||||
send_port_(send_port),
|
||||
context_(context),
|
||||
list_prev_(list_prev),
|
||||
list_next_(list_next) {}
|
||||
|
||||
|
@ -119,7 +122,7 @@ class FfiCallbackMetadata {
|
|||
return target_isolate_ == other.target_isolate_ &&
|
||||
trampoline_type_ == other.trampoline_type_ &&
|
||||
target_entry_point_ == other.target_entry_point_ &&
|
||||
send_port_ == other.send_port_;
|
||||
context_ == other.context_;
|
||||
}
|
||||
|
||||
// Whether the callback is still alive.
|
||||
|
@ -139,10 +142,27 @@ class FfiCallbackMetadata {
|
|||
return target_entry_point_;
|
||||
}
|
||||
|
||||
// The persistent handle to the closure that the NativeCallable.isolateLocal
|
||||
// is wrapping.
|
||||
PersistentHandle* closure_handle() const {
|
||||
ASSERT(IsLive());
|
||||
ASSERT(trampoline_type_ == TrampolineType::kSync ||
|
||||
trampoline_type_ == TrampolineType::kSyncStackDelta4);
|
||||
return reinterpret_cast<PersistentHandle*>(context_);
|
||||
}
|
||||
|
||||
// For async callbacks, this is the send port. For sync callbacks this is a
|
||||
// persistent handle to the callback's closure, or null.
|
||||
uint64_t context() const {
|
||||
ASSERT(IsLive());
|
||||
return context_;
|
||||
}
|
||||
|
||||
// The send port that the async callback will send a message to.
|
||||
Dart_Port send_port() const {
|
||||
ASSERT(IsLive());
|
||||
return send_port_;
|
||||
ASSERT(trampoline_type_ == TrampolineType::kAsync);
|
||||
return static_cast<Dart_Port>(context_);
|
||||
}
|
||||
|
||||
// To efficiently delete all the callbacks for a isolate, they are stored in
|
||||
|
@ -265,16 +285,24 @@ class FfiCallbackMetadata {
|
|||
~FfiCallbackMetadata();
|
||||
void EnsureStubPageLocked();
|
||||
void AddToFreeListLocked(Metadata* entry);
|
||||
void DeleteCallbackLocked(Metadata* entry);
|
||||
void FillRuntimeFunction(VirtualMemory* page, uword index, void* function);
|
||||
VirtualMemory* AllocateTrampolinePage();
|
||||
void EnsureFreeListNotEmptyLocked();
|
||||
Trampoline CreateMetadataEntry(Isolate* target_isolate,
|
||||
TrampolineType trampoline_type,
|
||||
uword target_entry_point,
|
||||
Dart_Port send_port,
|
||||
uint64_t context,
|
||||
Metadata** list_head);
|
||||
Trampoline CreateSyncFfiCallbackImpl(Isolate* isolate,
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
PersistentHandle* closure,
|
||||
Metadata** list_head);
|
||||
Trampoline TryAllocateFromFreeListLocked();
|
||||
static uword GetEntryPoint(Zone* zone, const Function& function);
|
||||
static PersistentHandle* CreatePersistentHandle(Isolate* isolate,
|
||||
const Closure& closure);
|
||||
|
||||
static FfiCallbackMetadata* singleton_;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
FunctionPtr CreateTestFunction(FfiTrampolineKind kind) {
|
||||
FunctionPtr CreateTestFunction(FfiFunctionKind kind) {
|
||||
const auto& ffi_lib = Library::Handle(Library::FfiLibrary());
|
||||
const auto& ffi_void = Class::Handle(ffi_lib.LookupClass(Symbols::FfiVoid()));
|
||||
const auto& ffi_void_type =
|
||||
|
@ -91,12 +91,13 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateSyncFfiCallback) {
|
|||
|
||||
auto* zone = thread->zone();
|
||||
|
||||
const auto& func =
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kSyncCallback));
|
||||
const auto& func = Function::Handle(
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const auto& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
tramp1 = isolate->CreateSyncFfiCallback(zone, func);
|
||||
tramp1 = isolate->CreateIsolateLocalFfiCallback(
|
||||
zone, func, Closure::Handle(Closure::null()), false);
|
||||
EXPECT_NE(tramp1, 0u);
|
||||
|
||||
{
|
||||
|
@ -105,7 +106,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateSyncFfiCallback) {
|
|||
EXPECT(m1.IsLive());
|
||||
EXPECT_EQ(m1.target_isolate(), isolate);
|
||||
EXPECT_EQ(m1.target_entry_point(), code.EntryPoint());
|
||||
EXPECT_EQ(m1.send_port(), ILLEGAL_PORT);
|
||||
EXPECT_EQ(m1.closure_handle(), nullptr);
|
||||
EXPECT_EQ(static_cast<int>(m1.trampoline_type()),
|
||||
static_cast<int>(FfiCallbackMetadata::TrampolineType::kSync));
|
||||
|
||||
|
@ -116,7 +117,8 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateSyncFfiCallback) {
|
|||
EXPECT_EQ(e1->list_next(), nullptr);
|
||||
}
|
||||
|
||||
tramp2 = isolate->CreateSyncFfiCallback(zone, func);
|
||||
tramp2 = isolate->CreateIsolateLocalFfiCallback(
|
||||
zone, func, Closure::Handle(Closure::null()), false);
|
||||
EXPECT_NE(tramp2, 0u);
|
||||
EXPECT_NE(tramp2, tramp1);
|
||||
|
||||
|
@ -126,7 +128,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateSyncFfiCallback) {
|
|||
EXPECT(m2.IsLive());
|
||||
EXPECT_EQ(m2.target_isolate(), isolate);
|
||||
EXPECT_EQ(m2.target_entry_point(), code.EntryPoint());
|
||||
EXPECT_EQ(m2.send_port(), ILLEGAL_PORT);
|
||||
EXPECT_EQ(m2.closure_handle(), nullptr);
|
||||
EXPECT_EQ(static_cast<int>(m2.trampoline_type()),
|
||||
static_cast<int>(FfiCallbackMetadata::TrampolineType::kSync));
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateAsyncFfiCallback) {
|
|||
auto* zone = thread->zone();
|
||||
|
||||
const Function& func =
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kAsyncCallback));
|
||||
Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -261,13 +263,117 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateAsyncFfiCallback) {
|
|||
}
|
||||
}
|
||||
|
||||
VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateIsolateLocalFfiCallback) {
|
||||
auto* fcm = FfiCallbackMetadata::Instance();
|
||||
FfiCallbackMetadata::Trampoline tramp1 = 0;
|
||||
FfiCallbackMetadata::Trampoline tramp2 = 0;
|
||||
|
||||
{
|
||||
TestIsolateScope isolate_scope;
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(thread->isolate() == isolate_scope.isolate());
|
||||
TransitionNativeToVM transition(thread);
|
||||
StackZone stack_zone(thread);
|
||||
HandleScope handle_scope(thread);
|
||||
|
||||
auto* zone = thread->zone();
|
||||
|
||||
const Function& func = Function::Handle(
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalClosureCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
// Using a FfiFunctionKind::kSync function as a dummy closure.
|
||||
const Function& closure_func = Function::Handle(
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const Context& context = Context::Handle(Context::null());
|
||||
const Closure& closure1 = Closure::Handle(
|
||||
Closure::New(Object::null_type_arguments(),
|
||||
Object::null_type_arguments(), closure_func, context));
|
||||
|
||||
EXPECT_EQ(isolate->ffi_callback_list_head(), nullptr);
|
||||
|
||||
tramp1 = isolate->CreateIsolateLocalFfiCallback(zone, func, closure1, true);
|
||||
EXPECT_NE(tramp1, 0u);
|
||||
|
||||
{
|
||||
FfiCallbackMetadata::Metadata m1 =
|
||||
fcm->LookupMetadataForTrampoline(tramp1);
|
||||
EXPECT(m1.IsLive());
|
||||
EXPECT_EQ(m1.target_isolate(), isolate);
|
||||
EXPECT_EQ(m1.target_entry_point(), code.EntryPoint());
|
||||
EXPECT_EQ(m1.closure_handle()->ptr(), closure1.ptr());
|
||||
EXPECT_EQ(static_cast<int>(m1.trampoline_type()),
|
||||
static_cast<int>(FfiCallbackMetadata::TrampolineType::kSync));
|
||||
|
||||
// head -> tramp1
|
||||
auto* e1 = fcm->MetadataOfTrampoline(tramp1);
|
||||
EXPECT_EQ(isolate->ffi_callback_list_head(), e1);
|
||||
EXPECT_EQ(e1->list_prev(), nullptr);
|
||||
EXPECT_EQ(e1->list_next(), nullptr);
|
||||
}
|
||||
|
||||
const Closure& closure2 = Closure::Handle(
|
||||
Closure::New(Object::null_type_arguments(),
|
||||
Object::null_type_arguments(), closure_func, context));
|
||||
tramp2 = isolate->CreateIsolateLocalFfiCallback(zone, func, closure2, true);
|
||||
EXPECT_NE(tramp2, 0u);
|
||||
EXPECT_NE(tramp2, tramp1);
|
||||
|
||||
{
|
||||
FfiCallbackMetadata::Metadata m2 =
|
||||
fcm->LookupMetadataForTrampoline(tramp2);
|
||||
EXPECT(m2.IsLive());
|
||||
EXPECT_EQ(m2.target_isolate(), isolate);
|
||||
EXPECT_EQ(m2.target_entry_point(), code.EntryPoint());
|
||||
EXPECT_EQ(m2.closure_handle()->ptr(), closure2.ptr());
|
||||
EXPECT_EQ(static_cast<int>(m2.trampoline_type()),
|
||||
static_cast<int>(FfiCallbackMetadata::TrampolineType::kSync));
|
||||
}
|
||||
|
||||
{
|
||||
// head -> tramp2 -> tramp1
|
||||
auto* e1 = fcm->MetadataOfTrampoline(tramp1);
|
||||
auto* e2 = fcm->MetadataOfTrampoline(tramp2);
|
||||
EXPECT_EQ(isolate->ffi_callback_list_head(), e2);
|
||||
EXPECT_EQ(e2->list_prev(), nullptr);
|
||||
EXPECT_EQ(e2->list_next(), e1);
|
||||
EXPECT_EQ(e1->list_prev(), e2);
|
||||
EXPECT_EQ(e1->list_next(), nullptr);
|
||||
}
|
||||
|
||||
{
|
||||
isolate->DeleteFfiCallback(tramp2);
|
||||
FfiCallbackMetadata::Metadata m2 =
|
||||
fcm->LookupMetadataForTrampoline(tramp2);
|
||||
EXPECT(!m2.IsLive());
|
||||
|
||||
// head -> tramp1
|
||||
auto* e1 = fcm->MetadataOfTrampoline(tramp1);
|
||||
EXPECT_EQ(isolate->ffi_callback_list_head(), e1);
|
||||
EXPECT_EQ(e1->list_prev(), nullptr);
|
||||
EXPECT_EQ(e1->list_next(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Isolate has shut down, so all callbacks should be deleted.
|
||||
FfiCallbackMetadata::Metadata m1 = fcm->LookupMetadataForTrampoline(tramp1);
|
||||
EXPECT(!m1.IsLive());
|
||||
|
||||
FfiCallbackMetadata::Metadata m2 = fcm->LookupMetadataForTrampoline(tramp2);
|
||||
EXPECT(!m2.IsLive());
|
||||
}
|
||||
}
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(FfiCallbackMetadata_TrampolineRecycling) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
auto* zone = thread->zone();
|
||||
auto* fcm = FfiCallbackMetadata::Instance();
|
||||
|
||||
const Function& func =
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kAsyncCallback));
|
||||
Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -333,15 +439,16 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_DeleteTrampolines) {
|
|||
std::unordered_set<FfiCallbackMetadata::Trampoline> tramps;
|
||||
FfiCallbackMetadata::Metadata* list_head = nullptr;
|
||||
|
||||
const auto& sync_func =
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kSyncCallback));
|
||||
const auto& sync_func = Function::Handle(
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
|
||||
EXPECT(!sync_code.IsNull());
|
||||
|
||||
// Create some callbacks.
|
||||
for (int itr = 0; itr < kCreations; ++itr) {
|
||||
tramps.insert(fcm->CreateSyncFfiCallback(isolate, thread->zone(), sync_func,
|
||||
&list_head));
|
||||
tramps.insert(fcm->CreateIsolateLocalFfiCallback(
|
||||
isolate, thread->zone(), sync_func, Closure::Handle(Closure::null()),
|
||||
&list_head));
|
||||
}
|
||||
|
||||
// Delete some of the callbacks.
|
||||
|
@ -414,11 +521,11 @@ static void RunBigRandomMultithreadedTest(uint64_t seed) {
|
|||
FfiCallbackMetadata::Metadata* list_head = nullptr;
|
||||
|
||||
const Function& async_func =
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kAsyncCallback));
|
||||
Function::Handle(CreateTestFunction(FfiFunctionKind::kAsyncCallback));
|
||||
const Code& async_code = Code::Handle(async_func.EnsureHasCode());
|
||||
EXPECT(!async_code.IsNull());
|
||||
const Function& sync_func =
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kSyncCallback));
|
||||
const Function& sync_func = Function::Handle(
|
||||
CreateTestFunction(FfiFunctionKind::kIsolateLocalStaticCallback));
|
||||
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
|
||||
EXPECT(!sync_code.IsNull());
|
||||
|
||||
|
@ -452,8 +559,9 @@ static void RunBigRandomMultithreadedTest(uint64_t seed) {
|
|||
if ((random.NextUInt32() % 2) == 0) {
|
||||
// 50% chance of creating a sync callback.
|
||||
tramp.port = ILLEGAL_PORT;
|
||||
tramp.tramp = fcm->CreateSyncFfiCallback(isolate, thread->zone(),
|
||||
sync_func, &list_head);
|
||||
tramp.tramp = fcm->CreateIsolateLocalFfiCallback(
|
||||
isolate, thread->zone(), sync_func,
|
||||
Closure::Handle(Closure::null()), &list_head);
|
||||
} else {
|
||||
// 50% chance of creating an async callback.
|
||||
tramp.port = PortMap::CreatePort(new FakeMessageHandler());
|
||||
|
@ -469,11 +577,12 @@ static void RunBigRandomMultithreadedTest(uint64_t seed) {
|
|||
auto metadata = fcm->LookupMetadataForTrampoline(tramp.tramp);
|
||||
EXPECT(metadata.IsLive());
|
||||
EXPECT_EQ(metadata.target_isolate(), isolate);
|
||||
EXPECT_EQ(metadata.send_port(), tramp.port);
|
||||
if (metadata.trampoline_type() ==
|
||||
FfiCallbackMetadata::TrampolineType::kSync) {
|
||||
EXPECT_EQ(metadata.closure_handle(), nullptr);
|
||||
EXPECT_EQ(metadata.target_entry_point(), sync_code.EntryPoint());
|
||||
} else {
|
||||
EXPECT_EQ(metadata.send_port(), tramp.port);
|
||||
EXPECT_EQ(metadata.target_entry_point(), async_code.EntryPoint());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1029,8 +1029,9 @@ class IsolateMessageHandler : public MessageHandler {
|
|||
virtual bool KeepAliveLocked() {
|
||||
// If the message handler was asked to shutdown we shut down.
|
||||
if (!MessageHandler::KeepAliveLocked()) return false;
|
||||
// Otherwise we only stay alive as long as there's active receive ports.
|
||||
return isolate_->HasLivePorts();
|
||||
// Otherwise we only stay alive as long as there's active receive ports, or
|
||||
// there are FFI callbacks keeping the isolate alive.
|
||||
return isolate_->HasLivePorts() || isolate_->HasOpenNativeCallables();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -3617,13 +3618,6 @@ void Isolate::WaitForOutstandingSpawns() {
|
|||
}
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline Isolate::CreateSyncFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& function) {
|
||||
return FfiCallbackMetadata::Instance()->CreateSyncFfiCallback(
|
||||
this, zone, function, &ffi_callback_list_head_);
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline Isolate::CreateAsyncFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& send_function,
|
||||
|
@ -3632,6 +3626,18 @@ FfiCallbackMetadata::Trampoline Isolate::CreateAsyncFfiCallback(
|
|||
this, zone, send_function, send_port, &ffi_callback_list_head_);
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline Isolate::CreateIsolateLocalFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& trampoline,
|
||||
const Closure& target,
|
||||
bool keep_isolate_alive) {
|
||||
if (keep_isolate_alive) {
|
||||
UpdateNativeCallableKeepIsolateAliveCounter(1);
|
||||
}
|
||||
return FfiCallbackMetadata::Instance()->CreateIsolateLocalFfiCallback(
|
||||
this, zone, trampoline, target, &ffi_callback_list_head_);
|
||||
}
|
||||
|
||||
bool Isolate::HasLivePorts() {
|
||||
ASSERT(0 <= open_ports_ && 0 <= open_ports_keepalive_ &&
|
||||
open_ports_keepalive_ <= open_ports_);
|
||||
|
@ -3686,6 +3692,16 @@ void Isolate::DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback) {
|
|||
&ffi_callback_list_head_);
|
||||
}
|
||||
|
||||
void Isolate::UpdateNativeCallableKeepIsolateAliveCounter(intptr_t delta) {
|
||||
ffi_callback_keep_alive_counter_ += delta;
|
||||
ASSERT(ffi_callback_keep_alive_counter_ >= 0);
|
||||
}
|
||||
|
||||
bool Isolate::HasOpenNativeCallables() {
|
||||
ASSERT(ffi_callback_keep_alive_counter_ >= 0);
|
||||
return ffi_callback_keep_alive_counter_ > 0;
|
||||
}
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
void IsolateGroup::CloneClassTableForReload() {
|
||||
RELEASE_ASSERT(class_table_ == heap_walk_class_table_);
|
||||
|
|
|
@ -1241,14 +1241,18 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
deopt_context_ = value;
|
||||
}
|
||||
|
||||
FfiCallbackMetadata::Trampoline CreateSyncFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& function);
|
||||
FfiCallbackMetadata::Trampoline CreateAsyncFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& send_function,
|
||||
Dart_Port send_port);
|
||||
FfiCallbackMetadata::Trampoline CreateIsolateLocalFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& trampoline,
|
||||
const Closure& target,
|
||||
bool keep_isolate_alive);
|
||||
void DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback);
|
||||
void UpdateNativeCallableKeepIsolateAliveCounter(intptr_t delta);
|
||||
bool HasOpenNativeCallables();
|
||||
|
||||
bool HasLivePorts();
|
||||
ReceivePortPtr CreateReceivePort(const String& debug_name);
|
||||
|
@ -1649,6 +1653,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
intptr_t defer_finalization_count_ = 0;
|
||||
DeoptContext* deopt_context_ = nullptr;
|
||||
FfiCallbackMetadata::Metadata* ffi_callback_list_head_ = nullptr;
|
||||
intptr_t ffi_callback_keep_alive_counter_ = 0;
|
||||
|
||||
GrowableObjectArrayPtr tag_table_;
|
||||
|
||||
|
|
|
@ -8429,7 +8429,7 @@ bool Function::FfiCSignatureReturnsStruct() const {
|
|||
|
||||
int32_t Function::FfiCallbackId() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
ASSERT(GetFfiTrampolineKind() != FfiTrampolineKind::kCall);
|
||||
ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
|
||||
|
||||
const auto& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
|
@ -8442,7 +8442,7 @@ int32_t Function::FfiCallbackId() const {
|
|||
|
||||
void Function::AssignFfiCallbackId(int32_t callback_id) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
ASSERT(GetFfiTrampolineKind() != FfiTrampolineKind::kCall);
|
||||
ASSERT(GetFfiFunctionKind() != FfiFunctionKind::kCall);
|
||||
|
||||
const auto& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
|
@ -8494,18 +8494,18 @@ void Function::SetFfiCallbackExceptionalReturn(const Instance& value) const {
|
|||
FfiTrampolineData::Cast(obj).set_callback_exceptional_return(value);
|
||||
}
|
||||
|
||||
FfiTrampolineKind Function::GetFfiTrampolineKind() const {
|
||||
FfiFunctionKind Function::GetFfiFunctionKind() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).trampoline_kind();
|
||||
return FfiTrampolineData::Cast(obj).ffi_function_kind();
|
||||
}
|
||||
|
||||
void Function::SetFfiTrampolineKind(FfiTrampolineKind value) const {
|
||||
void Function::SetFfiFunctionKind(FfiFunctionKind value) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_trampoline_kind(value);
|
||||
FfiTrampolineData::Cast(obj).set_ffi_function_kind(value);
|
||||
}
|
||||
|
||||
const char* Function::KindToCString(UntaggedFunction::Kind kind) {
|
||||
|
@ -11632,8 +11632,8 @@ void FfiTrampolineData::set_callback_exceptional_return(
|
|||
untag()->set_callback_exceptional_return(value.ptr());
|
||||
}
|
||||
|
||||
void FfiTrampolineData::set_trampoline_kind(FfiTrampolineKind kind) const {
|
||||
StoreNonPointer(&untag()->trampoline_kind_, static_cast<uint8_t>(kind));
|
||||
void FfiTrampolineData::set_ffi_function_kind(FfiFunctionKind kind) const {
|
||||
StoreNonPointer(&untag()->ffi_function_kind_, static_cast<uint8_t>(kind));
|
||||
}
|
||||
|
||||
FfiTrampolineDataPtr FfiTrampolineData::New() {
|
||||
|
|
|
@ -2949,9 +2949,10 @@ struct NameFormattingParams {
|
|||
}
|
||||
};
|
||||
|
||||
enum class FfiTrampolineKind : uint8_t {
|
||||
enum class FfiFunctionKind : uint8_t {
|
||||
kCall,
|
||||
kSyncCallback,
|
||||
kIsolateLocalStaticCallback,
|
||||
kIsolateLocalClosureCallback,
|
||||
kAsyncCallback,
|
||||
};
|
||||
|
||||
|
@ -3013,10 +3014,10 @@ class Function : public Object {
|
|||
void SetFfiCallbackExceptionalReturn(const Instance& value) const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
FfiTrampolineKind GetFfiTrampolineKind() const;
|
||||
FfiFunctionKind GetFfiFunctionKind() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
void SetFfiTrampolineKind(FfiTrampolineKind value) const;
|
||||
void SetFfiFunctionKind(FfiFunctionKind value) const;
|
||||
|
||||
// Return the signature of this function.
|
||||
PRECOMPILER_WSR_FIELD_DECLARATION(FunctionType, signature);
|
||||
|
@ -4355,10 +4356,10 @@ class FfiTrampolineData : public Object {
|
|||
}
|
||||
void set_callback_exceptional_return(const Instance& value) const;
|
||||
|
||||
FfiTrampolineKind trampoline_kind() const {
|
||||
return static_cast<FfiTrampolineKind>(untag()->trampoline_kind_);
|
||||
FfiFunctionKind ffi_function_kind() const {
|
||||
return static_cast<FfiFunctionKind>(untag()->ffi_function_kind_);
|
||||
}
|
||||
void set_trampoline_kind(FfiTrampolineKind kind) const;
|
||||
void set_ffi_function_kind(FfiFunctionKind kind) const;
|
||||
|
||||
int32_t callback_id() const { return untag()->callback_id_; }
|
||||
void set_callback_id(int32_t value) const;
|
||||
|
|
|
@ -1506,8 +1506,8 @@ class UntaggedFfiTrampolineData : public UntaggedObject {
|
|||
// Whether this is a leaf call - i.e. one that doesn't call back into Dart.
|
||||
bool is_leaf_;
|
||||
|
||||
// The kind of trampoline this is. See FfiTrampolineKind.
|
||||
uint8_t trampoline_kind_;
|
||||
// The kind of trampoline this is. See FfiFunctionKind.
|
||||
uint8_t ffi_function_kind_;
|
||||
};
|
||||
|
||||
class UntaggedField : public UntaggedObject {
|
||||
|
|
|
@ -4094,6 +4094,8 @@ extern "C" Thread* DLRT_GetFfiCallbackMetadata(
|
|||
|
||||
current_thread->ExitSafepoint();
|
||||
|
||||
current_thread->set_unboxed_int64_runtime_arg(metadata.context());
|
||||
|
||||
TRACE_RUNTIME_CALL("GetFfiCallbackMetadata thread %p", current_thread);
|
||||
TRACE_RUNTIME_CALL("GetFfiCallbackMetadata entry_point %p",
|
||||
(void*)*out_entry_point);
|
||||
|
|
|
@ -115,11 +115,11 @@ class ObjectPointerVisitor;
|
|||
V(FfiInt64, "Int64") \
|
||||
V(FfiInt8, "Int8") \
|
||||
V(FfiIntPtr, "IntPtr") \
|
||||
V(FfiIsolateLocalCallback, "_FfiIsolateLocalCallback") \
|
||||
V(FfiNativeFunction, "NativeFunction") \
|
||||
V(FfiNativeType, "NativeType") \
|
||||
V(FfiNativeTypes, "nativeTypes") \
|
||||
V(FfiPointer, "Pointer") \
|
||||
V(FfiPointerAsyncFromFunctionSendArgs, "_pointerAsyncFromFunctionSendArgs") \
|
||||
V(FfiStructLayout, "_FfiStructLayout") \
|
||||
V(FfiStructLayoutArray, "_FfiInlineArray") \
|
||||
V(FfiTrampolineData, "FfiTrampolineData") \
|
||||
|
|
|
@ -152,22 +152,33 @@ external Float64List _asExternalTypedDataDouble(
|
|||
external dynamic _nativeCallbackFunction<NS extends Function>(
|
||||
Function target, Object? exceptionalReturn);
|
||||
|
||||
@pragma("vm:external-name", "Ffi_pointerFromFunction")
|
||||
external Pointer<NS> _pointerFromFunction<NS extends NativeFunction>(
|
||||
dynamic function);
|
||||
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:external-name", "Ffi_nativeAsyncCallbackFunction")
|
||||
external dynamic _nativeAsyncCallbackFunction<NS extends Function>();
|
||||
|
||||
@pragma("vm:external-name", "Ffi_pointerAsyncFromFunction")
|
||||
external Pointer<NS> _pointerAsyncFromFunction<NS extends NativeFunction>(
|
||||
@pragma("vm:external-name", "Ffi_createNativeCallableListener")
|
||||
external Pointer<NS> _createNativeCallableListener<NS extends NativeFunction>(
|
||||
dynamic function, RawReceivePort port);
|
||||
|
||||
@pragma("vm:external-name", "Ffi_deleteAsyncFunctionPointer")
|
||||
external void _deleteAsyncFunctionPointer<NS extends NativeFunction>(
|
||||
@pragma("vm:external-name", "Ffi_createNativeCallableIsolateLocal")
|
||||
external Pointer<NS>
|
||||
_createNativeCallableIsolateLocal<NS extends NativeFunction>(
|
||||
dynamic trampoline, dynamic target, bool keepIsolateAlive);
|
||||
|
||||
@pragma("vm:external-name", "Ffi_deleteNativeCallable")
|
||||
external void _deleteNativeCallable<NS extends NativeFunction>(
|
||||
Pointer<NS> pointer);
|
||||
|
||||
@pragma("vm:external-name", "Ffi_updateNativeCallableKeepIsolateAliveCounter")
|
||||
external void
|
||||
_updateNativeCallableKeepIsolateAliveCounter<NS extends NativeFunction>(
|
||||
int delta);
|
||||
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:external-name", "Ffi_nativeIsolateLocalCallbackFunction")
|
||||
external dynamic _nativeIsolateLocalCallbackFunction<NS extends Function>(
|
||||
dynamic exceptionalReturn);
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
final class Pointer<T extends NativeType> {
|
||||
|
@ -199,40 +210,77 @@ final class Pointer<T extends NativeType> {
|
|||
Pointer<U> cast<U extends NativeType>() => Pointer.fromAddress(address);
|
||||
}
|
||||
|
||||
@patch
|
||||
final class NativeCallable<T extends Function> {
|
||||
Pointer<NativeFunction<T>> _pointer = nullptr;
|
||||
final RawReceivePort _port;
|
||||
abstract final class _NativeCallableBase<T extends Function>
|
||||
implements NativeCallable<T> {
|
||||
Pointer<NativeFunction<T>> _pointer;
|
||||
|
||||
@patch
|
||||
NativeCallable.listener(@DartRepresentationOf("T") Function callback)
|
||||
: _port = RawReceivePort()..close() {
|
||||
throw UnsupportedError("NativeCallable cannot be constructed dynamically.");
|
||||
}
|
||||
_NativeCallableBase(this._pointer);
|
||||
|
||||
NativeCallable._(void Function(List) handler, String portDebugName)
|
||||
: _port = RawReceivePort(
|
||||
Zone.current.bindUnaryCallbackGuarded(handler), portDebugName);
|
||||
|
||||
@patch
|
||||
@override
|
||||
Pointer<NativeFunction<T>> get nativeFunction => _pointer;
|
||||
|
||||
@patch
|
||||
@override
|
||||
void close() {
|
||||
if (_pointer == nullptr) {
|
||||
throw StateError("NativeCallable is already closed.");
|
||||
}
|
||||
_port.close();
|
||||
_deleteAsyncFunctionPointer(_pointer);
|
||||
_deleteNativeCallable(_pointer);
|
||||
_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
final class _NativeCallableIsolateLocal<T extends Function>
|
||||
extends _NativeCallableBase<T> {
|
||||
bool _keepIsolateAlive = true;
|
||||
|
||||
_NativeCallableIsolateLocal(super._pointer);
|
||||
|
||||
@override
|
||||
void close() {
|
||||
super.close();
|
||||
_setKeepIsolateAlive(false);
|
||||
}
|
||||
|
||||
@override
|
||||
void set keepIsolateAlive(bool value) {
|
||||
if (_pointer == nullptr) {
|
||||
throw StateError("NativeCallable is already closed.");
|
||||
}
|
||||
_setKeepIsolateAlive(value);
|
||||
}
|
||||
|
||||
void _setKeepIsolateAlive(bool value) {
|
||||
if (_keepIsolateAlive != value) {
|
||||
_keepIsolateAlive = value;
|
||||
_updateNativeCallableKeepIsolateAliveCounter(value ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get keepIsolateAlive => _keepIsolateAlive;
|
||||
}
|
||||
|
||||
final class _NativeCallableListener<T extends Function>
|
||||
extends _NativeCallableBase<T> {
|
||||
final RawReceivePort _port;
|
||||
|
||||
_NativeCallableListener(void Function(List) handler, String portDebugName)
|
||||
: _port = RawReceivePort(
|
||||
Zone.current.bindUnaryCallbackGuarded(handler), portDebugName),
|
||||
super(nullptr);
|
||||
|
||||
@override
|
||||
void close() {
|
||||
super.close();
|
||||
_port.close();
|
||||
}
|
||||
|
||||
@override
|
||||
void set keepIsolateAlive(bool value) {
|
||||
_port.keepIsolateAlive = value;
|
||||
}
|
||||
|
||||
@patch
|
||||
@override
|
||||
bool get keepIsolateAlive => _port.keepIsolateAlive;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,9 @@ final class Pointer<T extends NativeType> extends NativeType {
|
|||
/// isolate's lifetime. After the isolate it was created in is terminated,
|
||||
/// invoking it from native code will cause undefined behavior.
|
||||
///
|
||||
/// Does not accept dynamic invocations -- where the type of the receiver is
|
||||
/// [dynamic].
|
||||
/// [Pointer.fromFunction] only accepts static or top level functions. Use
|
||||
/// [NativeCallable.isolateLocal] to create callbacks from any Dart function
|
||||
/// or closure.
|
||||
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||
@DartRepresentationOf('T') Function f,
|
||||
[Object? exceptionalReturn]);
|
||||
|
@ -171,7 +172,32 @@ extension NativeFunctionPointer<NF extends Function>
|
|||
/// native function will call the Dart function in some way, with the arguments
|
||||
/// converted to Dart values.
|
||||
@Since('3.1')
|
||||
final class NativeCallable<T extends Function> {
|
||||
abstract final class NativeCallable<T extends Function> {
|
||||
/// Constructs a [NativeCallable] that must be invoked from the same thread
|
||||
/// that created it.
|
||||
///
|
||||
/// If an exception is thrown by the [callback], the native function will
|
||||
/// return the `exceptionalReturn`, which must be assignable to the return
|
||||
/// type of the [callback].
|
||||
///
|
||||
/// The returned function address can only be invoked on the mutator (main)
|
||||
/// thread of the current isolate. It will abort the process if invoked on any
|
||||
/// other thread. Use [NativeCallable.listener] to create callbacks that can
|
||||
/// be invoked from any thread.
|
||||
///
|
||||
/// Unlike [Pointer.fromFunction], [NativeCallable]s can be constructed from
|
||||
/// any Dart function or closure, not just static or top level functions.
|
||||
///
|
||||
/// This callback must be [close]d when it is no longer needed, but it will
|
||||
/// *not* keep its [Isolate] alive. After the isolate is terminated, or
|
||||
/// [NativeCallable.close] is called, invoking the [nativeFunction] from
|
||||
/// native code will cause undefined behavior.
|
||||
factory NativeCallable.isolateLocal(
|
||||
@DartRepresentationOf("T") Function callback,
|
||||
{Object? exceptionalReturn}) {
|
||||
throw UnsupportedError("NativeCallable cannot be constructed dynamically.");
|
||||
}
|
||||
|
||||
/// Constructs a [NativeCallable] that can be invoked from any thread.
|
||||
///
|
||||
/// When the native code invokes the function [nativeFunction], the arguments
|
||||
|
@ -188,14 +214,16 @@ final class NativeCallable<T extends Function> {
|
|||
///
|
||||
/// This callback must be [close]d when it is no longer needed. The [Isolate]
|
||||
/// that created the callback will be kept alive until [close] is called.
|
||||
external NativeCallable.listener(
|
||||
@DartRepresentationOf("T") Function callback);
|
||||
factory NativeCallable.listener(
|
||||
@DartRepresentationOf("T") Function callback) {
|
||||
throw UnsupportedError("NativeCallable cannot be constructed dynamically.");
|
||||
}
|
||||
|
||||
/// The native function pointer which can be used to invoke the `callback`
|
||||
/// passed to the constructor.
|
||||
///
|
||||
/// If this receiver has been [close]d, the pointer is a [nullptr].
|
||||
external Pointer<NativeFunction<T>> get nativeFunction;
|
||||
Pointer<NativeFunction<T>> get nativeFunction;
|
||||
|
||||
/// Closes this callback and releases its resources.
|
||||
///
|
||||
|
@ -205,15 +233,14 @@ final class NativeCallable<T extends Function> {
|
|||
/// This method must not be called more than once on each native callback.
|
||||
///
|
||||
/// It is safe to call [close] inside the [callback].
|
||||
external void close();
|
||||
void close();
|
||||
|
||||
/// Whether this [NativeCallable] keeps its [Isolate] alive.
|
||||
///
|
||||
/// By default, [NativeCallable]s keep the [Isolate] that created them alive
|
||||
/// until [close] is called. If [keepIsolateAlive] is set to `false`, the
|
||||
/// isolate may close while the port is still open.
|
||||
external bool get keepIsolateAlive;
|
||||
external void set keepIsolateAlive(bool value);
|
||||
/// isolate may exit even if the [NativeCallable] isn't closed.
|
||||
external bool keepIsolateAlive;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -18,7 +18,7 @@ class AsyncCallbackTest {
|
|||
final Future<void> Function() afterCallbackChecks;
|
||||
|
||||
// Either a NativeCallable or a Pointer.fromFunction.
|
||||
final dynamic callback;
|
||||
final Object callback;
|
||||
|
||||
AsyncCallbackTest(this.name, this.callback, this.afterCallbackChecks) {}
|
||||
|
||||
|
@ -26,11 +26,12 @@ class AsyncCallbackTest {
|
|||
final NativeAsyncCallbackTestFn tester = ffiTestFunctions.lookupFunction<
|
||||
NativeAsyncCallbackTest, NativeAsyncCallbackTestFn>("TestAsync$name");
|
||||
|
||||
tester(callback is NativeCallable ? callback.nativeFunction : callback);
|
||||
final cb = callback;
|
||||
tester(cb is NativeCallable ? cb.nativeFunction : cb as Pointer);
|
||||
|
||||
await afterCallbackChecks();
|
||||
if (callback is NativeCallable) {
|
||||
callback.close();
|
||||
if (cb is NativeCallable) {
|
||||
cb.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,12 @@ typedef NativeCallbackTestFn = int Function(Pointer);
|
|||
|
||||
class CallbackTest {
|
||||
final String name;
|
||||
final Pointer callback;
|
||||
final void Function() afterCallbackChecks;
|
||||
final bool isLeaf;
|
||||
|
||||
// Either a NativeCallable or a Pointer.fromFunction.
|
||||
final Object callback;
|
||||
|
||||
CallbackTest(this.name, this.callback, {this.isLeaf = false})
|
||||
: afterCallbackChecks = noChecks {}
|
||||
CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks,
|
||||
|
@ -31,13 +33,18 @@ class CallbackTest {
|
|||
: ffiTestFunctions.lookupFunction<NativeCallbackTest,
|
||||
NativeCallbackTestFn>("Test$name", isLeaf: false);
|
||||
|
||||
final int testCode = tester(callback);
|
||||
final cb = callback;
|
||||
final int testCode =
|
||||
tester(cb is NativeCallable ? cb.nativeFunction : cb as Pointer);
|
||||
|
||||
if (testCode != 0) {
|
||||
Expect.fail("Test $name failed.");
|
||||
}
|
||||
|
||||
afterCallbackChecks();
|
||||
if (cb is NativeCallable) {
|
||||
cb.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -755,23 +755,28 @@ void ${dartName}AfterCallback() {
|
|||
""";
|
||||
}
|
||||
|
||||
String get dartCallbackTestConstructor {
|
||||
String dartCallbackTestConstructor({required bool isNativeCallable}) {
|
||||
String exceptionalPrefix = isNativeCallable ? "exceptionalReturn: " : "";
|
||||
String exceptionalReturn = "";
|
||||
if (returnValue is FundamentalType) {
|
||||
final returnValue_ = returnValue as FundamentalType;
|
||||
if (returnValue_.isFloatingPoint) {
|
||||
exceptionalReturn = ", 0.0";
|
||||
exceptionalReturn = ", ${exceptionalPrefix}0.0";
|
||||
} else if (returnValue_.isInteger) {
|
||||
exceptionalReturn = ", 0";
|
||||
exceptionalReturn = ", ${exceptionalPrefix}0";
|
||||
} else if (returnValue_.isBool) {
|
||||
exceptionalReturn = ", false";
|
||||
exceptionalReturn = ", ${exceptionalPrefix}false";
|
||||
} else {
|
||||
throw 'Unexpected type $returnValue_';
|
||||
}
|
||||
}
|
||||
final T = '${cName}Type';
|
||||
final constructor = isNativeCallable
|
||||
? 'NativeCallable<$T>.isolateLocal'
|
||||
: 'Pointer.fromFunction<$T>';
|
||||
return """
|
||||
CallbackTest.withCheck("$cName",
|
||||
Pointer.fromFunction<${cName}Type>($dartName$exceptionalReturn),
|
||||
$constructor($dartName$exceptionalReturn),
|
||||
${dartName}AfterCallback),
|
||||
""";
|
||||
}
|
||||
|
@ -1219,36 +1224,43 @@ Future<void> writeDartCallbackTest(
|
|||
List<FunctionType> functions, {
|
||||
required bool isVarArgs,
|
||||
}) async {
|
||||
await Future.wait([
|
||||
true,
|
||||
if (!isVarArgs) false,
|
||||
].map((isNnbd) async {
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
buffer.write(headerDartCallbackTest(
|
||||
isNnbd: isNnbd,
|
||||
copyrightYear: isVarArgs ? 2023 : 2020,
|
||||
));
|
||||
for (bool isNnbd in [true, if (!isVarArgs) false]) {
|
||||
for (bool isNativeCallable in [false, if (isNnbd) true]) {
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
buffer.write(headerDartCallbackTest(
|
||||
isNnbd: isNnbd,
|
||||
copyrightYear: isVarArgs || isNativeCallable ? 2023 : 2020,
|
||||
));
|
||||
|
||||
buffer.write("""
|
||||
final testCases = [
|
||||
${functions.map((e) => e.dartCallbackTestConstructor).join("\n")}
|
||||
];
|
||||
""");
|
||||
buffer.write("""
|
||||
final testCases = [
|
||||
${functions.map((e) => e.dartCallbackTestConstructor(isNativeCallable: isNativeCallable)).join("\n")}
|
||||
];
|
||||
""");
|
||||
|
||||
buffer.writeAll(functions.map((e) => e.dartCallbackCode(isNnbd: isNnbd)));
|
||||
buffer.writeAll(functions.map((e) => e.dartCallbackCode(isNnbd: isNnbd)));
|
||||
|
||||
final path = callbackTestPath(isNnbd: isNnbd, isVarArgs: isVarArgs);
|
||||
await File(path).writeAsString(buffer.toString());
|
||||
await runProcess(Platform.resolvedExecutable, ["format", path]);
|
||||
}));
|
||||
final path = callbackTestPath(
|
||||
isNnbd: isNnbd,
|
||||
isVarArgs: isVarArgs,
|
||||
isNativeCallable: isNativeCallable);
|
||||
await File(path).writeAsString(buffer.toString());
|
||||
await runProcess(Platform.resolvedExecutable, ["format", path]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String callbackTestPath({required bool isNnbd, required bool isVarArgs}) {
|
||||
String callbackTestPath({
|
||||
required bool isNnbd,
|
||||
required bool isVarArgs,
|
||||
required bool isNativeCallable,
|
||||
}) {
|
||||
final folder = isNnbd ? "ffi" : "ffi_2";
|
||||
final baseName = isVarArgs ? "varargs" : "structs_by_value";
|
||||
final natCall = isNativeCallable ? "_native_callable" : "";
|
||||
return Platform.script
|
||||
.resolve(
|
||||
"../../$folder/function_callbacks_${baseName}_generated_test.dart")
|
||||
"../../$folder/function_callbacks_${baseName}${natCall}_generated_test.dart")
|
||||
.toFilePath();
|
||||
}
|
||||
|
||||
|
|
211
tests/ffi/isolate_local_function_callbacks_test.dart
Normal file
211
tests/ffi/isolate_local_function_callbacks_test.dart
Normal file
|
@ -0,0 +1,211 @@
|
|||
// Copyright (c) 2023, 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.
|
||||
|
||||
// Dart test program for testing dart:ffi async callbacks.
|
||||
//
|
||||
// VMOptions=--stacktrace-every=100
|
||||
// VMOptions=--write-protect-code --no-dual-map-code
|
||||
// VMOptions=--write-protect-code --no-dual-map-code --stacktrace-every=100
|
||||
// VMOptions=--use-slow-path
|
||||
// VMOptions=--use-slow-path --stacktrace-every=100
|
||||
// VMOptions=--use-slow-path --write-protect-code --no-dual-map-code
|
||||
// VMOptions=--use-slow-path --write-protect-code --no-dual-map-code --stacktrace-every=100
|
||||
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
|
||||
// VMOptions=--test_il_serialization
|
||||
// VMOptions=--profiler
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
main(args, message) async {
|
||||
testNativeCallableStatic();
|
||||
testNativeCallableClosure();
|
||||
testNativeCallableDoubleCloseError();
|
||||
testNativeCallableNestedCloseCallStatic();
|
||||
testNativeCallableNestedCloseCallClosure();
|
||||
testNativeCallableExceptionalReturnStatic();
|
||||
testNativeCallableExceptionalReturnClosure();
|
||||
await testNativeCallableDontKeepAliveStatic();
|
||||
await testNativeCallableDontKeepAliveClosure();
|
||||
testNativeCallableKeepAliveGetter();
|
||||
print("All tests completed :)");
|
||||
}
|
||||
|
||||
final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
|
||||
|
||||
typedef TwoIntFnNativeType = Int32 Function(Pointer, Int32, Int32);
|
||||
typedef TwoIntFnType = int Function(Pointer, int, int);
|
||||
final callTwoIntFunction = ffiTestFunctions
|
||||
.lookupFunction<TwoIntFnNativeType, TwoIntFnType>("CallTwoIntFunction");
|
||||
|
||||
typedef CallbackNativeType = Int32 Function(Int32, Int32);
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
testNativeCallableStatic() {
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
|
||||
exceptionalReturn: 0);
|
||||
|
||||
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
|
||||
callback.close();
|
||||
}
|
||||
|
||||
testNativeCallableClosure() {
|
||||
int c = 70000;
|
||||
int closure(int a, int b) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(closure,
|
||||
exceptionalReturn: 0);
|
||||
|
||||
Expect.equals(71234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
|
||||
c = 80000;
|
||||
Expect.equals(81234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
|
||||
callback.close();
|
||||
}
|
||||
|
||||
testNativeCallableDoubleCloseError() {
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
|
||||
exceptionalReturn: 0);
|
||||
Expect.notEquals(nullptr, callback.nativeFunction);
|
||||
callback.close();
|
||||
Expect.equals(nullptr, callback.nativeFunction);
|
||||
Expect.throwsStateError(() {
|
||||
callback.close();
|
||||
});
|
||||
}
|
||||
|
||||
late NativeCallable selfClosingStaticCallback;
|
||||
int selfClosingStatic(int a, int b) {
|
||||
selfClosingStaticCallback.close();
|
||||
return a + b;
|
||||
}
|
||||
|
||||
testNativeCallableNestedCloseCallStatic() {
|
||||
selfClosingStaticCallback = NativeCallable<CallbackNativeType>.isolateLocal(
|
||||
selfClosingStatic,
|
||||
exceptionalReturn: 0);
|
||||
|
||||
Expect.equals(1234,
|
||||
callTwoIntFunction(selfClosingStaticCallback.nativeFunction, 1000, 234));
|
||||
|
||||
// The callback is already closed.
|
||||
Expect.equals(nullptr, selfClosingStaticCallback.nativeFunction);
|
||||
}
|
||||
|
||||
testNativeCallableNestedCloseCallClosure() {
|
||||
late NativeCallable callback;
|
||||
|
||||
int selfClosing(int a, int b) {
|
||||
callback.close();
|
||||
return a + b;
|
||||
}
|
||||
|
||||
callback = NativeCallable<CallbackNativeType>.isolateLocal(selfClosing,
|
||||
exceptionalReturn: 0);
|
||||
|
||||
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
|
||||
// The callback is already closed.
|
||||
Expect.equals(nullptr, callback.nativeFunction);
|
||||
}
|
||||
|
||||
int throwerCallback(int a, int b) {
|
||||
if (a != 1000) {
|
||||
throw "Oh no!";
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
|
||||
testNativeCallableExceptionalReturnStatic() {
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
|
||||
throwerCallback,
|
||||
exceptionalReturn: 5678);
|
||||
|
||||
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
Expect.equals(5678, callTwoIntFunction(callback.nativeFunction, 0, 0));
|
||||
|
||||
callback.close();
|
||||
}
|
||||
|
||||
testNativeCallableExceptionalReturnClosure() {
|
||||
int thrower(int a, int b) {
|
||||
if (a != 1000) {
|
||||
throw "Oh no!";
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(thrower,
|
||||
exceptionalReturn: 5678);
|
||||
|
||||
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
Expect.equals(5678, callTwoIntFunction(callback.nativeFunction, 0, 0));
|
||||
|
||||
callback.close();
|
||||
}
|
||||
|
||||
Future<void> testNativeCallableDontKeepAliveStatic() async {
|
||||
final exitPort = ReceivePort();
|
||||
await Isolate.spawn((_) async {
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
|
||||
exceptionalReturn: 0);
|
||||
|
||||
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
|
||||
callback.keepIsolateAlive = false;
|
||||
}, null, onExit: exitPort.sendPort);
|
||||
await exitPort.first;
|
||||
exitPort.close();
|
||||
}
|
||||
|
||||
Future<void> testNativeCallableDontKeepAliveClosure() async {
|
||||
int c = 70000;
|
||||
int closure(int a, int b) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
final exitPort = ReceivePort();
|
||||
await Isolate.spawn((_) async {
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(closure,
|
||||
exceptionalReturn: 0);
|
||||
|
||||
Expect.equals(
|
||||
71234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
|
||||
|
||||
callback.keepIsolateAlive = false;
|
||||
}, null, onExit: exitPort.sendPort);
|
||||
await exitPort.first;
|
||||
exitPort.close();
|
||||
}
|
||||
|
||||
testNativeCallableKeepAliveGetter() {
|
||||
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
|
||||
exceptionalReturn: 0);
|
||||
// Check that only the flag changes are counted by decrementing and
|
||||
// incrementing a lot, and by different amounts.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
callback.keepIsolateAlive = false;
|
||||
Expect.isFalse(callback.keepIsolateAlive);
|
||||
}
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
callback.keepIsolateAlive = true;
|
||||
Expect.isTrue(callback.keepIsolateAlive);
|
||||
}
|
||||
callback.close();
|
||||
}
|
|
@ -37,12 +37,19 @@ void main() {
|
|||
testFromFunctionTearOff();
|
||||
testFromFunctionAbstract();
|
||||
testFromFunctionFunctionExceptionValueMustBeConst();
|
||||
testNativeCallableGeneric();
|
||||
testNativeCallableGeneric2();
|
||||
testNativeCallableWrongNativeFunctionSignature();
|
||||
testNativeCallableTypeMismatch();
|
||||
testNativeCallableAbstract();
|
||||
testNativeCallableMustReturnVoid();
|
||||
testNativeCallableListenerGeneric();
|
||||
testNativeCallableListenerGeneric2();
|
||||
testNativeCallableListenerWrongNativeFunctionSignature();
|
||||
testNativeCallableListenerTypeMismatch();
|
||||
testNativeCallableListenerAbstract();
|
||||
testNativeCallableListenerMustReturnVoid();
|
||||
testNativeCallableIsolateLocalGeneric();
|
||||
testNativeCallableIsolateLocalGeneric2();
|
||||
testNativeCallableIsolateLocalWrongNativeFunctionSignature();
|
||||
testNativeCallableIsolateLocalTypeMismatch();
|
||||
testNativeCallableIsolateLocalTearOff();
|
||||
testNativeCallableIsolateLocalAbstract();
|
||||
testNativeCallableIsolateLocalFunctionExceptionValueMustBeConst();
|
||||
testLookupFunctionGeneric();
|
||||
testLookupFunctionGeneric2();
|
||||
testLookupFunctionWrongNativeFunctionSignature();
|
||||
|
@ -375,7 +382,7 @@ void myVoidFunc2(int x) {
|
|||
print("x = $x");
|
||||
}
|
||||
|
||||
void testNativeCallableGeneric() {
|
||||
void testNativeCallableListenerGeneric() {
|
||||
NativeCallable? generic<T extends Function>(T f) {
|
||||
NativeCallable<NativeVoidFunc>? result;
|
||||
result = NativeCallable.listener(f);
|
||||
|
@ -389,7 +396,7 @@ void testNativeCallableGeneric() {
|
|||
generic(myVoidFunc);
|
||||
}
|
||||
|
||||
void testNativeCallableGeneric2() {
|
||||
void testNativeCallableListenerGeneric2() {
|
||||
NativeCallable<T>? generic<T extends Function>() {
|
||||
NativeCallable<T>? result;
|
||||
result = NativeCallable.listener(myVoidFunc);
|
||||
|
@ -403,7 +410,7 @@ void testNativeCallableGeneric2() {
|
|||
generic<NativeVoidFunc>();
|
||||
}
|
||||
|
||||
void testNativeCallableWrongNativeFunctionSignature() {
|
||||
void testNativeCallableListenerWrongNativeFunctionSignature() {
|
||||
/**/ NativeCallable<NativeVoidFunc>.listener(myVoidFunc2);
|
||||
// ^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
|
||||
|
@ -411,7 +418,7 @@ void testNativeCallableWrongNativeFunctionSignature() {
|
|||
// [cfe] Expected type 'void Function(int)' to be 'void Function()', which is the Dart type corresponding to 'NativeFunction<Void Function()>'.
|
||||
}
|
||||
|
||||
void testNativeCallableTypeMismatch() {
|
||||
void testNativeCallableListenerTypeMismatch() {
|
||||
NativeCallable<NativeVoidFunc> p;
|
||||
p = NativeCallable.listener(myVoidFunc2);
|
||||
// ^^^^^^^^^^^
|
||||
|
@ -420,20 +427,93 @@ void testNativeCallableTypeMismatch() {
|
|||
// [cfe] Expected type 'void Function(int)' to be 'void Function()', which is the Dart type corresponding to 'NativeFunction<Void Function()>'.
|
||||
}
|
||||
|
||||
void testNativeCallableAbstract() {
|
||||
final f = NativeCallable<Function>.listener(testNativeCallableAbstract);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [cfe] Expected type 'NativeFunction<Function>' to be a valid and instantiated subtype of 'NativeType'.
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_NATIVE_FUNCTION_TYPE
|
||||
void testNativeCallableListenerAbstract() {
|
||||
final f = NativeCallable<Function>.listener(
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [cfe] Expected type 'NativeFunction<Function>' to be a valid and instantiated subtype of 'NativeType'.
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_NATIVE_FUNCTION_TYPE
|
||||
testNativeCallableListenerAbstract);
|
||||
}
|
||||
|
||||
void testNativeCallableMustReturnVoid() {
|
||||
void testNativeCallableListenerMustReturnVoid() {
|
||||
final f = NativeCallable<NativeDoubleUnOp>.listener(myTimesThree);
|
||||
// ^^^^^^^^^^^^
|
||||
// [cfe] The return type of the function passed to NativeCallable.listener must be void rather than 'double'.
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_RETURN_VOID
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalGeneric() {
|
||||
NativeCallable<Function> generic<T extends Function>(T f) {
|
||||
late NativeCallable<NativeDoubleUnOp> result;
|
||||
result = NativeCallable.isolateLocal(f);
|
||||
// ^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
|
||||
// ^
|
||||
// [cfe] Expected type 'T' to be 'double Function(double)', which is the Dart type corresponding to 'NativeFunction<Double Function(Double)>'.
|
||||
return result;
|
||||
}
|
||||
|
||||
generic(myTimesThree);
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalGeneric2() {
|
||||
NativeCallable<T> generic<T extends Function>() {
|
||||
late NativeCallable<T> result;
|
||||
result = NativeCallable.isolateLocal(myTimesThree);
|
||||
// ^
|
||||
// [cfe] Expected type 'NativeFunction<T>' to be a valid and instantiated subtype of 'NativeType'.
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_NATIVE_FUNCTION_TYPE
|
||||
return result;
|
||||
}
|
||||
|
||||
generic<NativeDoubleUnOp>();
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalWrongNativeFunctionSignature() {
|
||||
/**/ NativeCallable<IntUnOp>.isolateLocal(myTimesFour);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_NATIVE_FUNCTION_TYPE
|
||||
// ^
|
||||
// [cfe] Expected type 'NativeFunction<int Function(int)>' to be a valid and instantiated subtype of 'NativeType'.
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalTypeMismatch() {
|
||||
NativeCallable<NativeDoubleUnOp> p;
|
||||
p = NativeCallable.isolateLocal(myTimesFour);
|
||||
// ^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
|
||||
// ^
|
||||
// [cfe] Expected type 'int Function(int)' to be 'double Function(double)', which is the Dart type corresponding to 'NativeFunction<Double Function(Double)>'.
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalTearOff() {
|
||||
DoubleUnOp fld = X().tearoff;
|
||||
NativeCallable<NativeDoubleUnOp> p;
|
||||
p = NativeCallable.isolateLocal(fld);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MISSING_EXCEPTION_VALUE
|
||||
// ^
|
||||
// [cfe] Expected an exceptional return value for a native callback returning 'double'.
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalAbstract() {
|
||||
NativeCallable<Function>.isolateLocal(testNativeCallableIsolateLocalAbstract);
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_NATIVE_FUNCTION_TYPE
|
||||
// [cfe] Expected type 'NativeFunction<Function>' to be a valid and instantiated subtype of 'NativeType'.
|
||||
}
|
||||
|
||||
void testNativeCallableIsolateLocalFunctionExceptionValueMustBeConst() {
|
||||
final notAConst = 1.1;
|
||||
NativeCallable<NativeDoubleUnOp> p;
|
||||
p = NativeCallable.isolateLocal(myTimesThree, exceptionalReturn: notAConst);
|
||||
// ^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ARGUMENT_MUST_BE_A_CONSTANT
|
||||
// ^
|
||||
// [cfe] Exceptional return value must be a constant.
|
||||
}
|
||||
|
||||
void testLookupFunctionGeneric() {
|
||||
Function generic<T extends Function>() {
|
||||
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
|
|
Loading…
Reference in a new issue