mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[vm/ffi] Closure callbacks for async callbacks
This change is almost trivial. The closure is stored on the callback's RawReceivePort, not in the VM. So we can basically just remove the CFE check and it pretty much works. The only problem is that we can't set function.FfiCallbackTarget anymore, so most of the CL is dealing with that. A few places were deciding whether an FFI trampoline was a call or a callback based on whether function.FfiCallbackTarget() was null. But now the target will be null for async callbacks. So instead I've added a new value to the FfiCallbackKind enum (and renamed it), and changed those checks. Sync callback closures will be a separate CL, because they're more complicated. Bug: https://github.com/dart-lang/sdk/issues/52689 Change-Id: I8e5dfb557362e679f66195b735c3c382e6792840 TEST=async_void_function_callbacks_test.dart Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316160 Commit-Queue: Liam Appelbe <liama@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
parent
f2611b7c8e
commit
edeac698c2
|
@ -141,9 +141,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
final Expression func = node.arguments.positional[0];
|
||||
final DartType dartType = func.getStaticType(staticTypeContext!);
|
||||
|
||||
_ensureIsStaticFunction(
|
||||
func, nativeCallableListenerConstructor.name.text);
|
||||
|
||||
ensureNativeTypeValid(nativeType, node);
|
||||
ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
|
||||
|
@ -599,7 +596,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
// void _handler(List args) => target(args[0], args[1], ...)
|
||||
// final _callback = NativeCallable<T>._(_handler, debugName);
|
||||
// _callback._pointer = _pointerAsyncFromFunction<NativeFunction<T>>(
|
||||
// _nativeAsyncCallbackFunction<T>(target), _callback._rawPort);
|
||||
// _nativeAsyncCallbackFunction<T>(), _callback._rawPort);
|
||||
// expression result: _callback;
|
||||
Expression _replaceNativeCallableListenerConstructor(
|
||||
ConstructorInvocation node) {
|
||||
|
@ -622,9 +619,13 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
functionType: Substitution.fromInterfaceType(listType)
|
||||
.substituteType(listElementAt.getterType) as FunctionType));
|
||||
}
|
||||
final target = _getStaticFunctionTarget(node.arguments.positional[0]);
|
||||
final handlerBody =
|
||||
ExpressionStatement(StaticInvocation(target, Arguments(targetArgs)));
|
||||
final target = node.arguments.positional[0];
|
||||
final handlerBody = ExpressionStatement(FunctionInvocation(
|
||||
FunctionAccessKind.FunctionType,
|
||||
target,
|
||||
Arguments(targetArgs),
|
||||
functionType: targetType,
|
||||
));
|
||||
final handler = FunctionNode(handlerBody,
|
||||
positionalParameters: [args], returnType: VoidType())
|
||||
..fileOffset = node.fileOffset;
|
||||
|
@ -635,7 +636,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
nativeCallablePrivateConstructor,
|
||||
Arguments([
|
||||
FunctionExpression(handler),
|
||||
StringLiteral('NativeCallable(${target.name})'),
|
||||
StringLiteral('NativeCallable($target)'),
|
||||
], types: [
|
||||
targetType,
|
||||
])),
|
||||
|
@ -644,12 +645,12 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
..fileOffset = node.fileOffset;
|
||||
|
||||
// _callback._pointer = _pointerAsyncFromFunction<NativeFunction<T>>(
|
||||
// _nativeAsyncCallbackFunction<T>(target), _callback._rawPort);
|
||||
// _nativeAsyncCallbackFunction<T>(), _callback._rawPort);
|
||||
final pointerValue = StaticInvocation(
|
||||
pointerAsyncFromFunctionProcedure,
|
||||
Arguments([
|
||||
StaticInvocation(
|
||||
nativeAsyncCallbackFunctionProcedure, node.arguments),
|
||||
StaticInvocation(nativeAsyncCallbackFunctionProcedure,
|
||||
Arguments([], types: [targetType])),
|
||||
InstanceGet(InstanceAccessKind.Instance, VariableGet(nativeCallable),
|
||||
nativeCallablePortField.name,
|
||||
interfaceTarget: nativeCallablePortField,
|
||||
|
@ -892,16 +893,6 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
|
|||
throw FfiStaticTypeError();
|
||||
}
|
||||
|
||||
Procedure _getStaticFunctionTarget(Expression node) {
|
||||
if (node is StaticGet) {
|
||||
return node.target as Procedure;
|
||||
}
|
||||
if (node is ConstantExpression) {
|
||||
return (node.constant as StaticTearOffConstant).target;
|
||||
}
|
||||
throw ArgumentError.value(node, "node", "Not a static function");
|
||||
}
|
||||
|
||||
/// Returns the class that should not be implemented or extended.
|
||||
///
|
||||
/// If the superclass is not sealed, returns `null`.
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:ffi';
|
|||
|
||||
void main() {
|
||||
testNativeCallableListener();
|
||||
testNativeCallableListenerClosure();
|
||||
}
|
||||
|
||||
void printInt(int i) => print(i);
|
||||
|
@ -15,3 +16,11 @@ void testNativeCallableListener() {
|
|||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
||||
void testNativeCallableListenerClosure() {
|
||||
int j = 123;
|
||||
void closure(int i) => print(i + j);
|
||||
final callback = NativeCallable<Void Function(Int32)>.listener(closure);
|
||||
print(callback.nativeFunction);
|
||||
callback.close();
|
||||
}
|
||||
|
|
|
@ -8,19 +8,33 @@ import "dart:ffi";
|
|||
|
||||
static method main() → void {
|
||||
self::testNativeCallableListener();
|
||||
self::testNativeCallableListenerClosure();
|
||||
}
|
||||
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
|
||||
self::printInt(args.{core::List::[]}(0){(core::int) → dynamic});
|
||||
);
|
||||
[@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>(#C1), [@vm.direct-call.metadata=dart.ffi::NativeCallable._port] [@vm.inferred-type.metadata=dart.isolate::_RawReceivePort] #t1.{ffi::NativeCallable::_port}{iso::RawReceivePort});
|
||||
#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});
|
||||
} =>#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};
|
||||
}
|
||||
static method testNativeCallableListenerClosure() → void {
|
||||
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
|
||||
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});
|
||||
} =>#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};
|
||||
}
|
||||
constants {
|
||||
#C1 = static-tearoff self::printInt
|
||||
}
|
||||
|
|
|
@ -8,19 +8,33 @@ import "dart:ffi";
|
|||
|
||||
static method main() → void {
|
||||
self::testNativeCallableListener();
|
||||
self::testNativeCallableListenerClosure();
|
||||
}
|
||||
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
|
||||
self::printInt(args.{core::List::[]}(0){(core::int) → dynamic});
|
||||
, "NativeCallable(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>(#C1), #t1.{ffi::NativeCallable::_port}{iso::RawReceivePort});
|
||||
#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});
|
||||
} =>#t1;
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
static method testNativeCallableListenerClosure() → 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 = block {
|
||||
final ffi::NativeCallable<(ffi::Int32) → ffi::Void> #t2 = new ffi::NativeCallable::_<(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});
|
||||
} =>#t2;
|
||||
core::print(callback.{ffi::NativeCallable::nativeFunction}{ffi::Pointer<ffi::NativeFunction<(ffi::Int32) → ffi::Void>>});
|
||||
callback.{ffi::NativeCallable::close}(){() → void};
|
||||
}
|
||||
constants {
|
||||
#C1 = static-tearoff self::printInt
|
||||
}
|
||||
|
|
|
@ -1386,7 +1386,7 @@ class FfiTrampolineDataSerializationCluster : public SerializationCluster {
|
|||
AutoTraceObject(data);
|
||||
WriteFromTo(data);
|
||||
s->Write<int32_t>(data->untag()->callback_id_);
|
||||
s->Write<uint8_t>(data->untag()->callback_kind_);
|
||||
s->Write<uint8_t>(data->untag()->trampoline_kind_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1415,7 +1415,7 @@ class FfiTrampolineDataDeserializationCluster : public DeserializationCluster {
|
|||
FfiTrampolineData::InstanceSize());
|
||||
d.ReadFromTo(data);
|
||||
data->untag()->callback_id_ = d.Read<int32_t>();
|
||||
data->untag()->callback_kind_ = d.Read<uint8_t>();
|
||||
data->untag()->trampoline_kind_ = d.Read<uint8_t>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -425,7 +425,7 @@ struct CanonicalFfiCallbackFunctionTraits {
|
|||
f1.FfiCSignature() == f2.FfiCSignature() &&
|
||||
f1.FfiCallbackExceptionalReturn() ==
|
||||
f2.FfiCallbackExceptionalReturn() &&
|
||||
f1.GetFfiCallbackKind() == f2.GetFfiCallbackKind());
|
||||
f1.GetFfiTrampolineKind() == f2.GetFfiTrampolineKind());
|
||||
}
|
||||
static bool ReportStats() { return false; }
|
||||
};
|
||||
|
|
|
@ -3451,7 +3451,7 @@ void PrecompileParsedFunctionHelper::FinalizeCompilation(
|
|||
}
|
||||
|
||||
if (function.IsFfiTrampoline() &&
|
||||
function.FfiCallbackTarget() != Function::null()) {
|
||||
function.GetFfiTrampolineKind() != FfiTrampolineKind::kCall) {
|
||||
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -835,10 +835,10 @@ void FlowGraphSerializer::WriteTrait<const Function&>::Write(
|
|||
s->Write<const Function&>(Function::Handle(zone, x.FfiCallbackTarget()));
|
||||
s->Write<const FunctionType&>(
|
||||
FunctionType::Handle(zone, x.FfiCSignature()));
|
||||
if (x.FfiCallbackTarget() != Object::null()) {
|
||||
if (x.GetFfiTrampolineKind() != FfiTrampolineKind::kCall) {
|
||||
s->Write<const Instance&>(
|
||||
Instance::Handle(zone, x.FfiCallbackExceptionalReturn()));
|
||||
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiCallbackKind()));
|
||||
s->Write<uint8_t>(static_cast<uint8_t>(x.GetFfiTrampolineKind()));
|
||||
} else {
|
||||
s->Write<const String&>(String::Handle(zone, x.name()));
|
||||
s->Write<const FunctionType&>(
|
||||
|
@ -925,7 +925,8 @@ const Function& FlowGraphDeserializer::ReadTrait<const Function&>::Read(
|
|||
const FunctionType& c_signature = d->Read<const FunctionType&>();
|
||||
if (!callback_target.IsNull()) {
|
||||
const Instance& exceptional_return = d->Read<const Instance&>();
|
||||
FfiCallbackKind kind = static_cast<FfiCallbackKind>(d->Read<uint8_t>());
|
||||
FfiTrampolineKind kind =
|
||||
static_cast<FfiTrampolineKind>(d->Read<uint8_t>());
|
||||
return Function::ZoneHandle(
|
||||
zone, compiler::ffi::NativeCallbackFunction(
|
||||
c_signature, callback_target, exceptional_return, kind));
|
||||
|
|
|
@ -47,6 +47,7 @@ FunctionPtr TrampolineFunction(const String& name,
|
|||
|
||||
function.SetFfiCSignature(c_signature);
|
||||
function.SetFfiIsLeaf(is_leaf);
|
||||
function.SetFfiTrampolineKind(FfiTrampolineKind::kCall);
|
||||
|
||||
return function.ptr();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace ffi {
|
|||
FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
||||
const Function& dart_target,
|
||||
const Instance& exceptional_return,
|
||||
FfiCallbackKind kind) {
|
||||
FfiTrampolineKind kind) {
|
||||
Thread* const thread = Thread::Current();
|
||||
Zone* const zone = thread->zone();
|
||||
Function& function = Function::Handle(zone);
|
||||
|
@ -28,9 +28,12 @@ FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
|||
// Create a new Function named '<target>_FfiCallback' and stick it in the
|
||||
// 'dart:ffi' library. Note that these functions will never be invoked by
|
||||
// Dart, so they may have duplicate names.
|
||||
const auto& name = String::Handle(
|
||||
zone, Symbols::FromConcat(thread, Symbols::FfiCallback(),
|
||||
String::Handle(zone, dart_target.name())));
|
||||
const auto& name =
|
||||
kind == FfiTrampolineKind::kSyncCallback
|
||||
? String::Handle(zone, Symbols::FromConcat(
|
||||
thread, Symbols::FfiCallback(),
|
||||
String::Handle(zone, dart_target.name())))
|
||||
: Symbols::FfiAsyncCallback();
|
||||
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());
|
||||
|
@ -47,7 +50,7 @@ FunctionPtr NativeCallbackFunction(const FunctionType& c_signature,
|
|||
// the body.
|
||||
function.SetFfiCSignature(c_signature);
|
||||
function.SetFfiCallbackTarget(dart_target);
|
||||
function.SetFfiCallbackKind(kind);
|
||||
function.SetFfiTrampolineKind(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,
|
||||
FfiCallbackKind kind);
|
||||
FfiTrampolineKind kind);
|
||||
|
||||
// Builds a mapping from `callback-id` to code object / ...
|
||||
//
|
||||
|
|
|
@ -3386,9 +3386,9 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
|
|||
case MethodRecognizer::kFfiAsFunctionInternal:
|
||||
return BuildFfiAsFunctionInternal();
|
||||
case MethodRecognizer::kFfiNativeCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(FfiCallbackKind::kSync);
|
||||
return BuildFfiNativeCallbackFunction(FfiTrampolineKind::kSyncCallback);
|
||||
case MethodRecognizer::kFfiNativeAsyncCallbackFunction:
|
||||
return BuildFfiNativeCallbackFunction(FfiCallbackKind::kAsync);
|
||||
return BuildFfiNativeCallbackFunction(FfiTrampolineKind::kAsyncCallback);
|
||||
case MethodRecognizer::kFfiLoadAbiSpecificInt:
|
||||
return BuildLoadAbiSpecificInt(/*at_index=*/false);
|
||||
case MethodRecognizer::kFfiLoadAbiSpecificIntAtIndex:
|
||||
|
@ -6321,19 +6321,20 @@ Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
|
|||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
|
||||
FfiCallbackKind kind) {
|
||||
FfiTrampolineKind kind) {
|
||||
// The call-site must look like this (guaranteed by the FE which inserts it):
|
||||
//
|
||||
// FfiCallbackKind::kSync:
|
||||
// FfiTrampolineKind::kSyncCallback:
|
||||
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
|
||||
//
|
||||
// FfiCallbackKind::kAsync:
|
||||
// _nativeAsyncCallbackFunction<NativeSignatureType>(target)
|
||||
// FfiTrampolineKind::kAsyncCallback:
|
||||
// _nativeAsyncCallbackFunction<NativeSignatureType>()
|
||||
//
|
||||
// The FE also guarantees that both arguments are constants.
|
||||
|
||||
// Target, and for kSync callbacks, the exceptional return.
|
||||
const intptr_t expected_argc = kind == FfiCallbackKind::kSync ? 2 : 1;
|
||||
const intptr_t expected_argc =
|
||||
kind == FfiTrampolineKind::kSyncCallback ? 2 : 0;
|
||||
|
||||
const intptr_t argc = ReadUInt(); // Read argument count.
|
||||
ASSERT(argc == expected_argc);
|
||||
|
@ -6352,19 +6353,21 @@ Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction(
|
|||
ASSERT(positional_count == expected_argc);
|
||||
|
||||
// Read target expression and extract the target function.
|
||||
code += BuildExpression(); // Build first positional argument (target).
|
||||
Definition* target_def = B->Peek();
|
||||
ASSERT(target_def->IsConstant());
|
||||
const Closure& target_closure =
|
||||
Closure::Cast(target_def->AsConstant()->value());
|
||||
ASSERT(!target_closure.IsNull());
|
||||
Function& target = Function::Handle(Z, target_closure.function());
|
||||
ASSERT(!target.IsNull() && target.IsImplicitClosureFunction());
|
||||
target = target.parent_function();
|
||||
code += Drop();
|
||||
|
||||
Function& target = Function::Handle(Z, Function::null());
|
||||
Instance& exceptional_return = Instance::ZoneHandle(Z, Instance::null());
|
||||
if (kind == FfiCallbackKind::kSync) {
|
||||
if (kind == FfiTrampolineKind::kSyncCallback) {
|
||||
// Build first positional argument (target).
|
||||
code += BuildExpression();
|
||||
Definition* target_def = B->Peek();
|
||||
ASSERT(target_def->IsConstant());
|
||||
const Closure& target_closure =
|
||||
Closure::Cast(target_def->AsConstant()->value());
|
||||
ASSERT(!target_closure.IsNull());
|
||||
target = target_closure.function();
|
||||
ASSERT(!target.IsNull() && target.IsImplicitClosureFunction());
|
||||
target = target.parent_function();
|
||||
code += Drop();
|
||||
|
||||
// Build second positional argument (exceptionalReturn).
|
||||
code += BuildExpression();
|
||||
Definition* exceptional_return_def = B->Peek();
|
||||
|
|
|
@ -398,7 +398,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
|||
|
||||
// Build FG for '_nativeCallbackFunction'. Reads an Arguments from the
|
||||
// Kernel buffer and pushes the resulting Function object.
|
||||
Fragment BuildFfiNativeCallbackFunction(FfiCallbackKind kind);
|
||||
Fragment BuildFfiNativeCallbackFunction(FfiTrampolineKind kind);
|
||||
|
||||
// Piece of a StringConcatenation.
|
||||
// Represents either a StringLiteral, or a Reader offset to the expression.
|
||||
|
|
|
@ -4740,15 +4740,16 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
|
|||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiTrampoline(
|
||||
const Function& function) {
|
||||
if (function.FfiCallbackTarget() != Function::null()) {
|
||||
if (function.GetFfiCallbackKind() == FfiCallbackKind::kSync) {
|
||||
switch (function.GetFfiTrampolineKind()) {
|
||||
case FfiTrampolineKind::kSyncCallback:
|
||||
return BuildGraphOfSyncFfiCallback(function);
|
||||
} else {
|
||||
case FfiTrampolineKind::kAsyncCallback:
|
||||
return BuildGraphOfAsyncFfiCallback(function);
|
||||
}
|
||||
} else {
|
||||
return BuildGraphOfFfiNative(function);
|
||||
case FfiTrampolineKind::kCall:
|
||||
return BuildGraphOfFfiNative(function);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
|
||||
|
|
|
@ -389,7 +389,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
case UntaggedFunction::kFfiTrampoline: {
|
||||
needs_expr_temp_ = true;
|
||||
// Callbacks and calls with handles need try/catch variables.
|
||||
if ((function.FfiCallbackTarget() != Function::null() ||
|
||||
if ((function.GetFfiTrampolineKind() != FfiTrampolineKind::kCall ||
|
||||
function.FfiCSignatureContainsHandles())) {
|
||||
++depth_.try_;
|
||||
AddTryVariables();
|
||||
|
|
|
@ -477,7 +477,7 @@ CodePtr CompileParsedFunctionHelper::FinalizeCompilation(
|
|||
}
|
||||
|
||||
if (function.IsFfiTrampoline() &&
|
||||
function.FfiCallbackTarget() != Function::null()) {
|
||||
function.GetFfiTrampolineKind() != FfiTrampolineKind::kCall) {
|
||||
compiler::ffi::SetFfiCallbackCode(thread(), function, code);
|
||||
}
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ namespace dart {
|
|||
V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x630c8491) \
|
||||
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3fe722bc) \
|
||||
V(::, _nativeAsyncCallbackFunction, FfiNativeAsyncCallbackFunction, \
|
||||
0x2057e24d) \
|
||||
0xbec4b7b9) \
|
||||
V(::, _nativeEffect, NativeEffect, 0x536f42b1) \
|
||||
V(::, _loadAbiSpecificInt, FfiLoadAbiSpecificInt, 0x77f95c92) \
|
||||
V(::, _loadAbiSpecificIntAtIndex, FfiLoadAbiSpecificIntAtIndex, 0x6a963ed4) \
|
||||
|
|
|
@ -236,7 +236,7 @@ FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateSyncFfiCallback(
|
|||
Zone* zone,
|
||||
const Function& function,
|
||||
Metadata** list_head) {
|
||||
ASSERT(function.GetFfiCallbackKind() == FfiCallbackKind::kSync);
|
||||
ASSERT(function.GetFfiTrampolineKind() == FfiTrampolineKind::kSyncCallback);
|
||||
TrampolineType trampoline_type = TrampolineType::kSync;
|
||||
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
|
@ -262,7 +262,7 @@ FfiCallbackMetadata::Trampoline FfiCallbackMetadata::CreateAsyncFfiCallback(
|
|||
const Function& function,
|
||||
Dart_Port send_port,
|
||||
Metadata** list_head) {
|
||||
ASSERT(function.GetFfiCallbackKind() == FfiCallbackKind::kAsync);
|
||||
ASSERT(function.GetFfiTrampolineKind() == FfiTrampolineKind::kAsyncCallback);
|
||||
return CreateMetadataEntry(isolate, TrampolineType::kAsync,
|
||||
GetEntryPoint(zone, function), send_port,
|
||||
list_head);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
FunctionPtr CreateTestFunction(FfiCallbackKind kind) {
|
||||
FunctionPtr CreateTestFunction(FfiTrampolineKind 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 =
|
||||
|
@ -92,7 +92,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateSyncFfiCallback) {
|
|||
auto* zone = thread->zone();
|
||||
|
||||
const auto& func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kSync));
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kSyncCallback));
|
||||
const auto& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -183,7 +183,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateAsyncFfiCallback) {
|
|||
auto* zone = thread->zone();
|
||||
|
||||
const Function& func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsync));
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kAsyncCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -267,7 +267,7 @@ ISOLATE_UNIT_TEST_CASE(FfiCallbackMetadata_TrampolineRecycling) {
|
|||
auto* fcm = FfiCallbackMetadata::Instance();
|
||||
|
||||
const Function& func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsync));
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kAsyncCallback));
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
|
@ -334,7 +334,7 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_DeleteTrampolines) {
|
|||
FfiCallbackMetadata::Metadata* list_head = nullptr;
|
||||
|
||||
const auto& sync_func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kSync));
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kSyncCallback));
|
||||
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
|
||||
EXPECT(!sync_code.IsNull());
|
||||
|
||||
|
@ -414,11 +414,11 @@ static void RunBigRandomMultithreadedTest(uint64_t seed) {
|
|||
FfiCallbackMetadata::Metadata* list_head = nullptr;
|
||||
|
||||
const Function& async_func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kAsync));
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kAsyncCallback));
|
||||
const Code& async_code = Code::Handle(async_func.EnsureHasCode());
|
||||
EXPECT(!async_code.IsNull());
|
||||
const Function& sync_func =
|
||||
Function::Handle(CreateTestFunction(FfiCallbackKind::kSync));
|
||||
Function::Handle(CreateTestFunction(FfiTrampolineKind::kSyncCallback));
|
||||
const auto& sync_code = Code::Handle(sync_func.EnsureHasCode());
|
||||
EXPECT(!sync_code.IsNull());
|
||||
|
||||
|
|
|
@ -3608,10 +3608,10 @@ FfiCallbackMetadata::Trampoline Isolate::CreateSyncFfiCallback(
|
|||
|
||||
FfiCallbackMetadata::Trampoline Isolate::CreateAsyncFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
const Function& send_function,
|
||||
Dart_Port send_port) {
|
||||
return FfiCallbackMetadata::Instance()->CreateAsyncFfiCallback(
|
||||
this, zone, function, send_port, &ffi_callback_list_head_);
|
||||
this, zone, send_function, send_port, &ffi_callback_list_head_);
|
||||
}
|
||||
|
||||
void Isolate::DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback) {
|
||||
|
|
|
@ -1246,7 +1246,7 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
const Function& function);
|
||||
FfiCallbackMetadata::Trampoline CreateAsyncFfiCallback(
|
||||
Zone* zone,
|
||||
const Function& function,
|
||||
const Function& send_function,
|
||||
Dart_Port send_port);
|
||||
void DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback);
|
||||
|
||||
|
|
|
@ -8297,7 +8297,7 @@ bool Function::FfiCSignatureReturnsStruct() const {
|
|||
|
||||
int32_t Function::FfiCallbackId() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
ASSERT(FfiCallbackTarget() != Object::null());
|
||||
ASSERT(GetFfiTrampolineKind() != FfiTrampolineKind::kCall);
|
||||
|
||||
const auto& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
|
@ -8310,7 +8310,7 @@ int32_t Function::FfiCallbackId() const {
|
|||
|
||||
void Function::AssignFfiCallbackId(int32_t callback_id) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
ASSERT(FfiCallbackTarget() != Object::null());
|
||||
ASSERT(GetFfiTrampolineKind() != FfiTrampolineKind::kCall);
|
||||
|
||||
const auto& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
|
@ -8362,18 +8362,18 @@ void Function::SetFfiCallbackExceptionalReturn(const Instance& value) const {
|
|||
FfiTrampolineData::Cast(obj).set_callback_exceptional_return(value);
|
||||
}
|
||||
|
||||
FfiCallbackKind Function::GetFfiCallbackKind() const {
|
||||
FfiTrampolineKind Function::GetFfiTrampolineKind() const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
return FfiTrampolineData::Cast(obj).callback_kind();
|
||||
return FfiTrampolineData::Cast(obj).trampoline_kind();
|
||||
}
|
||||
|
||||
void Function::SetFfiCallbackKind(FfiCallbackKind value) const {
|
||||
void Function::SetFfiTrampolineKind(FfiTrampolineKind value) const {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
const Object& obj = Object::Handle(data());
|
||||
ASSERT(!obj.IsNull());
|
||||
FfiTrampolineData::Cast(obj).set_callback_kind(value);
|
||||
FfiTrampolineData::Cast(obj).set_trampoline_kind(value);
|
||||
}
|
||||
|
||||
const char* Function::KindToCString(UntaggedFunction::Kind kind) {
|
||||
|
@ -11473,8 +11473,8 @@ void FfiTrampolineData::set_callback_exceptional_return(
|
|||
untag()->set_callback_exceptional_return(value.ptr());
|
||||
}
|
||||
|
||||
void FfiTrampolineData::set_callback_kind(FfiCallbackKind kind) const {
|
||||
StoreNonPointer(&untag()->callback_kind_, static_cast<uint8_t>(kind));
|
||||
void FfiTrampolineData::set_trampoline_kind(FfiTrampolineKind kind) const {
|
||||
StoreNonPointer(&untag()->trampoline_kind_, static_cast<uint8_t>(kind));
|
||||
}
|
||||
|
||||
FfiTrampolineDataPtr FfiTrampolineData::New() {
|
||||
|
|
|
@ -2949,9 +2949,10 @@ struct NameFormattingParams {
|
|||
}
|
||||
};
|
||||
|
||||
enum class FfiCallbackKind : uint8_t {
|
||||
kSync,
|
||||
kAsync,
|
||||
enum class FfiTrampolineKind : uint8_t {
|
||||
kCall,
|
||||
kSyncCallback,
|
||||
kAsyncCallback,
|
||||
};
|
||||
|
||||
class Function : public Object {
|
||||
|
@ -3012,10 +3013,10 @@ class Function : public Object {
|
|||
void SetFfiCallbackExceptionalReturn(const Instance& value) const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
FfiCallbackKind GetFfiCallbackKind() const;
|
||||
FfiTrampolineKind GetFfiTrampolineKind() const;
|
||||
|
||||
// Can only be called on FFI trampolines.
|
||||
void SetFfiCallbackKind(FfiCallbackKind value) const;
|
||||
void SetFfiTrampolineKind(FfiTrampolineKind value) const;
|
||||
|
||||
// Return the signature of this function.
|
||||
PRECOMPILER_WSR_FIELD_DECLARATION(FunctionType, signature);
|
||||
|
@ -4347,10 +4348,10 @@ class FfiTrampolineData : public Object {
|
|||
}
|
||||
void set_callback_exceptional_return(const Instance& value) const;
|
||||
|
||||
FfiCallbackKind callback_kind() const {
|
||||
return static_cast<FfiCallbackKind>(untag()->callback_kind_);
|
||||
FfiTrampolineKind trampoline_kind() const {
|
||||
return static_cast<FfiTrampolineKind>(untag()->trampoline_kind_);
|
||||
}
|
||||
void set_callback_kind(FfiCallbackKind kind) const;
|
||||
void set_trampoline_kind(FfiTrampolineKind kind) const;
|
||||
|
||||
int32_t callback_id() const { return untag()->callback_id_; }
|
||||
void set_callback_id(int32_t value) const;
|
||||
|
|
|
@ -1516,8 +1516,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 callback this is. See FfiCallbackKind.
|
||||
uint8_t callback_kind_;
|
||||
// The kind of trampoline this is. See FfiTrampolineKind.
|
||||
uint8_t trampoline_kind_;
|
||||
};
|
||||
|
||||
class UntaggedField : public UntaggedObject {
|
||||
|
|
|
@ -100,6 +100,7 @@ class ObjectPointerVisitor;
|
|||
V(ExternalOneByteString, "_ExternalOneByteString") \
|
||||
V(ExternalTwoByteString, "_ExternalTwoByteString") \
|
||||
V(FfiAbiSpecificMapping, "_FfiAbiSpecificMapping") \
|
||||
V(FfiAsyncCallback, "_FfiAsyncCallback") \
|
||||
V(FfiBool, "Bool") \
|
||||
V(FfiCallback, "_FfiCallback") \
|
||||
V(FfiDouble, "Double") \
|
||||
|
|
|
@ -146,8 +146,7 @@ external Pointer<NS> _pointerFromFunction<NS extends NativeFunction>(
|
|||
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:external-name", "Ffi_nativeAsyncCallbackFunction")
|
||||
external dynamic _nativeAsyncCallbackFunction<NS extends Function>(
|
||||
Function target);
|
||||
external dynamic _nativeAsyncCallbackFunction<NS extends Function>();
|
||||
|
||||
@pragma("vm:external-name", "Ffi_pointerAsyncFromFunction")
|
||||
external Pointer<NS> _pointerAsyncFromFunction<NS extends NativeFunction>(
|
||||
|
|
|
@ -40,6 +40,7 @@ main(args, message) async {
|
|||
await testNativeCallableUseAfterFree();
|
||||
await testNativeCallableNestedCloseCall();
|
||||
await testNativeCallableThrowInsideCallback();
|
||||
await testNativeCallableClosure();
|
||||
|
||||
// Message passing tests.
|
||||
globalVar = 1000;
|
||||
|
@ -153,6 +154,28 @@ testNativeCallableThrowInsideCallback() async {
|
|||
callback.close();
|
||||
}
|
||||
|
||||
testNativeCallableClosure() async {
|
||||
final lib = NativeLibrary();
|
||||
int c = 70000;
|
||||
void foo(int a, int b) {
|
||||
print("foo");
|
||||
simpleFunctionResult.complete(a + b + c);
|
||||
}
|
||||
|
||||
final callback = NativeCallable<CallbackNativeType>.listener(foo);
|
||||
|
||||
simpleFunctionResult = Completer<int>();
|
||||
lib.callFunctionOnSameThread(1000, callback.nativeFunction);
|
||||
Expect.equals(71123, await simpleFunctionResult.future);
|
||||
|
||||
c = 80000;
|
||||
simpleFunctionResult = Completer<int>();
|
||||
lib.callFunctionOnSameThread(4000, callback.nativeFunction);
|
||||
Expect.equals(84123, await simpleFunctionResult.future);
|
||||
|
||||
callback.close();
|
||||
}
|
||||
|
||||
final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
|
||||
|
||||
// Global variable that is 1000 on isolate A, and 2000 on isolate B.
|
||||
|
@ -565,7 +588,6 @@ class IsolateA {
|
|||
instance = this;
|
||||
fnPtrsA = FnPtrs.fromCallbacks(callbacksA);
|
||||
atm.toIsoA = atm.toThis;
|
||||
print("IsolateA fn ptr: ${fnPtrsA.addGlobalVarPtr.toRadixString(16)}");
|
||||
}
|
||||
|
||||
Future<void> messageLoop() async {
|
||||
|
@ -634,7 +656,6 @@ class IsolateB {
|
|||
instance = this;
|
||||
fnPtrsB = FnPtrs.fromCallbacks(callbacksB);
|
||||
atm.toIsoB = atm.toThis;
|
||||
print("IsolateB fn ptr: ${fnPtrsB.addGlobalVarPtr.toRadixString(16)}");
|
||||
sendPort.send(['sendPort', recvPort.sendPort]);
|
||||
sendPort.send(['testPort', atm.toThis]);
|
||||
sendPort.send(['fnPtrs', fnPtrsB.toList()]);
|
||||
|
|
|
@ -41,7 +41,6 @@ void main() {
|
|||
testNativeCallableGeneric2();
|
||||
testNativeCallableWrongNativeFunctionSignature();
|
||||
testNativeCallableTypeMismatch();
|
||||
testNativeCallableClosure();
|
||||
testNativeCallableAbstract();
|
||||
testNativeCallableMustReturnVoid();
|
||||
testLookupFunctionGeneric();
|
||||
|
@ -381,8 +380,9 @@ void testNativeCallableGeneric() {
|
|||
NativeCallable<NativeVoidFunc>? result;
|
||||
result = NativeCallable.listener(f);
|
||||
// ^
|
||||
// [cfe] listener expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code. Closures and tear-offs are not supported because they can capture context.
|
||||
// [analyzer] COMPILE_TIME_ERROR.MUST_BE_A_SUBTYPE
|
||||
// ^
|
||||
// [cfe] Expected type 'T' to be 'void Function()', which is the Dart type corresponding to 'NativeFunction<Void Function()>'.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -420,14 +420,6 @@ void testNativeCallableTypeMismatch() {
|
|||
// [cfe] Expected type 'void Function(int)' to be 'void Function()', which is the Dart type corresponding to 'NativeFunction<Void Function()>'.
|
||||
}
|
||||
|
||||
void testNativeCallableClosure() {
|
||||
VoidFunc someClosure = () => print("Closure");
|
||||
NativeCallable<NativeVoidFunc> p;
|
||||
p = NativeCallable.listener(someClosure);
|
||||
// ^
|
||||
// [cfe] listener expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code. Closures and tear-offs are not supported because they can capture context.
|
||||
}
|
||||
|
||||
void testNativeCallableAbstract() {
|
||||
final f = NativeCallable<Function>.listener(testNativeCallableAbstract);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
Loading…
Reference in a new issue