Skip to content

Commit

Permalink
Merge 6317148 into 0d1de61
Browse files Browse the repository at this point in the history
  • Loading branch information
addaleax committed May 17, 2020
2 parents 0d1de61 + 6317148 commit f29620f
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 105 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ os:
osx_image: xcode10

node_js:
- "8"
- "9"
- "10"
- "12"
- "14"

jobs:
exclude:
Expand Down
2 changes: 0 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ environment:
# Test against these versions of Node.js and io.js
matrix:
# node.js
- nodejs_version: "8"
- nodejs_version: "9"
- nodejs_version: "10"
- nodejs_version: "12"

Expand Down
3 changes: 2 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
],
'include_dirs': [
"<!@(node -p \"require('node-addon-api').include\")",
"<!@(node -p \"require('get-uv-event-loop-napi-h').include\")"
"<!@(node -p \"require('get-uv-event-loop-napi-h').include\")",
"<!@(node -p \"require('ref-napi/lib/get-paths').include\")",
],
'dependencies': [
"<!(node -p \"require('node-addon-api').gyp\")",
Expand Down
10 changes: 8 additions & 2 deletions lib/bindings.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
'use strict';
var path = require('path');
module.exports = require('node-gyp-build')(path.join(__dirname, '..'));
const path = require('path');
const ref = require('ref-napi');
const assert = require('assert');

assert(ref.instance);

const bindings = require('node-gyp-build')(path.join(__dirname, '..'));
module.exports = bindings.initializeBindings(ref.instance);
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ffi-napi",
"version": "2.5.0",
"version": "3.0.0",
"license": "MIT",
"author": "Anna Henningsen <anna@addaleax.net>",
"contributors": [
Expand All @@ -22,20 +22,20 @@
],
"homepage": "http://github.com/node-ffi-napi/node-ffi-napi",
"engines": {
"node": ">=4"
"node": ">=10"
},
"main": "./lib/ffi",
"dependencies": {
"debug": "^3.1.0",
"debug": "^4.1.1",
"get-uv-event-loop-napi-h": "^1.0.5",
"node-addon-api": "1.6.1",
"node-addon-api": "^2.0.0",
"node-gyp-build": "^4.2.1",
"ref-napi": "^1.5.2",
"ref-napi": "^2.0.1",
"ref-struct-di": "^1.1.0"
},
"devDependencies": {
"fs-extra": "^5.0.0",
"mocha": "^5.0.0",
"fs-extra": "^9.0.0",
"mocha": "^7.1.1",
"nyc": "^15.0.0",
"prebuildify": "^3.0.4",
"prebuildify-ci": "^1.0.5",
Expand All @@ -45,7 +45,7 @@
"install": "node-gyp-build",
"prebuild": "prebuildify --napi",
"prepack": "prebuildify-ci download && ([ $(ls prebuilds | wc -l) = '4' ] || (echo 'Some prebuilds are missing'; exit 1))",
"test": "node-gyp rebuild --directory test && nyc mocha -gc --reporter spec"
"test": "node-gyp rebuild --directory test && nyc mocha --expose-gc --reporter spec"
},
"repository": {
"type": "git",
Expand Down
58 changes: 20 additions & 38 deletions src/callback_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@

namespace FFI {

std::unordered_map<napi_env, std::unique_ptr<PerEnvironmentData>>
CallbackInfo::per_environment;
uv_mutex_t CallbackInfo::per_environment_mutex;

/*
* Called when the `ffi_closure *` pointer (actually the "code" pointer) get's
* GC'd on the JavaScript side. In this case we have to unwrap the
* `callback_info *` struct, dispose of the JS function Persistent reference,
* then finally free the struct.
*/

void closure_pointer_cb(Env env, char *data, void *hint) {
void closure_pointer_cb(Env env, char* data, callback_info* hint) {
callback_info* info = static_cast<callback_info*>(hint);
// now we can free the closure data
info->~callback_info();
Expand All @@ -29,14 +25,14 @@ void closure_pointer_cb(Env env, char *data, void *hint) {
*/

void CallbackInfo::DispatchToV8(callback_info* info, void* retval, void** parameters, bool dispatched) {
Env env = info->per_env->env;
Env env = info->instance_data->env;
HandleScope handle_scope(env);

static const char* errorMessage =
"ffi fatal: callback has been garbage collected!";

try {
if (info->function.IsEmpty()) {
if (info->function.IsEmpty() || info->function.Value().IsEmpty()) {
// throw an error instead of segfaulting.
// see: https://github.com/rbranson/node-ffi/issues/72
if (dispatched) {
Expand Down Expand Up @@ -65,7 +61,7 @@ void CallbackInfo::DispatchToV8(callback_info* info, void* retval, void** parame
}

void CallbackInfo::WatcherCallback(uv_async_t* w) {
PerEnvironmentData* data = static_cast<PerEnvironmentData*>(w->data);
InstanceData* data = static_cast<InstanceData*>(w->data);
uv_mutex_lock(&data->mutex);

while (!data->queue.empty()) {
Expand Down Expand Up @@ -93,7 +89,7 @@ Value CallbackInfo::Callback(const Napi::CallbackInfo& args) {
}

// Args: cif pointer, JS function
ffi_cif* cif = args[0].As<Buffer<ffi_cif>>().Data();
ffi_cif* cif = GetBufferData<ffi_cif>(args[0]);
size_t resultSize = args[1].ToNumber().Int32Value();
int32_t argc = args[2].ToNumber();
Function errorReportCallback = args[3].As<Function>();
Expand All @@ -114,10 +110,7 @@ Value CallbackInfo::Callback(const Napi::CallbackInfo& args) {
cbInfo->argc = argc;
cbInfo->errorFunction = Reference<Function>::New(errorReportCallback, 1);
cbInfo->function = Reference<Function>::New(callback, 1);

uv_mutex_lock(&per_environment_mutex);
cbInfo->per_env = per_environment[env].get();
uv_mutex_unlock(&per_environment_mutex);
cbInfo->instance_data = InstanceData::Get(env);

// store a reference to the callback function pointer
// (not sure if this is actually needed...)
Expand All @@ -140,11 +133,10 @@ Value CallbackInfo::Callback(const Napi::CallbackInfo& args) {
throw e;
}

return Buffer<char>::New(env,
static_cast<char*>(code),
sizeof(void*),
closure_pointer_cb,
cbInfo);
TypedArray ret = WrapPointer(env, code, sizeof(void*));
ret.ArrayBuffer().
AddFinalizer(closure_pointer_cb, static_cast<char*>(code), cbInfo);
return ret;
}

/*
Expand All @@ -157,7 +149,7 @@ void CallbackInfo::Invoke(ffi_cif* cif,
void** parameters,
void* user_data) {
callback_info* info = static_cast<callback_info*>(user_data);
PerEnvironmentData* data = info->per_env;
InstanceData* data = info->instance_data;
#ifdef WIN32
if (data->thread == GetCurrentThreadId()) {
#else
Expand Down Expand Up @@ -191,38 +183,28 @@ void CallbackInfo::Invoke(ffi_cif* cif,
}
}

static uv_once_t init_per_env_mutex_once = UV_ONCE_INIT;

/*
* Init stuff.
*/

Function CallbackInfo::Initialize(Env env) {
Function fn = Function::New(env, Callback);


std::unique_ptr<PerEnvironmentData> data (new PerEnvironmentData(env));
InstanceData* instance_data = InstanceData::Get(env);
// initialize our threaded invokation stuff
#ifdef WIN32
data->thread = GetCurrentThreadId();
instance_data->thread = GetCurrentThreadId();
#else
data->thread = uv_thread_self();
instance_data->thread = uv_thread_self();
#endif
uv_async_init(uv_default_loop(), &data->async, CallbackInfo::WatcherCallback);
data->async.data = data.get();
uv_mutex_init(&data->mutex);
uv_loop_t* loop = nullptr;
napi_get_uv_event_loop(env, &loop);
uv_async_init(loop, &instance_data->async, CallbackInfo::WatcherCallback);
instance_data->async.data = instance_data;
uv_mutex_init(&instance_data->mutex);

// allow the event loop to exit while this is running
uv_unref(reinterpret_cast<uv_handle_t*>(&data->async));

uv_once(&init_per_env_mutex_once, []() {
uv_mutex_init(&per_environment_mutex);
});

uv_mutex_lock(&per_environment_mutex);
per_environment[env] = std::move(data);
uv_mutex_unlock(&per_environment_mutex);

uv_unref(reinterpret_cast<uv_handle_t*>(&instance_data->async));
return fn;
}

Expand Down
87 changes: 67 additions & 20 deletions src/ffi.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
#define NAPI_VERSION 6
#include "ffi.h"
#include "fficonfig.h"
#include <get-uv-event-loop-napi.h>

namespace FFI {

InstanceData* InstanceData::Get(Env env) {
void* d = nullptr;
napi_status status = napi_get_instance_data(env, &d);
assert(status == napi_ok);
return static_cast<InstanceData*>(d);
}

void InstanceData::Dispose() {
uv_close(reinterpret_cast<uv_handle_t*>(&async), [](uv_handle_t* handle) {
InstanceData* self = static_cast<InstanceData*>(handle->data);
uv_mutex_destroy(&self->mutex);
delete self;
});
}

TypedArray WrapPointerImpl(Env env, char* ptr, size_t length) {
InstanceData* data = InstanceData::Get(env);
assert(data->ref_napi_instance != nullptr);
return TypedArray(env, data->ref_napi_instance->WrapPointer(ptr, length));
}

char* GetBufferDataImpl(Value val) {
InstanceData* data = InstanceData::Get(val.Env());
assert(data->ref_napi_instance != nullptr);
return data->ref_napi_instance->GetBufferData(val);
}

static int __ffi_errno() { return errno; }

Object FFI::InitializeStaticFunctions(Env env) {
Expand Down Expand Up @@ -154,10 +182,10 @@ Value FFI::FFIPrepCif(const Napi::CallbackInfo& args) {
if (!args[3].IsBuffer())
throw TypeError::New(env, "prepCif(): Buffer required as atypes arg");

ffi_cif* cif = args[0].As<Buffer<ffi_cif>>().Data();
ffi_cif* cif = GetBufferData<ffi_cif>(args[0]);
uint32_t nargs = args[1].ToNumber();
ffi_type* rtype = args[2].As<Buffer<ffi_type>>().Data();
ffi_type** atypes = args[3].As<Buffer<ffi_type*>>().Data();
ffi_type* rtype = GetBufferData<ffi_type>(args[2]);
ffi_type** atypes = GetBufferData<ffi_type*>(args[3]);
ffi_abi abi = static_cast<ffi_abi>(args[4].ToNumber().Int32Value());

ffi_status status = ffi_prep_cif(cif, abi, nargs, rtype, atypes);
Expand Down Expand Up @@ -188,11 +216,11 @@ Value FFI::FFIPrepCifVar(const Napi::CallbackInfo& args) {
if (!args[3].IsBuffer())
throw TypeError::New(env, "prepCifVar(): Buffer required as atypes arg");

ffi_cif* cif = args[0].As<Buffer<ffi_cif>>().Data();
ffi_cif* cif = GetBufferData<ffi_cif>(args[0]);
uint32_t fargs = args[1].ToNumber();
uint32_t targs = args[2].ToNumber();
ffi_type* rtype = args[3].As<Buffer<ffi_type>>().Data();
ffi_type** atypes = args[4].As<Buffer<ffi_type*>>().Data();
ffi_type* rtype = GetBufferData<ffi_type>(args[3]);
ffi_type** atypes = GetBufferData<ffi_type*>(args[4]);
ffi_abi abi = static_cast<ffi_abi>(args[5].ToNumber().Int32Value());

ffi_status status = ffi_prep_cif_var(cif, abi, fargs, targs, rtype, atypes);
Expand All @@ -216,10 +244,10 @@ void FFI::FFICall(const Napi::CallbackInfo& args) {
throw TypeError::New(env, "ffi_call() requires 4 Buffer arguments!");
}

ffi_cif* cif = args[0].As<Buffer<ffi_cif>>().Data();
char* fn = args[1].As<Buffer<char>>().Data();
char* res = args[2].As<Buffer<char>>().Data();
void** fnargs = args[3].As<Buffer<void*>>().Data();
ffi_cif* cif = GetBufferData<ffi_cif>(args[0]);
char* fn = GetBufferData<char>(args[1]);
char* res = GetBufferData<char>(args[2]);
void** fnargs = GetBufferData<void*>(args[3]);

ffi_call(cif, FFI_FN(fn), static_cast<void*>(res), fnargs);
}
Expand All @@ -246,10 +274,10 @@ void FFI::FFICallAsync(const Napi::CallbackInfo& args) {

// store a persistent references to all the Buffers and the callback function
AsyncCallParams* p = new AsyncCallParams(env);
p->cif = args[0].As<Buffer<ffi_cif>>().Data();
p->fn = args[1].As<Buffer<char>>().Data();
p->res = args[2].As<Buffer<char>>().Data();
p->argv = args[3].As<Buffer<void*>>().Data();
p->cif = GetBufferData<ffi_cif>(args[0]);
p->fn = GetBufferData<char>(args[1]);
p->res = GetBufferData<char>(args[2]);
p->argv = GetBufferData<void*>(args[3]);

p->result = FFI_OK;
p->callback = Reference<Function>::New(args[4].As<Function>(), 1);
Expand Down Expand Up @@ -297,14 +325,33 @@ void FFI::FinishAsyncFFICall(uv_work_t* req, int status) {
delete p;
}

}
Value InitializeBindings(const Napi::CallbackInfo& args) {
Env env = args.Env();

static Napi::Object Init(Napi::Env env, Napi::Object exports) {
FFI::FFI::InitializeBindings(env, exports);
exports["StaticFunctions"] = FFI::FFI::InitializeStaticFunctions(env);
exports["Callback"] = FFI::CallbackInfo::Initialize(env);
assert(args[0].IsExternal());
InstanceData* data = InstanceData::Get(env);
data->ref_napi_instance = args[0].As<External<RefNapi::Instance>>().Data();

Object exports = Object::New(env);
FFI::InitializeBindings(env, exports);
exports["StaticFunctions"] = FFI::InitializeStaticFunctions(env);
exports["Callback"] = CallbackInfo::Initialize(env);
return exports;
}

NODE_API_MODULE(ffi_bindings, Init)
} // namespace FFI

Napi::Object BindingHook(Napi::Env env, Napi::Object exports) {
FFI::InstanceData* data = new FFI::InstanceData(env);
napi_status status = napi_set_instance_data(
env, data, [](napi_env env, void* data, void* hint) {
static_cast<FFI::InstanceData*>(data)->Dispose();
}, nullptr);
if (status != napi_ok) delete data;

exports["initializeBindings"] =
Napi::Function::New(env, FFI::InitializeBindings);
return exports;
}

NODE_API_MODULE(ffi_bindings, BindingHook)

0 comments on commit f29620f

Please sign in to comment.