2025-04-28 12:25:20 +08:00

161 lines
4.1 KiB
C++

#include <uv.h>
#include <napi.h>
#include <limits.h>
#include <errno.h>
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS true
#endif
#include <stdint.h>
#include <queue>
#include <memory>
#include <unordered_map>
#ifdef WIN32
#include "win32-dlfcn.h"
#else
#include <dlfcn.h>
#endif
#include "ref-napi.h"
/* define FFI_BUILDING before including ffi.h because this is a static build */
#define FFI_BUILDING
#include <ffi.h>
#define FFI_ASYNC_ERROR static_cast<ffi_status>(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<ThreadedCallbackInvokation*> queue;
uv_async_t async;
static InstanceData* Get(Env env);
};
TypedArray WrapPointerImpl(Env env, char* ptr, size_t length);
char* GetBufferDataImpl(Value val);
template <typename T>
inline TypedArray WrapPointer(Env env, T* ptr, size_t length = 0) {
return WrapPointerImpl(env, reinterpret_cast<char*>(ptr), length);
}
template <typename T>
inline T* GetBufferData(Value val) {
return reinterpret_cast<T*>(GetBufferDataImpl(val));
}
}