diff --git a/lib/ui/painting/rrect.h b/lib/ui/painting/rrect.h index 75c89468a307b..df250a2e855c6 100644 --- a/lib/ui/painting/rrect.h +++ b/lib/ui/painting/rrect.h @@ -23,10 +23,21 @@ namespace tonic { template <> struct DartConverter { - static flutter::RRect FromDart(Dart_Handle handle); - static flutter::RRect FromArguments(Dart_NativeArguments args, - int index, - Dart_Handle& exception); + using NativeType = flutter::RRect; + using FfiType = Dart_Handle; + static constexpr const char* kFfiRepresentation = "Handle"; + static constexpr const char* kDartRepresentation = "Object"; + static constexpr bool kAllowedInLeafCall = false; + + static NativeType FromDart(Dart_Handle handle); + static NativeType FromArguments(Dart_NativeArguments args, + int index, + Dart_Handle& exception); + + static NativeType FromFfi(FfiType val) { return FromDart(val); } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; } // namespace tonic diff --git a/testing/dart_fixture.cc b/testing/dart_fixture.cc index 0f92085406098..ab0a15c9f7170 100644 --- a/testing/dart_fixture.cc +++ b/testing/dart_fixture.cc @@ -74,4 +74,8 @@ void DartFixture::AddNativeCallback(std::string name, native_resolver_->AddNativeCallback(std::move(name), callback); } +void DartFixture::AddFfiNativeCallback(std::string name, void* callback_ptr) { + native_resolver_->AddFfiNativeCallback(std::move(name), callback_ptr); +} + } // namespace flutter::testing diff --git a/testing/dart_fixture.h b/testing/dart_fixture.h index 06c00e86e97d0..35a2aecfa05c2 100644 --- a/testing/dart_fixture.h +++ b/testing/dart_fixture.h @@ -29,6 +29,7 @@ class DartFixture { virtual Settings CreateSettingsForFixture(); void AddNativeCallback(std::string name, Dart_NativeFunction callback); + void AddFfiNativeCallback(std::string name, void* callback_ptr); protected: void SetSnapshotsAndAssets(Settings& settings); diff --git a/testing/test_dart_native_resolver.cc b/testing/test_dart_native_resolver.cc index b8a8f76963feb..f1123d18b751e 100644 --- a/testing/test_dart_native_resolver.cc +++ b/testing/test_dart_native_resolver.cc @@ -22,6 +22,10 @@ void TestDartNativeResolver::AddNativeCallback(std::string name, Dart_NativeFunction callback) { native_callbacks_[name] = callback; } +void TestDartNativeResolver::AddFfiNativeCallback(std::string name, + void* callback_ptr) { + ffi_native_callbacks_[name] = callback_ptr; +} Dart_NativeFunction TestDartNativeResolver::ResolveCallback( std::string name) const { @@ -33,6 +37,14 @@ Dart_NativeFunction TestDartNativeResolver::ResolveCallback( return found->second; } +void* TestDartNativeResolver::ResolveFfiCallback(std::string name) const { + auto found = ffi_native_callbacks_.find(name); + if (found == ffi_native_callbacks_.end()) { + return nullptr; + } + return found->second; +} + static std::mutex gIsolateResolversMutex; static std::map> gIsolateResolvers; @@ -65,6 +77,25 @@ static const uint8_t* DartNativeEntrySymbolCallback( return reinterpret_cast("¯\\_(ツ)_/¯"); } +void* TestDartNativeResolver::FfiNativeResolver(const char* name, + uintptr_t args_n) { + std::scoped_lock lock(gIsolateResolversMutex); + auto found = gIsolateResolvers.find(Dart_CurrentIsolate()); + if (found == gIsolateResolvers.end()) { + FML_LOG(ERROR) << "Could not resolve native method for :" << name; + return nullptr; + } + + if (auto resolver = found->second.lock()) { + return resolver->ResolveFfiCallback(name); + } else { + gIsolateResolvers.erase(found); + } + + FML_LOG(ERROR) << "Could not resolve native method for :" << name; + return nullptr; +} + void TestDartNativeResolver::SetNativeResolverForIsolate() { FML_CHECK(!Dart_IsError(Dart_RootLibrary())); auto result = Dart_SetNativeResolver(Dart_RootLibrary(), @@ -73,6 +104,10 @@ void TestDartNativeResolver::SetNativeResolverForIsolate() { FML_CHECK(!tonic::LogIfError(result)) << "Could not set native resolver in test."; + result = Dart_SetFfiNativeResolver(Dart_RootLibrary(), &FfiNativeResolver); + FML_CHECK(!tonic::LogIfError(result)) + << "Could not set FFI native resolver in test."; + std::scoped_lock lock(gIsolateResolversMutex); gIsolateResolvers[Dart_CurrentIsolate()] = shared_from_this(); diff --git a/testing/test_dart_native_resolver.h b/testing/test_dart_native_resolver.h index 66050def9b221..ee82efc3c1b59 100644 --- a/testing/test_dart_native_resolver.h +++ b/testing/test_dart_native_resolver.h @@ -35,18 +35,22 @@ class TestDartNativeResolver ~TestDartNativeResolver(); void AddNativeCallback(std::string name, Dart_NativeFunction callback); + void AddFfiNativeCallback(std::string name, void* callback_ptr); void SetNativeResolverForIsolate(); private: std::map native_callbacks_; + std::map ffi_native_callbacks_; Dart_NativeFunction ResolveCallback(std::string name) const; + void* ResolveFfiCallback(std::string name) const; static Dart_NativeFunction DartNativeEntryResolverCallback( Dart_Handle dart_name, int num_of_arguments, bool* auto_setup_scope); + static void* FfiNativeResolver(const char* name, uintptr_t args_n); FML_DISALLOW_COPY_AND_ASSIGN(TestDartNativeResolver); }; diff --git a/third_party/tonic/converter/dart_converter.h b/third_party/tonic/converter/dart_converter.h index 2ba75a9c96aa9..0947241130a3b 100644 --- a/third_party/tonic/converter/dart_converter.h +++ b/third_party/tonic/converter/dart_converter.h @@ -36,30 +36,53 @@ struct DartConverterTypes { using ValueType = T; }; +template <> +struct DartConverter { + using FfiType = void; + static constexpr const char* kFfiRepresentation = "Void"; + static constexpr const char* kDartRepresentation = "void"; + static constexpr bool kAllowedInLeafCall = true; + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } +}; + //////////////////////////////////////////////////////////////////////////////// // Boolean template <> struct DartConverter { - static Dart_Handle ToDart(bool val) { return Dart_NewBoolean(val); } + using NativeType = bool; + using FfiType = bool; + static constexpr const char* kFfiRepresentation = "Bool"; + static constexpr const char* kDartRepresentation = "bool"; + static constexpr bool kAllowedInLeafCall = true; + + static Dart_Handle ToDart(NativeType val) { return Dart_NewBoolean(val); } static void SetReturnValue(Dart_NativeArguments args, bool val) { Dart_SetBooleanReturnValue(args, val); } - static bool FromDart(Dart_Handle handle) { + static NativeType FromDart(Dart_Handle handle) { bool result = 0; Dart_BooleanValue(handle, &result); return result; } - static bool FromArguments(Dart_NativeArguments args, - int index, - Dart_Handle& exception) { + static NativeType FromArguments(Dart_NativeArguments args, + int index, + Dart_Handle& exception) { bool result = false; Dart_GetNativeBooleanArgument(args, index, &result); return result; } + + static NativeType FromFfi(FfiType val) { return val; } + static FfiType ToFfi(NativeType val) { return val; } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; //////////////////////////////////////////////////////////////////////////////// @@ -67,6 +90,10 @@ struct DartConverter { template struct DartConverterInteger { + using FfiType = T; + static constexpr const char* kDartRepresentation = "int"; + static constexpr bool kAllowedInLeafCall = true; + static Dart_Handle ToDart(T val) { return Dart_NewInteger(val); } static void SetReturnValue(Dart_NativeArguments args, T val) { @@ -86,6 +113,24 @@ struct DartConverterInteger { Dart_GetNativeIntegerArgument(args, index, &result); return static_cast(result); } + static T FromFfi(FfiType val) { return val; } + static FfiType ToFfi(T val) { return val; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + // Note: Returns the correct bit-width for the host architecture. + static const char* GetFfiRepresentation() { + if (sizeof(T) == 4) { + if (std::is_signed()) { + return "Int32"; + } + return "Uint32"; + } + TONIC_DCHECK(sizeof(T) == 8); + if (std::is_signed()) { + return "Int64"; + } + return "Uint64"; + } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; template <> @@ -106,6 +151,11 @@ struct DartConverter template <> struct DartConverter { + using FfiType = unsigned long long; + static constexpr const char* kFfiRepresentation = "Uint64"; + static constexpr const char* kDartRepresentation = "int"; + static constexpr bool kAllowedInLeafCall = true; + // TODO(abarth): The Dart VM API doesn't yet have an entry-point for // an unsigned 64-bit type. We will need to add a Dart API for // constructing an integer from uint64_t. @@ -137,10 +187,26 @@ struct DartConverter { Dart_GetNativeIntegerArgument(args, index, &result); return result; } + + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } + static FfiType FromFfi(FfiType val) { + TONIC_DCHECK(val <= 0x7fffffffffffffffLL); + return val; + } + // FFI does a bitwise conversion from uint64_t in C to int64 in Dart. + static FfiType ToFfi(FfiType val) { + TONIC_DCHECK(val <= 0x7fffffffffffffffLL); + return val; + } }; template struct DartConverterFloatingPoint { + using FfiType = T; + static constexpr bool kAllowedInLeafCall = true; + static Dart_Handle ToDart(T val) { return Dart_NewDouble(val); } static void SetReturnValue(Dart_NativeArguments args, T val) { @@ -160,19 +226,38 @@ struct DartConverterFloatingPoint { Dart_GetNativeDoubleArgument(args, index, &result); return result; } + + static T FromFfi(FfiType val) { return val; } + static FfiType ToFfi(T val) { return val; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; template <> -struct DartConverter : public DartConverterFloatingPoint {}; +struct DartConverter : public DartConverterFloatingPoint { + static constexpr const char* kFfiRepresentation = "Float"; + static constexpr const char* kDartRepresentation = "double"; + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } +}; template <> -struct DartConverter : public DartConverterFloatingPoint {}; +struct DartConverter : public DartConverterFloatingPoint { + static constexpr const char* kFfiRepresentation = "Double"; + static constexpr const char* kDartRepresentation = "double"; + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } +}; //////////////////////////////////////////////////////////////////////////////// // Enum Classes template struct DartConverter::value>::type> { + using FfiType = int32_t; + static constexpr const char* kFfiRepresentation = "Int32"; + static constexpr const char* kDartRepresentation = "int"; + static constexpr bool kAllowedInLeafCall = true; + static Dart_Handle ToDart(T val) { return Dart_NewInteger( static_cast::type>(val)); @@ -196,6 +281,11 @@ struct DartConverter::value>::type> { Dart_GetNativeIntegerArgument(args, index, &result); return static_cast(result); } + + static T FromFfi(FfiType val) { return static_cast(val); } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; //////////////////////////////////////////////////////////////////////////////// @@ -203,17 +293,22 @@ struct DartConverter::value>::type> { template <> struct DartConverter { - static Dart_Handle ToDart(const std::string& val) { + using NativeType = std::string; + using FfiType = Dart_Handle; + static constexpr const char* kFfiRepresentation = "Handle"; + static constexpr const char* kDartRepresentation = "String"; + static constexpr bool kAllowedInLeafCall = false; + + static Dart_Handle ToDart(const NativeType& val) { return Dart_NewStringFromUTF8(reinterpret_cast(val.data()), val.length()); } - static void SetReturnValue(Dart_NativeArguments args, - const std::string& val) { + static void SetReturnValue(Dart_NativeArguments args, const NativeType& val) { Dart_SetReturnValue(args, ToDart(val)); } - static std::string FromDart(Dart_Handle handle) { + static NativeType FromDart(Dart_Handle handle) { uint8_t* data = nullptr; intptr_t length = 0; if (Dart_IsError(Dart_StringToUTF8(handle, &data, &length))) @@ -221,26 +316,37 @@ struct DartConverter { return std::string(reinterpret_cast(data), length); } - static std::string FromArguments(Dart_NativeArguments args, - int index, - Dart_Handle& exception) { + static NativeType FromArguments(Dart_NativeArguments args, + int index, + Dart_Handle& exception) { return FromDart(Dart_GetNativeArgument(args, index)); } + + static NativeType FromFfi(FfiType val) { return FromDart(val); } + static FfiType ToFfi(NativeType val) { return ToDart(val); } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; template <> struct DartConverter { - static Dart_Handle ToDart(const std::u16string& val) { + using NativeType = std::u16string; + using FfiType = Dart_Handle; + static constexpr const char* kFfiRepresentation = "Handle"; + static constexpr const char* kDartRepresentation = "String"; + static constexpr bool kAllowedInLeafCall = false; + + static Dart_Handle ToDart(const NativeType& val) { return Dart_NewStringFromUTF16( reinterpret_cast(val.data()), val.length()); } - static void SetReturnValue(Dart_NativeArguments args, - const std::u16string& val) { + static void SetReturnValue(Dart_NativeArguments args, const NativeType& val) { Dart_SetReturnValue(args, ToDart(val)); } - static std::u16string FromDart(Dart_Handle handle) { + static NativeType FromDart(Dart_Handle handle) { intptr_t length = 0; Dart_StringLength(handle, &length); std::vector data(length); @@ -248,11 +354,17 @@ struct DartConverter { return std::u16string(reinterpret_cast(data.data()), length); } - static std::u16string FromArguments(Dart_NativeArguments args, - int index, - Dart_Handle& exception) { + static NativeType FromArguments(Dart_NativeArguments args, + int index, + Dart_Handle& exception) { return FromDart(Dart_GetNativeArgument(args, index)); } + + static NativeType FromFfi(FfiType val) { return FromDart(val); } + static FfiType ToFfi(NativeType val) { return ToDart(val); } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; template <> @@ -360,6 +472,11 @@ struct DartListFactory { template struct DartConverter> { + using FfiType = Dart_Handle; + static constexpr const char* kFfiRepresentation = "Handle"; + static constexpr const char* kDartRepresentation = "List"; + static constexpr bool kAllowedInLeafCall = false; + using ValueType = typename DartConverterTypes::ValueType; using ConverterType = typename DartConverterTypes::ConverterType; @@ -413,6 +530,12 @@ struct DartConverter> { Dart_Handle& exception) { return FromDart(Dart_GetNativeArgument(args, index)); } + + static std::vector FromFfi(FfiType val) { return FromDart(val); } + static FfiType ToFfi(std::vector val) { return ToDart(val); } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; //////////////////////////////////////////////////////////////////////////////// @@ -420,19 +543,31 @@ struct DartConverter> { template <> struct DartConverter { - static Dart_Handle ToDart(Dart_Handle val) { return val; } + using NativeType = Dart_Handle; + using FfiType = Dart_Handle; + static constexpr const char* kFfiRepresentation = "Handle"; + static constexpr const char* kDartRepresentation = "Object"; + static constexpr bool kAllowedInLeafCall = false; + + static Dart_Handle ToDart(NativeType val) { return val; } static void SetReturnValue(Dart_NativeArguments args, Dart_Handle val) { Dart_SetReturnValue(args, val); } - static Dart_Handle FromDart(Dart_Handle handle) { return handle; } + static NativeType FromDart(Dart_Handle handle) { return handle; } - static Dart_Handle FromArguments(Dart_NativeArguments args, - int index, - Dart_Handle& exception) { + static NativeType FromArguments(Dart_NativeArguments args, + int index, + Dart_Handle& exception) { return Dart_GetNativeArgument(args, index); } + + static NativeType FromFfi(FfiType val) { return val; } + static FfiType ToFfi(NativeType val) { return val; } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; //////////////////////////////////////////////////////////////////////////////// diff --git a/third_party/tonic/dart_args.h b/third_party/tonic/dart_args.h index cb9d0e3d273c2..438ff7082bc81 100644 --- a/third_party/tonic/dart_args.h +++ b/third_party/tonic/dart_args.h @@ -5,6 +5,8 @@ #ifndef LIB_TONIC_DART_ARGS_H_ #define LIB_TONIC_DART_ARGS_H_ +#include +#include #include #include @@ -101,6 +103,8 @@ void DartReturn(T result, Dart_NativeArguments args) { template class DartDispatcher {}; +// Match functions on the form: +// `void f(ArgTypes...)` template struct DartDispatcher, void (*)(ArgTypes...)> : public DartArgHolder... { @@ -116,6 +120,8 @@ struct DartDispatcher, void (*)(ArgTypes...)> } }; +// Match functions on the form: +// `ResultType f(ArgTypes...)` template struct DartDispatcher, ResultType (*)(ArgTypes...)> : public DartArgHolder... { @@ -137,6 +143,8 @@ struct DartDispatcher, ResultType (*)(ArgTypes...)> } }; +// Match instance methods on the form: +// `void C::m(ArgTypes...)` template struct DartDispatcher, void (C::*)(ArgTypes...)> : public DartArgHolder... { @@ -153,6 +161,8 @@ struct DartDispatcher, void (C::*)(ArgTypes...)> } }; +// Match instance methods on the form: +// `ReturnType (C::m)(ArgTypes...) const` template , } }; +// Match instance methods on the form: +// `ReturnType (C::m)(ArgTypes...)` template AssociateWithDartWrapper(wrapper); } +// Templates to automatically setup static entry points for FFI Native +// functions. +// Entry points for instance methods take the instance as the first argument and +// call the given method with the remaining arguments. +// Arguments will automatically get converted to and from their FFI +// representations with the DartConverter templates. +// +// @tparam C The type of the receiver. Or `void` if there is no receiver. +// @tparam Signature The signature of the function being dispatched to. +// @tparam function The function pointer being dispatched to. +template +struct FfiDispatcher; + +// Concatenate the FFI representation of each argument to the stream, +// serialising them into a comma separated list. +// Example: "Handle, Bool, Uint64" +template +void WriteFfiArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::type>::type>::GetFfiRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + WriteFfiArguments(stream); + } +} + +// Concatenate the Dart representation of each argument to the stream, +// serialising them into a comma separated list. +// Example: "Object, bool, int" +template +void WriteDartArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter< + typename std::remove_const::type>:: + type>::GetDartRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + WriteDartArguments(stream); + } +} + +// Logical 'and' together whether each argument is allowed in a leaf call. +template +bool AllowedInLeafCall() { + bool result = tonic::DartConverter::type>::type>::AllowedInLeafCall(); + if constexpr (sizeof...(Args) > 0) { + result &= AllowedInLeafCall(); + } + return result; +} + +// Match `Return function(...)`. +template +struct FfiDispatcher { + using FfiReturn = typename DartConverter::FfiType; + static const size_t n_args = sizeof...(Args); + + // Static C entry-point with Dart FFI signature. + static FfiReturn Call( + typename DartConverter::type>::type>::FfiType... args) { + // Call C++ function, forwarding converted native arguments. + return DartConverter::ToFfi(function( + DartConverter::type>::type>::FromFfi(args)...)); + } + + static bool AllowedAsLeafCall() { + if constexpr (sizeof...(Args) > 0) { + return AllowedInLeafCall() && AllowedInLeafCall(); + } + return AllowedInLeafCall(); + } + + static const char* GetReturnFfiRepresentation() { + return tonic::DartConverter::GetFfiRepresentation(); + } + + static const char* GetReturnDartRepresentation() { + return tonic::DartConverter::GetDartRepresentation(); + } + + static void WriteFfiArguments(std::ostringstream* stream) { + if constexpr (sizeof...(Args) > 0) { + ::tonic::WriteFfiArguments(stream); + } + } + + static void WriteDartArguments(std::ostringstream* stream) { + if constexpr (sizeof...(Args) > 0) { + ::tonic::WriteDartArguments(stream); + } + } +}; + +// Match `Return C::method(...)`. +template +struct FfiDispatcher { + using FfiReturn = typename DartConverter::FfiType; + static const size_t n_args = sizeof...(Args); + + // Static C entry-point with Dart FFI signature. + static FfiReturn Call( + C* receiver, + typename DartConverter::type>::type>::FfiType... args) { + // Call C++ method on receiver, forwarding converted native arguments. + return DartConverter::ToFfi((receiver->*method)( + DartConverter::type>::type>::FromFfi(args)...)); + } + + static bool AllowedAsLeafCall() { + if constexpr (sizeof...(Args) > 0) { + return AllowedInLeafCall() && AllowedInLeafCall(); + } + return AllowedInLeafCall(); + } + + static const char* GetReturnFfiRepresentation() { + return tonic::DartConverter::GetFfiRepresentation(); + } + + static const char* GetReturnDartRepresentation() { + return tonic::DartConverter::GetDartRepresentation(); + } + + static void WriteFfiArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::GetFfiRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + ::tonic::WriteFfiArguments(stream); + } + } + + static void WriteDartArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::GetDartRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + ::tonic::WriteDartArguments(stream); + } + } +}; + +// Match `Return C::method(...) const`. +template +struct FfiDispatcher { + using FfiReturn = typename DartConverter::FfiType; + static const size_t n_args = sizeof...(Args); + + // Static C entry-point with Dart FFI signature. + static FfiReturn Call( + C* receiver, + typename DartConverter::type>::type>::FfiType... args) { + // Call C++ method on receiver, forwarding converted native arguments. + return DartConverter::ToFfi((receiver->*method)( + DartConverter::type>::type>::FromFfi(args)...)); + } + + static bool AllowedAsLeafCall() { + if constexpr (sizeof...(Args) > 0) { + return AllowedInLeafCall() && AllowedInLeafCall(); + } + return AllowedInLeafCall(); + } + + static const char* GetReturnFfiRepresentation() { + return tonic::DartConverter::GetFfiRepresentation(); + } + + static const char* GetReturnDartRepresentation() { + return tonic::DartConverter::GetDartRepresentation(); + } + + static void WriteFfiArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::GetFfiRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + ::tonic::WriteFfiArguments(stream); + } + } + + static void WriteDartArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::GetDartRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + ::tonic::WriteDartArguments(stream); + } + } +}; + +// `void` specialisation since we can't declare `ToFfi` to take void rvalues. +// Match `void function(...)`. +template +struct FfiDispatcher { + static const size_t n_args = sizeof...(Args); + + // Static C entry-point with Dart FFI signature. + static void Call( + typename DartConverter::type>::type>::FfiType... args) { + // Call C++ function, forwarding converted native arguments. + function( + DartConverter::type>::type>::FromFfi(args)...); + } + + static bool AllowedAsLeafCall() { + if constexpr (sizeof...(Args) > 0) { + return AllowedInLeafCall(); + } + return true; + } + + static const char* GetReturnFfiRepresentation() { + return tonic::DartConverter::GetFfiRepresentation(); + } + + static const char* GetReturnDartRepresentation() { + return tonic::DartConverter::GetDartRepresentation(); + } + + static void WriteFfiArguments(std::ostringstream* stream) { + if constexpr (sizeof...(Args) > 0) { + ::tonic::WriteFfiArguments(stream); + } + } + + static void WriteDartArguments(std::ostringstream* stream) { + if constexpr (sizeof...(Args) > 0) { + ::tonic::WriteDartArguments(stream); + } + } +}; + +// `void` specialisation since we can't declare `ToFfi` to take void rvalues. +// Match `void C::method(...)`. +template +struct FfiDispatcher { + static const size_t n_args = sizeof...(Args); + + // Static C entry-point with Dart FFI signature. + static void Call( + C* receiver, + typename DartConverter::type>::type>::FfiType... args) { + // Call C++ method on receiver, forwarding converted native arguments. + (receiver->*method)( + DartConverter::type>::type>::FromFfi(args)...); + } + + static bool AllowedAsLeafCall() { + if constexpr (sizeof...(Args) > 0) { + return AllowedInLeafCall(); + } + return true; + } + + static const char* GetReturnFfiRepresentation() { + return tonic::DartConverter::GetFfiRepresentation(); + } + + static const char* GetReturnDartRepresentation() { + return tonic::DartConverter::GetDartRepresentation(); + } + + static void WriteFfiArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::GetFfiRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + ::tonic::WriteFfiArguments(stream); + } + } + + static void WriteDartArguments(std::ostringstream* stream) { + *stream << tonic::DartConverter::GetDartRepresentation(); + if constexpr (sizeof...(Args) > 0) { + *stream << ", "; + ::tonic::WriteDartArguments(stream); + } + } +}; + } // namespace tonic #endif // LIB_TONIC_DART_ARGS_H_ diff --git a/third_party/tonic/dart_wrappable.h b/third_party/tonic/dart_wrappable.h index 1a914bc9d51e4..40532bf0cb265 100644 --- a/third_party/tonic/dart_wrappable.h +++ b/third_party/tonic/dart_wrappable.h @@ -102,6 +102,11 @@ struct DartConverter< T*, typename std::enable_if< std::is_convertible::value>::type> { + using FfiType = T*; + static constexpr const char* kFfiRepresentation = "Pointer"; + static constexpr const char* kDartRepresentation = "Pointer"; + static constexpr bool kAllowedInLeafCall = true; + static Dart_Handle ToDart(DartWrappable* val) { if (!val) { return Dart_Null(); @@ -148,6 +153,12 @@ struct DartConverter< return static_cast( DartConverterWrappable::FromArguments(args, index, exception)); } + + static T* FromFfi(FfiType val) { return val; } + static FfiType ToFfi(T* val) { return val; } + static const char* GetFfiRepresentation() { return kFfiRepresentation; } + static const char* GetDartRepresentation() { return kDartRepresentation; } + static bool AllowedInLeafCall() { return kAllowedInLeafCall; } }; //////////////////////////////////////////////////////////////////////////////// @@ -157,27 +168,39 @@ struct DartConverter< template