88#include " platform/globals.h"
99#include " vm/bootstrap_natives.h"
1010#include " vm/class_finalizer.h"
11+ #include " vm/class_id.h"
1112#include " vm/compiler/assembler/assembler.h"
1213#include " vm/compiler/ffi.h"
1314#include " vm/compiler/jit/compiler.h"
@@ -527,11 +528,11 @@ DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) {
527528}
528529
529530// Generates assembly to trampoline from native code into Dart.
531+ #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER)
530532static uword CompileNativeCallback (const Function& c_signature,
531- const Function& dart_target) {
532- #if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
533- UNREACHABLE ();
534- #elif defined(TARGET_ARCH_DBC)
533+ const Function& dart_target,
534+ const Instance& exceptional_return) {
535+ #if defined(TARGET_ARCH_DBC)
535536 // https://github.com/dart-lang/sdk/issues/35774
536537 // FFI is supported, but callbacks are not.
537538 Exceptions::ThrowUnsupportedError (
@@ -564,6 +565,33 @@ static uword CompileNativeCallback(const Function& c_signature,
564565 function.SetFfiCallbackId (callback_id);
565566 function.SetFfiCallbackTarget (dart_target);
566567
568+ // We require that the exceptional return value for functions returning 'Void'
569+ // must be 'null', since native code should not look at the result.
570+ if (compiler::ffi::NativeTypeIsVoid (
571+ AbstractType::Handle (c_signature.result_type ())) &&
572+ !exceptional_return.IsNull ()) {
573+ Exceptions::ThrowUnsupportedError (
574+ " Only 'null' may be used as the exceptional return value for a "
575+ " callback returning void." );
576+ }
577+
578+ // We need to load the exceptional return value as a constant in the generated
579+ // function. This means we need to ensure that it's in old space and has no
580+ // (transitively) mutable fields. This is done by checking (asserting) that
581+ // it's a built-in FFI class, whose fields are all immutable, or a
582+ // user-defined Pointer class, which has no fields.
583+ //
584+ // TODO(36730): We'll need to extend this when we support passing/returning
585+ // structs by value.
586+ ASSERT (exceptional_return.IsNull () || exceptional_return.IsNumber () ||
587+ exceptional_return.IsPointer ());
588+ if (!exceptional_return.IsSmi () && exceptional_return.IsNew ()) {
589+ function.SetFfiCallbackExceptionalReturn (
590+ Instance::Handle (exceptional_return.CopyShallowToOldSpace (thread)));
591+ } else {
592+ function.SetFfiCallbackExceptionalReturn (exceptional_return);
593+ }
594+
567595 // We compile the callback immediately because we need to return a pointer to
568596 // the entry-point. Native calls do not use patching like Dart calls, so we
569597 // cannot compile it lazily.
@@ -578,23 +606,36 @@ static uword CompileNativeCallback(const Function& c_signature,
578606 thread->SetFfiCallbackCode (callback_id, code);
579607
580608 return code.EntryPoint ();
581- #endif
609+ #endif // defined(TARGET_ARCH_DBC)
582610}
611+ #endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER)
583612
584- DEFINE_NATIVE_ENTRY (Ffi_fromFunction, 1 , 1 ) {
613+ DEFINE_NATIVE_ENTRY (Ffi_fromFunction, 1 , 2 ) {
614+ #if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
615+ UNREACHABLE ();
616+ #else
585617 GET_NATIVE_TYPE_ARGUMENT (type_arg, arguments->NativeTypeArgAt (0 ));
586618 GET_NON_NULL_NATIVE_ARGUMENT (Closure, closure, arguments->NativeArgAt (0 ));
619+ GET_NON_NULL_NATIVE_ARGUMENT (Instance, exceptional_return,
620+ arguments->NativeArgAt (1 ));
621+
622+ if (!type_arg.IsInstantiated () || !type_arg.IsFunctionType ()) {
623+ // TODO(35902): Remove this when dynamic invocations of fromFunction are
624+ // prohibited.
625+ Exceptions::ThrowUnsupportedError (
626+ " Type argument to fromFunction must an instantiated function type." );
627+ }
587628
588629 const Function& native_signature =
589- Function::Handle ((( Type&) type_arg).signature ());
630+ Function::Handle (Type::Cast ( type_arg).signature ());
590631 Function& func = Function::Handle (closure.function ());
591632 TypeArguments& type_args = TypeArguments::Handle (zone);
592633 type_args = TypeArguments::New (1 );
593634 type_args.SetTypeAt (Pointer::kNativeTypeArgPos , type_arg);
594635 type_args = type_args.Canonicalize ();
595636
596- Class& native_function_class = Class::Handle (
597- Isolate::Current () ->class_table ()->At (kFfiNativeFunctionCid ));
637+ Class& native_function_class =
638+ Class::Handle (isolate ->class_table ()->At (kFfiNativeFunctionCid ));
598639 native_function_class.EnsureIsFinalized (Thread::Current ());
599640
600641 Type& native_function_type = Type::Handle (
@@ -613,12 +654,30 @@ DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 1) {
613654 func = func.parent_function ();
614655 ASSERT (func.is_static ());
615656
616- const uword address = CompileNativeCallback (native_signature, func);
657+ const AbstractType& return_type =
658+ AbstractType::Handle (native_signature.result_type ());
659+ if (compiler::ffi::NativeTypeIsVoid (return_type)) {
660+ if (!exceptional_return.IsNull ()) {
661+ const String& error = String::Handle (
662+ String::NewFormatted (" Exceptional return argument to 'fromFunction' "
663+ " must be null for functions returning void." ));
664+ Exceptions::ThrowArgumentError (error);
665+ }
666+ } else if (!compiler::ffi::NativeTypeIsPointer (return_type) &&
667+ exceptional_return.IsNull ()) {
668+ const String& error = String::Handle (String::NewFormatted (
669+ " Exceptional return argument to 'fromFunction' must not be null." ));
670+ Exceptions::ThrowArgumentError (error);
671+ }
672+
673+ const uword address =
674+ CompileNativeCallback (native_signature, func, exceptional_return);
617675
618676 const Pointer& result = Pointer::Handle (Pointer::New (
619677 native_function_type, Integer::Handle (zone, Integer::New (address))));
620678
621679 return result.raw ();
680+ #endif
622681}
623682
624683#if defined(TARGET_ARCH_DBC)
0 commit comments