| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef LIB_TONIC_DART_ARGS_H_ | |
| #define LIB_TONIC_DART_ARGS_H_ | |
| #include <iostream> | |
| #include <sstream> | |
| #include <type_traits> | |
| #include <utility> | |
| #include "third_party/dart/runtime/include/dart_api.h" | |
| #include "tonic/converter/dart_converter.h" | |
| #include "tonic/dart_wrappable.h" | |
| namespace tonic { | |
| class DartArgIterator { | |
| public: | |
| explicit DartArgIterator(Dart_NativeArguments args, int start_index = 1) | |
| : args_(args), index_(start_index), had_exception_(false) {} | |
| template <typename T> | |
| T GetNext() { | |
| if (had_exception_) | |
| return T(); | |
| Dart_Handle exception = nullptr; | |
| T arg = DartConverter<T>::FromArguments(args_, index_++, exception); | |
| if (exception) { | |
| had_exception_ = true; | |
| Dart_ThrowException(exception); | |
| } | |
| return arg; | |
| } | |
| bool had_exception() const { return had_exception_; } | |
| Dart_NativeArguments args() const { return args_; } | |
| private: | |
| Dart_NativeArguments args_; | |
| int index_; | |
| bool had_exception_; | |
| TONIC_DISALLOW_COPY_AND_ASSIGN(DartArgIterator); | |
| }; | |
| // Classes for generating and storing an argument pack of integer indices | |
| // (based on well-known "indices trick", see: http://goo.gl/bKKojn): | |
| template <size_t... indices> | |
| struct IndicesHolder {}; | |
| template <size_t requested_index, size_t... indices> | |
| struct IndicesGenerator { | |
| using type = typename IndicesGenerator<requested_index - 1, | |
| requested_index - 1, | |
| indices...>::type; | |
| }; | |
| template <size_t... indices> | |
| struct IndicesGenerator<0, indices...> { | |
| using type = IndicesHolder<indices...>; | |
| }; | |
| template <typename T> | |
| class IndicesForSignature {}; | |
| template <typename ResultType, typename... ArgTypes> | |
| struct IndicesForSignature<ResultType (*)(ArgTypes...)> { | |
| static const size_t count = sizeof...(ArgTypes); | |
| using type = typename IndicesGenerator<count>::type; | |
| }; | |
| template <typename C, typename ResultType, typename... ArgTypes> | |
| struct IndicesForSignature<ResultType (C::*)(ArgTypes...)> { | |
| static const size_t count = sizeof...(ArgTypes); | |
| using type = typename IndicesGenerator<count>::type; | |
| }; | |
| template <typename C, typename ResultType, typename... ArgTypes> | |
| struct IndicesForSignature<ResultType (C::*)(ArgTypes...) const> { | |
| static const size_t count = sizeof...(ArgTypes); | |
| using type = typename IndicesGenerator<count>::type; | |
| }; | |
| template <size_t index, typename ArgType> | |
| struct DartArgHolder { | |
| using ValueType = typename std::remove_const< | |
| typename std::remove_reference<ArgType>::type>::type; | |
| ValueType value; | |
| explicit DartArgHolder(DartArgIterator* it) | |
| : value(it->GetNext<ValueType>()) {} | |
| }; | |
| template <typename T> | |
| void DartReturn(T result, Dart_NativeArguments args) { | |
| DartConverter<T>::SetReturnValue(args, std::move(result)); | |
| } | |
| template <typename IndicesType, typename T> | |
| class DartDispatcher {}; | |
| // Match functions on the form: | |
| // `void f(ArgTypes...)` | |
| template <size_t... indices, typename... ArgTypes> | |
| struct DartDispatcher<IndicesHolder<indices...>, void (*)(ArgTypes...)> | |
| : public DartArgHolder<indices, ArgTypes>... { | |
| using FunctionPtr = void (*)(ArgTypes...); | |
| DartArgIterator* it_; | |
| explicit DartDispatcher(DartArgIterator* it) | |
| : DartArgHolder<indices, ArgTypes>(it)..., it_(it) {} | |
| void Dispatch(FunctionPtr func) { | |
| (*func)(DartArgHolder<indices, ArgTypes>::value...); | |
| } | |
| }; | |
| // Match functions on the form: | |
| // `ResultType f(ArgTypes...)` | |
| template <size_t... indices, typename ResultType, typename... ArgTypes> | |
| struct DartDispatcher<IndicesHolder<indices...>, ResultType (*)(ArgTypes...)> | |
| : public DartArgHolder<indices, ArgTypes>... { | |
| using FunctionPtr = ResultType (*)(ArgTypes...); | |
| using CtorResultType = ResultType; | |
| DartArgIterator* it_; | |
| explicit DartDispatcher(DartArgIterator* it) | |
| : DartArgHolder<indices, ArgTypes>(it)..., it_(it) {} | |
| void Dispatch(FunctionPtr func) { | |
| DartReturn((*func)(DartArgHolder<indices, ArgTypes>::value...), | |
| it_->args()); | |
| } | |
| ResultType DispatchCtor(FunctionPtr func) { | |
| return (*func)(DartArgHolder<indices, ArgTypes>::value...); | |
| } | |
| }; | |
| // Match instance methods on the form: | |
| // `void C::m(ArgTypes...)` | |
| template <size_t... indices, typename C, typename... ArgTypes> | |
| struct DartDispatcher<IndicesHolder<indices...>, void (C::*)(ArgTypes...)> | |
| : public DartArgHolder<indices, ArgTypes>... { | |
| using FunctionPtr = void (C::*)(ArgTypes...); | |
| DartArgIterator* it_; | |
| explicit DartDispatcher(DartArgIterator* it) | |
| : DartArgHolder<indices, ArgTypes>(it)..., it_(it) {} | |
| void Dispatch(FunctionPtr func) { | |
| (GetReceiver<C>(it_->args())->*func)( | |
| DartArgHolder<indices, ArgTypes>::value...); | |
| } | |
| }; | |
| // Match instance methods on the form: | |
| // `ReturnType (C::m)(ArgTypes...) const` | |
| template <size_t... indices, | |
| typename C, | |
| typename ReturnType, | |
| typename... ArgTypes> | |
| struct DartDispatcher<IndicesHolder<indices...>, | |
| ReturnType (C::*)(ArgTypes...) const> | |
| : public DartArgHolder<indices, ArgTypes>... { | |
| using FunctionPtr = ReturnType (C::*)(ArgTypes...) const; | |
| DartArgIterator* it_; | |
| explicit DartDispatcher(DartArgIterator* it) | |
| : DartArgHolder<indices, ArgTypes>(it)..., it_(it) {} | |
| void Dispatch(FunctionPtr func) { | |
| DartReturn((GetReceiver<C>(it_->args())->*func)( | |
| DartArgHolder<indices, ArgTypes>::value...), | |
| it_->args()); | |
| } | |
| }; | |
| // Match instance methods on the form: | |
| // `ReturnType (C::m)(ArgTypes...)` | |
| template <size_t... indices, | |
| typename C, | |
| typename ResultType, | |
| typename... ArgTypes> | |
| struct DartDispatcher<IndicesHolder<indices...>, ResultType (C::*)(ArgTypes...)> | |
| : public DartArgHolder<indices, ArgTypes>... { | |
| using FunctionPtr = ResultType (C::*)(ArgTypes...); | |
| DartArgIterator* it_; | |
| explicit DartDispatcher(DartArgIterator* it) | |
| : DartArgHolder<indices, ArgTypes>(it)..., it_(it) {} | |
| void Dispatch(FunctionPtr func) { | |
| DartReturn((GetReceiver<C>(it_->args())->*func)( | |
| DartArgHolder<indices, ArgTypes>::value...), | |
| it_->args()); | |
| } | |
| }; | |
| template <typename Sig> | |
| void DartCall(Sig func, Dart_NativeArguments args) { | |
| DartArgIterator it(args); | |
| using Indices = typename IndicesForSignature<Sig>::type; | |
| DartDispatcher<Indices, Sig> decoder(&it); | |
| if (it.had_exception()) | |
| return; | |
| decoder.Dispatch(func); | |
| } | |
| template <typename Sig> | |
| void DartCallStatic(Sig func, Dart_NativeArguments args) { | |
| DartArgIterator it(args, 0); | |
| using Indices = typename IndicesForSignature<Sig>::type; | |
| DartDispatcher<Indices, Sig> decoder(&it); | |
| if (it.had_exception()) | |
| return; | |
| decoder.Dispatch(func); | |
| } | |
| template <typename Sig> | |
| void DartCallConstructor(Sig func, Dart_NativeArguments args) { | |
| DartArgIterator it(args); | |
| using Indices = typename IndicesForSignature<Sig>::type; | |
| using Wrappable = typename DartDispatcher<Indices, Sig>::CtorResultType; | |
| Wrappable wrappable; | |
| { | |
| DartDispatcher<Indices, Sig> decoder(&it); | |
| if (it.had_exception()) | |
| return; | |
| wrappable = decoder.DispatchCtor(func); | |
| } | |
| Dart_Handle wrapper = Dart_GetNativeArgument(args, 0); | |
| TONIC_CHECK(!CheckAndHandleError(wrapper)); | |
| intptr_t native_fields[DartWrappable::kNumberOfNativeFields]; | |
| TONIC_CHECK(!CheckAndHandleError(Dart_GetNativeFieldsOfArgument( | |
| args, 0, DartWrappable::kNumberOfNativeFields, native_fields))); | |
| TONIC_CHECK(!native_fields[DartWrappable::kPeerIndex]); | |
| wrappable->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 <typename C, typename Signature, Signature function> | |
| struct FfiDispatcher; | |
| // Concatenate the FFI representation of each argument to the stream, | |
| // serialising them into a comma separated list. | |
| // Example: "Handle, Bool, Uint64" | |
| template <typename Arg, typename... Args> | |
| void WriteFfiArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Arg>::type>::type>::GetFfiRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| WriteFfiArguments<Args...>(stream); | |
| } | |
| } | |
| // Concatenate the Dart representation of each argument to the stream, | |
| // serialising them into a comma separated list. | |
| // Example: "Object, bool, int" | |
| template <typename Arg, typename... Args> | |
| void WriteDartArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter< | |
| typename std::remove_const<typename std::remove_reference<Arg>::type>:: | |
| type>::GetDartRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| WriteDartArguments<Args...>(stream); | |
| } | |
| } | |
| // Logical 'and' together whether each argument is allowed in a leaf call. | |
| template <typename Arg, typename... Args> | |
| bool AllowedInLeafCall() { | |
| bool result = tonic::DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Arg>::type>::type>::AllowedInLeafCall(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| result &= AllowedInLeafCall<Args...>(); | |
| } | |
| return result; | |
| } | |
| // Match `Return function(...)`. | |
| template <typename Return, typename... Args, Return (*function)(Args...)> | |
| struct FfiDispatcher<void, Return (*)(Args...), function> { | |
| using FfiReturn = typename DartConverter<Return>::FfiType; | |
| static const size_t n_args = sizeof...(Args); | |
| // Static C entry-point with Dart FFI signature. | |
| static FfiReturn Call( | |
| typename DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Args>::type>::type>::FfiType... args) { | |
| // Call C++ function, forwarding converted native arguments. | |
| return DartConverter<Return>::ToFfi(function( | |
| DartConverter<typename std::remove_const<typename std::remove_reference< | |
| Args>::type>::type>::FromFfi(args)...)); | |
| } | |
| static bool AllowedAsLeafCall() { | |
| if constexpr (sizeof...(Args) > 0) { | |
| return AllowedInLeafCall<Return>() && AllowedInLeafCall<Args...>(); | |
| } | |
| return AllowedInLeafCall<Return>(); | |
| } | |
| static const char* GetReturnFfiRepresentation() { | |
| return tonic::DartConverter<Return>::GetFfiRepresentation(); | |
| } | |
| static const char* GetReturnDartRepresentation() { | |
| return tonic::DartConverter<Return>::GetDartRepresentation(); | |
| } | |
| static void WriteFfiArguments(std::ostringstream* stream) { | |
| if constexpr (sizeof...(Args) > 0) { | |
| ::tonic::WriteFfiArguments<Args...>(stream); | |
| } | |
| } | |
| static void WriteDartArguments(std::ostringstream* stream) { | |
| if constexpr (sizeof...(Args) > 0) { | |
| ::tonic::WriteDartArguments<Args...>(stream); | |
| } | |
| } | |
| }; | |
| // Match `Return C::method(...)`. | |
| template <typename C, | |
| typename Return, | |
| typename... Args, | |
| Return (C::*method)(Args...)> | |
| struct FfiDispatcher<C, Return (C::*)(Args...), method> { | |
| using FfiReturn = typename DartConverter<Return>::FfiType; | |
| static const size_t n_args = sizeof...(Args); | |
| // Static C entry-point with Dart FFI signature. | |
| static FfiReturn Call( | |
| C* receiver, | |
| typename DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Args>::type>::type>::FfiType... args) { | |
| // Call C++ method on receiver, forwarding converted native arguments. | |
| return DartConverter<Return>::ToFfi((receiver->*method)( | |
| DartConverter<typename std::remove_const<typename std::remove_reference< | |
| Args>::type>::type>::FromFfi(args)...)); | |
| } | |
| static bool AllowedAsLeafCall() { | |
| if constexpr (sizeof...(Args) > 0) { | |
| return AllowedInLeafCall<Return>() && AllowedInLeafCall<Args...>(); | |
| } | |
| return AllowedInLeafCall<Return>(); | |
| } | |
| static const char* GetReturnFfiRepresentation() { | |
| return tonic::DartConverter<Return>::GetFfiRepresentation(); | |
| } | |
| static const char* GetReturnDartRepresentation() { | |
| return tonic::DartConverter<Return>::GetDartRepresentation(); | |
| } | |
| static void WriteFfiArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<C*>::GetFfiRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| ::tonic::WriteFfiArguments<Args...>(stream); | |
| } | |
| } | |
| static void WriteDartArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<C*>::GetDartRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| ::tonic::WriteDartArguments<Args...>(stream); | |
| } | |
| } | |
| }; | |
| // Match `Return C::method(...) const`. | |
| template <typename C, | |
| typename Return, | |
| typename... Args, | |
| Return (C::*method)(Args...) const> | |
| struct FfiDispatcher<C, Return (C::*)(Args...) const, method> { | |
| using FfiReturn = typename DartConverter<Return>::FfiType; | |
| static const size_t n_args = sizeof...(Args); | |
| // Static C entry-point with Dart FFI signature. | |
| static FfiReturn Call( | |
| C* receiver, | |
| typename DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Args>::type>::type>::FfiType... args) { | |
| // Call C++ method on receiver, forwarding converted native arguments. | |
| return DartConverter<Return>::ToFfi((receiver->*method)( | |
| DartConverter<typename std::remove_const<typename std::remove_reference< | |
| Args>::type>::type>::FromFfi(args)...)); | |
| } | |
| static bool AllowedAsLeafCall() { | |
| if constexpr (sizeof...(Args) > 0) { | |
| return AllowedInLeafCall<Return>() && AllowedInLeafCall<Args...>(); | |
| } | |
| return AllowedInLeafCall<Return>(); | |
| } | |
| static const char* GetReturnFfiRepresentation() { | |
| return tonic::DartConverter<Return>::GetFfiRepresentation(); | |
| } | |
| static const char* GetReturnDartRepresentation() { | |
| return tonic::DartConverter<Return>::GetDartRepresentation(); | |
| } | |
| static void WriteFfiArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<C*>::GetFfiRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| ::tonic::WriteFfiArguments<Args...>(stream); | |
| } | |
| } | |
| static void WriteDartArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<C*>::GetDartRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| ::tonic::WriteDartArguments<Args...>(stream); | |
| } | |
| } | |
| }; | |
| // `void` specialisation since we can't declare `ToFfi` to take void rvalues. | |
| // Match `void function(...)`. | |
| template <typename... Args, void (*function)(Args...)> | |
| struct FfiDispatcher<void, void (*)(Args...), function> { | |
| static const size_t n_args = sizeof...(Args); | |
| // Static C entry-point with Dart FFI signature. | |
| static void Call( | |
| typename DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Args>::type>::type>::FfiType... args) { | |
| // Call C++ function, forwarding converted native arguments. | |
| function( | |
| DartConverter<typename std::remove_const<typename std::remove_reference< | |
| Args>::type>::type>::FromFfi(args)...); | |
| } | |
| static bool AllowedAsLeafCall() { | |
| if constexpr (sizeof...(Args) > 0) { | |
| return AllowedInLeafCall<Args...>(); | |
| } | |
| return true; | |
| } | |
| static const char* GetReturnFfiRepresentation() { | |
| return tonic::DartConverter<void>::GetFfiRepresentation(); | |
| } | |
| static const char* GetReturnDartRepresentation() { | |
| return tonic::DartConverter<void>::GetDartRepresentation(); | |
| } | |
| static void WriteFfiArguments(std::ostringstream* stream) { | |
| if constexpr (sizeof...(Args) > 0) { | |
| ::tonic::WriteFfiArguments<Args...>(stream); | |
| } | |
| } | |
| static void WriteDartArguments(std::ostringstream* stream) { | |
| if constexpr (sizeof...(Args) > 0) { | |
| ::tonic::WriteDartArguments<Args...>(stream); | |
| } | |
| } | |
| }; | |
| // `void` specialisation since we can't declare `ToFfi` to take void rvalues. | |
| // Match `void C::method(...)`. | |
| template <typename C, typename... Args, void (C::*method)(Args...)> | |
| struct FfiDispatcher<C, void (C::*)(Args...), method> { | |
| static const size_t n_args = sizeof...(Args); | |
| // Static C entry-point with Dart FFI signature. | |
| static void Call( | |
| C* receiver, | |
| typename DartConverter<typename std::remove_const< | |
| typename std::remove_reference<Args>::type>::type>::FfiType... args) { | |
| // Call C++ method on receiver, forwarding converted native arguments. | |
| (receiver->*method)( | |
| DartConverter<typename std::remove_const<typename std::remove_reference< | |
| Args>::type>::type>::FromFfi(args)...); | |
| } | |
| static bool AllowedAsLeafCall() { | |
| if constexpr (sizeof...(Args) > 0) { | |
| return AllowedInLeafCall<Args...>(); | |
| } | |
| return true; | |
| } | |
| static const char* GetReturnFfiRepresentation() { | |
| return tonic::DartConverter<void>::GetFfiRepresentation(); | |
| } | |
| static const char* GetReturnDartRepresentation() { | |
| return tonic::DartConverter<void>::GetDartRepresentation(); | |
| } | |
| static void WriteFfiArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<C*>::GetFfiRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| ::tonic::WriteFfiArguments<Args...>(stream); | |
| } | |
| } | |
| static void WriteDartArguments(std::ostringstream* stream) { | |
| *stream << tonic::DartConverter<C*>::GetDartRepresentation(); | |
| if constexpr (sizeof...(Args) > 0) { | |
| *stream << ", "; | |
| ::tonic::WriteDartArguments<Args...>(stream); | |
| } | |
| } | |
| }; | |
| } // namespace tonic | |
| #endif // LIB_TONIC_DART_ARGS_H_ |