blob: 67659e012fbddaac40ee673d7b62411bfb3ac7af [file] [edit]
#if HAVE_FFI
#include "node_ffi.h"
#include <cstring>
#include "base_object-inl.h"
#include "env-inl.h"
#include "ffi/data.h"
#include "ffi/types.h"
#include "node_errors.h"
namespace node {
using v8::Array;
using v8::BigInt;
using v8::Context;
using v8::DontDelete;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Global;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::LocalVector;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Null;
using v8::Object;
using v8::PropertyAttribute;
using v8::ReadOnly;
using v8::String;
using v8::TryCatch;
using v8::Value;
using v8::WeakCallbackInfo;
using v8::WeakCallbackType;
namespace ffi {
DynamicLibrary::DynamicLibrary(Environment* env, Local<Object> object)
: BaseObject(env, object), lib_{}, handle_(nullptr), symbols_() {
MakeWeak();
}
DynamicLibrary::~DynamicLibrary() {
this->Close();
}
void DynamicLibrary::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackFieldWithSize("path", path_.capacity() + 1, "std::string");
size_t symbols_size = 0;
for (const auto& [name, ptr] : symbols_) {
symbols_size += name.capacity() + 1;
symbols_size += sizeof(ptr);
symbols_size += sizeof(decltype(symbols_)::value_type);
}
tracker->TrackFieldWithSize(
"symbols", symbols_size, "std::unordered_map<std::string, void*>");
}
void DynamicLibrary::Close() {
for (auto& [name, fn] : functions_) {
fn->closed = true;
fn->ptr = nullptr;
}
// Closing the library invalidates all registered callbacks. Node.js does not
// track or revoke callback pointers that have already been handed to native
// code. If native code calls a callback pointer after `close()` or
// `unregisterCallback()`, the behavior is undefined, not allowed, and
// dangerous: it can crash the process, produce incorrect output, or corrupt
// memory.
if (handle_ != nullptr) {
uv_dlclose(&lib_);
handle_ = nullptr;
}
symbols_.clear();
functions_.clear();
callbacks_.clear();
}
bool DynamicLibrary::ResolveSymbol(Environment* env,
const std::string& name,
void** ret) {
if (handle_ == nullptr) {
env->ThrowError("Library is closed");
return false;
}
auto existing = symbols_.find(name);
void* ptr;
if (existing != symbols_.end()) {
ptr = existing->second;
} else {
if (uv_dlsym(&lib_, name.c_str(), &ptr) != 0) {
std::string msg = std::string("dlsym failed: ") + uv_dlerror(&lib_);
env->ThrowError(msg.c_str());
return false;
}
}
*ret = ptr;
return true;
}
bool DynamicLibrary::PrepareFunction(Environment* env,
const std::string& name,
Local<Object> signature,
std::shared_ptr<FFIFunction>* ret,
bool* should_cache_symbol,
bool* should_cache_function) {
std::shared_ptr<FFIFunction> fn;
auto existing = functions_.find(name);
ffi_type* return_type = &ffi_type_void;
std::vector<ffi_type*> args;
*should_cache_symbol = false;
*should_cache_function = false;
if (!ParseFunctionSignature(env, name, signature, &return_type, &args)) {
return false;
}
if (existing == functions_.end()) {
void* ptr;
if (!ResolveSymbol(env, name, &ptr)) {
return false;
}
*should_cache_symbol = symbols_.find(name) == symbols_.end();
fn = std::make_shared<FFIFunction>(FFIFunction{.closed = false,
.ptr = ptr,
.cif = {},
.args = args,
.return_type = return_type});
ffi_status status = ffi_prep_cif(&fn->cif,
FFI_DEFAULT_ABI,
fn->args.size(),
fn->return_type,
fn->args.data());
if (status != FFI_OK) {
const char* msg = "ffi_prep_cif failed";
switch (status) {
case FFI_BAD_TYPEDEF:
msg = "ffi_prep_cif failed: bad typedef";
break;
case FFI_BAD_ABI:
msg = "ffi_prep_cif failed: bad ABI";
break;
default:
msg = "ffi_prep_cif failed: unknown error";
break;
}
env->ThrowError(msg);
return false;
}
*should_cache_function = true;
} else {
fn = existing->second;
if (!SignaturesMatch(*fn, return_type, args)) {
std::string msg = "Function " + name +
" was already requested with a different signature";
env->ThrowError(msg.c_str());
return false;
}
}
*ret = fn;
return true;
}
void DynamicLibrary::CleanupFunctionInfo(
const WeakCallbackInfo<FFIFunctionInfo>& data) {
FFIFunctionInfo* info = data.GetParameter();
info->fn.reset();
info->self.Reset();
delete info;
}
MaybeLocal<Function> DynamicLibrary::CreateFunction(
Environment* env,
const std::string& name,
const std::shared_ptr<FFIFunction>& fn) {
Isolate* isolate = env->isolate();
FFIFunctionInfo* info = new FFIFunctionInfo();
info->fn = fn;
Local<External> data = External::New(isolate, info);
MaybeLocal<Function> maybe_ret =
Function::New(env->context(), DynamicLibrary::InvokeFunction, data);
Local<Function> ret;
if (!maybe_ret.ToLocal(&ret)) {
return MaybeLocal<Function>();
}
Local<String> name_str;
if (!String::NewFromUtf8(isolate, name.c_str(), NewStringType::kNormal)
.ToLocal(&name_str)) {
return MaybeLocal<Function>();
}
info->self.Reset(isolate, ret);
info->self.SetWeak(
info, DynamicLibrary::CleanupFunctionInfo, WeakCallbackType::kParameter);
ret->SetName(name_str);
if (!ret->Set(
env->context(),
env->pointer_string(),
BigInt::NewFromUnsigned(
isolate,
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(fn->ptr))))
.FromMaybe(false)) {
return MaybeLocal<Function>();
}
return ret;
}
void DynamicLibrary::New(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args.IsConstructCall()) {
return THROW_ERR_CONSTRUCT_CALL_REQUIRED(
env,
"Class constructor DynamicLibrary cannot be invoked without 'new'");
}
THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
if (args.Length() < 1 || !args[0]->IsString()) {
env->ThrowTypeError("Library path must be a string");
return;
}
DynamicLibrary* lib = new DynamicLibrary(env, args.This());
Utf8Value filename(env->isolate(), args[0]);
if (ThrowIfContainsNullBytes(env, filename, "Library path")) {
return;
}
lib->path_ = std::string(*filename);
// Open the library
if (uv_dlopen(*filename, &lib->lib_) != 0) {
std::string msg = std::string("dlopen failed: ") + uv_dlerror(&lib->lib_);
env->ThrowError(msg.c_str());
return;
}
lib->handle_ = static_cast<void*>(lib->lib_.handle);
}
void DynamicLibrary::Close(const FunctionCallbackInfo<Value>& args) {
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
// Closing a library from one of its active callbacks is unsupported and
// dangerous. Callbacks must return before the owning library is closed.
lib->Close();
}
void DynamicLibrary::InvokeFunction(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
FFIFunctionInfo* info =
static_cast<FFIFunctionInfo*>(args.Data().As<External>()->Value());
FFIFunction* fn = info->fn.get();
if (fn == nullptr || fn->closed || fn->ptr == nullptr) {
env->ThrowError("Library is closed");
return;
}
// Convert arguments
unsigned int expected_args = fn->args.size();
unsigned int provided_args = args.Length();
if (provided_args != expected_args) {
std::string msg = "Invalid argument count: expected " +
std::to_string(expected_args) + ", got " +
std::to_string(provided_args);
env->ThrowError(msg.c_str());
return;
}
std::vector<uint64_t> values(expected_args, 0);
std::vector<void*> ffi_args(expected_args, nullptr);
std::vector<std::string> strings;
strings.reserve(expected_args);
for (unsigned int i = 0; i < expected_args; i++) {
uint8_t res = ToFFIArgument(env, i, fn->args[i], args[i], &values[i]);
if (!res) {
return;
}
// The argument is a string, we need to copy
if (res == 2) {
Utf8Value str(env->isolate(), args[i]);
if (*str == nullptr) {
env->ThrowTypeError(
("Argument " + std::to_string(i) + " must be a string").c_str());
return;
}
if (ThrowIfContainsNullBytes(env, str, "Argument " + std::to_string(i))) {
return;
}
strings.push_back(*str);
values[i] = reinterpret_cast<uint64_t>(strings.back().c_str());
ffi_args[i] = &values[i];
} else {
ffi_args[i] = &values[i];
}
}
void* result = nullptr;
if (fn->return_type->type != FFI_TYPE_VOID) {
result = Malloc(GetFFIReturnValueStorageSize(fn->return_type));
}
ffi_call(&fn->cif, FFI_FN(fn->ptr), result, ffi_args.data());
// Return result back to Javascript
ToJSReturnValue(env, args, fn->return_type, result);
free(result);
}
void DynamicLibrary::InvokeCallback(ffi_cif* cif,
void* ret,
void** args,
void* user_data) {
FFICallback* cb = static_cast<FFICallback*>(user_data);
// It is unsupported and dangerous for a callback to unregister itself or
// close its owning library while executing. The current invocation must
// return before teardown APIs are used.
if (cb->owner->handle_ == nullptr || cb->ptr == nullptr) {
if (ret != nullptr && cb->return_type->size > 0) {
std::memset(ret, 0, GetFFIReturnValueStorageSize(cb->return_type));
}
return;
}
if (std::this_thread::get_id() != cb->thread_id) {
FPrintF(stderr,
"Callbacks can only be invoked on the system thread they were "
"created on\n");
ABORT();
}
Environment* env = cb->env;
Isolate* isolate = env->isolate();
HandleScope handle_scope(isolate);
Local<Context> context = env->context();
if (cb->fn.IsEmpty()) {
if (ret != nullptr && cb->return_type->size > 0) {
std::memset(ret, 0, GetFFIReturnValueStorageSize(cb->return_type));
}
return;
}
size_t expected_args = cb->args.size();
LocalVector<Value> callback_args(isolate, expected_args);
for (size_t i = 0; i < expected_args; i++) {
if (args[i] == nullptr) {
callback_args[i] = Null(isolate);
continue;
} else {
callback_args[i] = ToJSArgument(isolate, cb->args[i], args[i]);
}
}
TryCatch try_catch(isolate);
Local<Function> callback = Local<Function>::New(isolate, cb->fn);
MaybeLocal<Value> result = callback->Call(
context, Undefined(isolate), expected_args, callback_args.data());
// Handle exceptions by crashing (can't propagate across FFI boundary)
if (try_catch.HasCaught()) {
FPrintF(stderr, "Callbacks cannot throw an exception\n");
ABORT();
}
Local<Value> result_val;
if (!result.ToLocal(&result_val)) {
if (try_catch.HasCaught()) {
FPrintF(stderr, "Callbacks cannot return an exception\n");
ABORT();
}
return;
}
if (result_val->IsPromise()) {
FPrintF(stderr, "Callbacks cannot return promises\n");
ABORT();
}
if (!ToFFIReturnValue(result_val, cb->return_type, ret)) {
FPrintF(stderr, "Callback returned invalid value for declared FFI type\n");
ABORT();
}
}
void DynamicLibrary::GetPath(const FunctionCallbackInfo<Value>& args) {
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
Local<String> path;
if (!String::NewFromUtf8(
args.GetIsolate(), lib->path_.c_str(), NewStringType::kNormal)
.ToLocal(&path)) {
return;
}
args.GetReturnValue().Set(path);
}
void DynamicLibrary::GetFunction(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
if (args.Length() < 1 || !args[0]->IsString()) {
env->ThrowTypeError("Function name must be a string");
return;
}
if (args.Length() < 2 || !args[1]->IsObject() || args[1]->IsArray()) {
env->ThrowTypeError("Function signature must be an object");
return;
}
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
Utf8Value name(isolate, args[0]);
if (ThrowIfContainsNullBytes(env, name, "Function name")) {
return;
}
std::shared_ptr<FFIFunction> fn;
bool should_cache_symbol;
bool should_cache_function;
Local<Object> signature = args[1].As<Object>();
if (!lib->PrepareFunction(env,
*name,
signature,
&fn,
&should_cache_symbol,
&should_cache_function)) {
return;
}
if (should_cache_symbol) {
lib->symbols_.emplace(*name, fn->ptr);
}
if (should_cache_function) {
lib->functions_.emplace(*name, fn);
}
MaybeLocal<Function> maybe_ret = lib->CreateFunction(env, *name, fn);
Local<Function> ret;
if (!maybe_ret.ToLocal(&ret)) {
return;
}
args.GetReturnValue().Set(ret);
}
void DynamicLibrary::GetFunctions(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
if (lib->handle_ == nullptr) {
env->ThrowError("Library is closed");
return;
}
Local<Object> functions = Object::New(isolate);
if (!functions->SetPrototype(context, Null(isolate)).FromMaybe(false)) {
return;
}
if (args.Length() > 0) {
if (!args[0]->IsObject() || args[0]->IsArray()) {
env->ThrowTypeError("Functions signatures must be an object");
return;
}
Local<Object> signatures = args[0].As<Object>();
Local<Array> keys;
if (!signatures->GetOwnPropertyNames(context).ToLocal(&keys)) {
return;
}
std::vector<ResolvedFunction> pending;
pending.reserve(keys->Length());
for (uint32_t i = 0; i < keys->Length(); i++) {
Local<Value> key;
Local<Value> signature;
if (!keys->Get(context, i).ToLocal(&key)) {
return;
}
Utf8Value name(isolate, key);
if (ThrowIfContainsNullBytes(env, name, "Function name")) {
return;
}
if (!signatures->Get(env->context(), key).ToLocal(&signature)) {
return;
}
if (!signature->IsObject() || signature->IsArray()) {
std::string msg = std::string("Signature of function ") + name.out() +
" must be an object";
env->ThrowTypeError(msg.c_str());
return;
}
std::shared_ptr<FFIFunction> fn;
bool should_cache_symbol;
bool should_cache_function;
Local<Object> signature_object = signature.As<Object>();
if (!lib->PrepareFunction(env,
*name,
signature_object,
&fn,
&should_cache_symbol,
&should_cache_function)) {
return;
}
pending.push_back(ResolvedFunction{
.name = *name,
.fn = fn,
.should_cache_symbol = should_cache_symbol,
.should_cache_function = should_cache_function,
});
}
for (const auto& item : pending) {
if (item.should_cache_symbol) {
lib->symbols_.emplace(item.name, item.fn->ptr);
}
if (item.should_cache_function) {
lib->functions_.emplace(item.name, item.fn);
}
}
for (const auto& item : pending) {
MaybeLocal<Function> maybe_ret =
lib->CreateFunction(env, item.name, item.fn);
Local<Function> ret;
if (!maybe_ret.ToLocal(&ret)) {
return;
}
Local<String> name_string;
if (!String::NewFromUtf8(
isolate, item.name.c_str(), NewStringType::kNormal)
.ToLocal(&name_string)) {
return;
}
if (!functions->Set(context, name_string, ret).FromMaybe(false)) {
return;
}
}
} else {
for (const auto& entry : lib->functions_) {
MaybeLocal<Function> maybe_fn =
lib->CreateFunction(env, entry.first, entry.second);
Local<Function> fn;
if (!maybe_fn.ToLocal(&fn)) {
return;
}
Local<String> name_string;
if (!String::NewFromUtf8(
isolate, entry.first.c_str(), NewStringType::kNormal)
.ToLocal(&name_string)) {
return;
}
if (!functions->Set(context, name_string, fn).FromMaybe(false)) {
return;
}
}
}
args.GetReturnValue().Set(functions);
}
void DynamicLibrary::GetSymbol(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
if (args.Length() < 1 || !args[0]->IsString()) {
env->ThrowTypeError("Symbol name must be a string");
return;
}
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
Utf8Value name(isolate, args[0]);
if (ThrowIfContainsNullBytes(env, name, "Symbol name")) {
return;
}
void* ptr;
if (!lib->ResolveSymbol(env, *name, &ptr)) {
return;
}
lib->symbols_.emplace(*name, ptr);
args.GetReturnValue().Set(BigInt::NewFromUnsigned(
isolate, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr))));
}
void DynamicLibrary::GetSymbols(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
if (lib->handle_ == nullptr) {
env->ThrowError("Library is closed");
return;
}
Local<Object> symbols = Object::New(isolate);
if (!symbols->SetPrototype(context, Null(isolate)).FromMaybe(false)) {
return;
}
for (const auto& entry : lib->symbols_) {
Local<String> symbol_key;
if (!String::NewFromUtf8(
isolate, entry.first.c_str(), NewStringType::kNormal)
.ToLocal(&symbol_key)) {
return;
}
if (!symbols
->Set(context,
symbol_key,
BigInt::NewFromUnsigned(
isolate,
static_cast<uint64_t>(
reinterpret_cast<uintptr_t>(entry.second))))
.FromMaybe(false)) {
return;
}
}
args.GetReturnValue().Set(symbols);
}
void DynamicLibrary::RegisterCallback(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
ffi_type* return_type = &ffi_type_void;
std::vector<ffi_type*> callback_args;
Local<Function> fn;
if (args.Length() < 1) {
env->ThrowTypeError(
"First argument must be a function or a signature object");
return;
}
if (args[0]->IsFunction()) {
fn = args[0].As<Function>();
} else {
if (!args[0]->IsObject() || args[0]->IsArray()) {
env->ThrowTypeError(
"First argument must be a function or a signature object");
return;
}
if (args.Length() < 2 || !args[1]->IsFunction()) {
env->ThrowTypeError("Second argument must be a function");
return;
}
if (!ParseFunctionSignature(env,
"<callback>",
args[0].As<Object>(),
&return_type,
&callback_args)) {
return;
}
fn = args[1].As<Function>();
}
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
if (lib->handle_ == nullptr) {
env->ThrowError("Library is closed");
return;
}
auto callback = new FFICallback{.owner = lib,
.env = env,
.thread_id = std::this_thread::get_id(),
.fn = Global<Function>(isolate, fn),
.closure = nullptr,
.ptr = nullptr,
.cif = {},
.args = std::move(callback_args),
.return_type = return_type};
callback->closure = static_cast<ffi_closure*>(
ffi_closure_alloc(sizeof(ffi_closure), &callback->ptr));
if (callback->closure == nullptr) {
env->ThrowError("ffi_closure_alloc failed");
delete callback;
return;
}
ffi_status status;
status = ffi_prep_cif(&callback->cif,
FFI_DEFAULT_ABI,
callback->args.size(),
callback->return_type,
callback->args.data());
if (status != FFI_OK) {
const char* msg = "ffi_prep_cif failed";
switch (status) {
case FFI_BAD_TYPEDEF:
msg = "ffi_prep_cif failed: bad typedef";
break;
case FFI_BAD_ABI:
msg = "ffi_prep_cif failed: bad ABI";
break;
default:
msg = "ffi_prep_cif failed: unknown error";
break;
}
env->ThrowError(msg);
delete callback;
return;
}
status = ffi_prep_closure_loc(callback->closure,
&callback->cif,
DynamicLibrary::InvokeCallback,
callback,
callback->ptr);
if (status != FFI_OK) {
const char* msg = "ffi_prep_closure_loc failed";
switch (status) {
case FFI_BAD_TYPEDEF:
msg = "ffi_prep_closure_loc failed: bad typedef";
break;
case FFI_BAD_ABI:
msg = "ffi_prep_closure_loc failed: bad ABI";
break;
default:
msg = "ffi_prep_closure_loc failed: unknown error";
break;
}
env->ThrowError(msg);
delete callback;
return;
}
lib->callbacks_.emplace(callback->ptr, callback);
args.GetReturnValue().Set(BigInt::NewFromUnsigned(
isolate,
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(callback->ptr))));
}
void DynamicLibrary::UnregisterCallback(
const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
if (lib->handle_ == nullptr) {
env->ThrowError("Library is closed");
return;
}
if (args.Length() < 1 || !args[0]->IsBigInt()) {
env->ThrowTypeError("The first argument must be a bigint");
return;
}
uintptr_t raw_ptr;
if (!GetValidatedPointerAddress(env, args[0], "first argument", &raw_ptr)) {
return;
}
void* ptr = reinterpret_cast<void*>(raw_ptr);
auto existing = lib->callbacks_.find(ptr);
if (existing == lib->callbacks_.end()) {
env->ThrowError("Callback not found");
return;
}
// This releases the callback trampoline immediately. If foreign code still
// retains and invokes the pointer afterwards, the behavior is undefined, not
// allowed, and dangerous: it can crash the process, produce incorrect
// output, or corrupt memory. Unregistering a callback while it is currently
// executing is also unsupported and dangerous.
lib->callbacks_.erase(existing);
}
void DynamicLibrary::RefCallback(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
if (lib->handle_ == nullptr) {
env->ThrowError("Library is closed");
return;
}
if (args.Length() < 1 || !args[0]->IsBigInt()) {
env->ThrowTypeError("The first argument must be a bigint");
return;
}
uintptr_t raw_ptr;
if (!GetValidatedPointerAddress(env, args[0], "first argument", &raw_ptr)) {
return;
}
void* ptr = reinterpret_cast<void*>(raw_ptr);
auto existing = lib->callbacks_.find(ptr);
if (existing == lib->callbacks_.end()) {
env->ThrowError("Callback not found");
return;
}
existing->second->fn.ClearWeak();
}
void DynamicLibrary::UnrefCallback(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
DynamicLibrary* lib = Unwrap<DynamicLibrary>(args.This());
if (lib->handle_ == nullptr) {
env->ThrowError("Library is closed");
return;
}
if (args.Length() < 1 || !args[0]->IsBigInt()) {
env->ThrowTypeError("The first argument must be a bigint");
return;
}
uintptr_t raw_ptr;
if (!GetValidatedPointerAddress(env, args[0], "first argument", &raw_ptr)) {
return;
}
void* ptr = reinterpret_cast<void*>(raw_ptr);
auto existing = lib->callbacks_.find(ptr);
if (existing == lib->callbacks_.end()) {
env->ThrowError("Callback not found");
return;
}
existing->second->fn.SetWeak();
}
Local<FunctionTemplate> DynamicLibrary::GetConstructorTemplate(
Environment* env) {
Local<FunctionTemplate> tmpl =
env->ffi_dynamic_library_constructor_template();
if (tmpl.IsEmpty()) {
Isolate* isolate = env->isolate();
enum PropertyAttribute attributes =
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
tmpl = NewFunctionTemplate(isolate, DynamicLibrary::New);
tmpl->InstanceTemplate()->SetInternalFieldCount(
DynamicLibrary::kInternalFieldCount);
tmpl->InstanceTemplate()->SetAccessorProperty(
FIXED_ONE_BYTE_STRING(isolate, "path"),
FunctionTemplate::New(env->isolate(), DynamicLibrary::GetPath),
Local<FunctionTemplate>(),
attributes);
tmpl->InstanceTemplate()->SetAccessorProperty(
FIXED_ONE_BYTE_STRING(isolate, "symbols"),
FunctionTemplate::New(env->isolate(), DynamicLibrary::GetSymbols),
Local<FunctionTemplate>(),
attributes);
tmpl->InstanceTemplate()->SetAccessorProperty(
FIXED_ONE_BYTE_STRING(isolate, "functions"),
FunctionTemplate::New(env->isolate(), DynamicLibrary::GetFunctions),
Local<FunctionTemplate>(),
attributes);
SetProtoMethod(isolate, tmpl, "close", DynamicLibrary::Close);
SetProtoMethod(isolate, tmpl, "getFunction", DynamicLibrary::GetFunction);
SetProtoMethod(isolate, tmpl, "getFunctions", DynamicLibrary::GetFunctions);
SetProtoMethod(isolate, tmpl, "getSymbol", DynamicLibrary::GetSymbol);
SetProtoMethod(isolate, tmpl, "getSymbols", DynamicLibrary::GetSymbols);
SetProtoMethod(
isolate, tmpl, "registerCallback", DynamicLibrary::RegisterCallback);
SetProtoMethod(isolate,
tmpl,
"unregisterCallback",
DynamicLibrary::UnregisterCallback);
SetProtoMethod(isolate, tmpl, "refCallback", DynamicLibrary::RefCallback);
SetProtoMethod(
isolate, tmpl, "unrefCallback", DynamicLibrary::UnrefCallback);
env->set_ffi_dynamic_library_constructor_template(tmpl);
}
return tmpl;
}
// Module initialization.
static void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
// Create the DynamicLibrary template
Local<FunctionTemplate> dl_tmpl = DynamicLibrary::GetConstructorTemplate(env);
SetConstructorFunction(context, target, "DynamicLibrary", dl_tmpl);
SetMethod(context, target, "toString", ToString);
SetMethod(context, target, "toBuffer", ToBuffer);
SetMethod(context, target, "toArrayBuffer", ToArrayBuffer);
SetMethod(context, target, "getInt8", GetInt8);
SetMethod(context, target, "getUint8", GetUint8);
SetMethod(context, target, "getInt16", GetInt16);
SetMethod(context, target, "getUint16", GetUint16);
SetMethod(context, target, "getInt32", GetInt32);
SetMethod(context, target, "getUint32", GetUint32);
SetMethod(context, target, "getInt64", GetInt64);
SetMethod(context, target, "getUint64", GetUint64);
SetMethod(context, target, "getFloat32", GetFloat32);
SetMethod(context, target, "getFloat64", GetFloat64);
SetMethod(context, target, "setInt8", SetInt8);
SetMethod(context, target, "setUint8", SetUint8);
SetMethod(context, target, "setInt16", SetInt16);
SetMethod(context, target, "setUint16", SetUint16);
SetMethod(context, target, "setInt32", SetInt32);
SetMethod(context, target, "setUint32", SetUint32);
SetMethod(context, target, "setInt64", SetInt64);
SetMethod(context, target, "setUint64", SetUint64);
SetMethod(context, target, "setFloat32", SetFloat32);
SetMethod(context, target, "setFloat64", SetFloat64);
}
} // namespace ffi
} // namespace node
NODE_BINDING_CONTEXT_AWARE_INTERNAL(ffi, node::ffi::Initialize)
#endif // HAVE_FFI