#include #include #include #include #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS true #endif #include #include #include #include #ifdef WIN32 #include "win32-dlfcn.h" #else #include #endif #include "ref-napi.h" /* define FFI_BUILDING before including ffi.h because this is a static build */ #define FFI_BUILDING #include #define FFI_ASYNC_ERROR static_cast(1) namespace FFI { using namespace Napi; class InstanceData; /* * Class used to store stuff during async ffi_call() invokations. */ class AsyncCallParams { public: explicit AsyncCallParams(Env env_) : env(env_) {} Env env; ffi_status result; std::string err; ffi_cif* cif; char* fn; char* res; void** argv; FunctionReference callback; uv_work_t req; }; class FFI { public: static Object InitializeStaticFunctions(Env env); static void InitializeBindings(Env env, Object target); protected: static Value FFIPrepCif(const Napi::CallbackInfo& args); static Value FFIPrepCifVar(const Napi::CallbackInfo& args); static void FFICall(const Napi::CallbackInfo& args); static void FFICallAsync(const Napi::CallbackInfo& args); static void AsyncFFICall(uv_work_t* req); static void FinishAsyncFFICall(uv_work_t* req, int status); }; /* * One of these structs gets created for each `ffi.Callback()` invokation in * JavaScript-land. It contains all the necessary information when invoking the * pointer to proxy back to JS-land properly. It gets created by * `ffi_closure_alloc()`, and free'd in the closure_pointer_cb function. */ struct callback_info { callback_info() { } ffi_closure closure; // the actual `ffi_closure` instance get inlined void* code; // the executable function pointer FunctionReference errorFunction; // JS callback function for reporting caught exceptions for the process' event loop FunctionReference function; // JS callback function the closure represents // these two are required for creating proper sized WrapPointer buffer instances int argc; // the number of arguments this function expects size_t resultSize; // the size of the result pointer InstanceData* instance_data; }; class ThreadedCallbackInvokation; class CallbackInfo { public: static Function Initialize(Env env); static void WatcherCallback(uv_async_t* w); protected: static void DispatchToV8(callback_info* self, void* retval, void** parameters, bool dispatched = false); static void Invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data); static Value Callback(const Napi::CallbackInfo& info); }; /** * Synchronization object to ensure following order of execution: * -> WaitForExecution() invoked * -> SignalDoneExecuting() returned * -> WaitForExecution() returned * * ^WaitForExecution() must always be called from the thread which owns the object */ class ThreadedCallbackInvokation { public: ThreadedCallbackInvokation(callback_info* cbinfo, void* retval, void** parameters); ~ThreadedCallbackInvokation(); void SignalDoneExecuting(); void WaitForExecution(); void* m_retval; void** m_parameters; callback_info* m_cbinfo; private: uv_cond_t m_cond; uv_mutex_t m_mutex; }; class InstanceData final { public: explicit InstanceData(Env env_) : env(env_) {} Env env; RefNapi::Instance* ref_napi_instance = nullptr; void Dispose(); #ifdef WIN32 DWORD thread; #else uv_thread_t thread; #endif uv_mutex_t mutex; std::queue queue; uv_async_t async; static InstanceData* Get(Env env); }; TypedArray WrapPointerImpl(Env env, char* ptr, size_t length); char* GetBufferDataImpl(Value val); template inline TypedArray WrapPointer(Env env, T* ptr, size_t length = 0) { return WrapPointerImpl(env, reinterpret_cast(ptr), length); } template inline T* GetBufferData(Value val) { return reinterpret_cast(GetBufferDataImpl(val)); } }