706 lines
22 KiB
C++
706 lines
22 KiB
C++
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unordered_map>
|
|
|
|
#include "ref-napi.h"
|
|
|
|
#ifdef _WIN32
|
|
#define __alignof__ __alignof
|
|
#define snprintf(buf, bufSize, format, arg) _snprintf_s(buf, bufSize, _TRUNCATE, format, arg)
|
|
#define strtoll _strtoi64
|
|
#define strtoull _strtoui64
|
|
#define PRId64 "lld"
|
|
#define PRIu64 "llu"
|
|
#else
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
#define __STDC_FORMAT_MACROS
|
|
#endif
|
|
#include <inttypes.h>
|
|
#endif
|
|
|
|
|
|
using namespace Napi;
|
|
|
|
namespace {
|
|
|
|
#if !defined(NAPI_VERSION) || NAPI_VERSION < 6
|
|
napi_status napix_set_instance_data(
|
|
napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint) {
|
|
typedef napi_status (*napi_set_instance_data_fn)(
|
|
napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint);
|
|
static const napi_set_instance_data_fn napi_set_instance_data__ =
|
|
(napi_set_instance_data_fn)
|
|
get_symbol_from_current_process("napi_set_instance_data");
|
|
|
|
if (napi_set_instance_data__ == nullptr)
|
|
return napi_generic_failure;
|
|
return napi_set_instance_data__(env, data, finalize_cb, finalize_hint);
|
|
}
|
|
|
|
napi_status napix_get_instance_data(
|
|
napi_env env, void** data) {
|
|
typedef napi_status (*napi_get_instance_data_fn)(
|
|
napi_env env, void** data);
|
|
static const napi_get_instance_data_fn napi_get_instance_data__ =
|
|
(napi_get_instance_data_fn)
|
|
get_symbol_from_current_process("napi_get_instance_data");
|
|
|
|
*data = nullptr;
|
|
if (napi_get_instance_data__ == nullptr)
|
|
return napi_generic_failure;
|
|
return napi_get_instance_data__(env, data);
|
|
}
|
|
#else // NAPI_VERSION >= 6
|
|
napi_status napix_set_instance_data(
|
|
napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint) {
|
|
return napi_set_instance_data(env, data, finalize_cb, finalize_hint);
|
|
}
|
|
|
|
napi_status napix_get_instance_data(
|
|
napi_env env, void** data) {
|
|
return napi_get_instance_data(env, data);
|
|
}
|
|
#endif
|
|
|
|
// used by the Int64 functions to determine whether to return a Number
|
|
// or String based on whether or not a Number will lose precision.
|
|
// http://stackoverflow.com/q/307179/376773
|
|
#define JS_MAX_INT +9007199254740992LL
|
|
#define JS_MIN_INT -9007199254740992LL
|
|
|
|
// mirrors deps/v8/src/objects.h.
|
|
// we could use `node::Buffer::kMaxLength`, but it's not defined on node v0.6.x
|
|
static const size_t kMaxLength = 0x3fffffff;
|
|
|
|
enum ArrayBufferMode {
|
|
AB_CREATED_BY_REF,
|
|
AB_PASSED_TO_REF
|
|
};
|
|
|
|
// Since Node.js v14.0.0, we have to keep a global list of all ArrayBuffer
|
|
// instances that we work with, in order not to create any duplicates.
|
|
// Luckily, N-API instance data is available on v14.x and above.
|
|
class InstanceData final : public RefNapi::Instance {
|
|
public:
|
|
InstanceData(Env env) : env(env) {}
|
|
|
|
struct ArrayBufferEntry {
|
|
Reference<ArrayBuffer> ab;
|
|
size_t finalizer_count;
|
|
};
|
|
|
|
Env env;
|
|
std::unordered_map<char*, ArrayBufferEntry> pointer_to_orig_buffer;
|
|
FunctionReference buffer_from;
|
|
|
|
void RegisterArrayBuffer(napi_value val) override {
|
|
ArrayBuffer buf(env, val);
|
|
RegisterArrayBuffer(buf, AB_PASSED_TO_REF);
|
|
}
|
|
|
|
inline void RegisterArrayBuffer(ArrayBuffer buf, ArrayBufferMode mode) {
|
|
char* ptr = static_cast<char*>(buf.Data());
|
|
if (ptr == nullptr) return;
|
|
|
|
auto it = pointer_to_orig_buffer.find(ptr);
|
|
if (it != pointer_to_orig_buffer.end()) {
|
|
if (!it->second.ab.Value().IsEmpty()) {
|
|
// Already have a valid entry, nothing to do.
|
|
return;
|
|
}
|
|
it->second.ab.Reset(buf, 0);
|
|
it->second.finalizer_count++;
|
|
} else {
|
|
pointer_to_orig_buffer.emplace(ptr, ArrayBufferEntry {
|
|
Reference<ArrayBuffer>::New(buf, 0),
|
|
1
|
|
});
|
|
}
|
|
|
|
// If AB_CREATED_BY_REF, then another finalizer has been added before this
|
|
// as a "real" backing store finalizer.
|
|
if (mode != AB_CREATED_BY_REF) {
|
|
buf.AddFinalizer([this](Env env, char* ptr) {
|
|
UnregisterArrayBuffer(ptr);
|
|
}, ptr);
|
|
}
|
|
}
|
|
|
|
inline void UnregisterArrayBuffer(char* ptr) {
|
|
auto it = pointer_to_orig_buffer.find(ptr);
|
|
if (--it->second.finalizer_count == 0)
|
|
pointer_to_orig_buffer.erase(it);
|
|
}
|
|
|
|
inline ArrayBuffer LookupOrCreateArrayBuffer(char* ptr, size_t length) {
|
|
assert(ptr != nullptr);
|
|
ArrayBuffer ab;
|
|
auto it = pointer_to_orig_buffer.find(ptr);
|
|
if (it != pointer_to_orig_buffer.end())
|
|
ab = it->second.ab.Value();
|
|
|
|
if (ab.IsEmpty()) {
|
|
length = std::max<size_t>(length, kMaxLength);
|
|
ab = Buffer<char>::New(env, ptr, length, [this](Env env, char* ptr) {
|
|
UnregisterArrayBuffer(ptr);
|
|
}).ArrayBuffer();
|
|
RegisterArrayBuffer(ab, AB_CREATED_BY_REF);
|
|
}
|
|
return ab;
|
|
}
|
|
|
|
napi_value WrapPointer(char* ptr, size_t length) override;
|
|
char* GetBufferData(napi_value val) override;
|
|
|
|
static InstanceData* Get(Env env) {
|
|
void* d = nullptr;
|
|
if (napix_get_instance_data(env, &d) == napi_ok)
|
|
return static_cast<InstanceData*>(d);
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Converts an arbitrary pointer to a node Buffer with specified length
|
|
*/
|
|
|
|
Value WrapPointer(Env env, char* ptr, size_t length) {
|
|
if (ptr == nullptr)
|
|
length = 0;
|
|
|
|
InstanceData* data;
|
|
if (ptr != nullptr && (data = InstanceData::Get(env)) != nullptr) {
|
|
ArrayBuffer ab = data->LookupOrCreateArrayBuffer(ptr, length);
|
|
assert(!ab.IsEmpty());
|
|
return data->buffer_from.Call({
|
|
ab, Number::New(env, 0), Number::New(env, length)
|
|
});
|
|
}
|
|
|
|
return Buffer<char>::New(env, ptr, length, [](Env,char*){});
|
|
}
|
|
|
|
char* GetBufferData(Value val) {
|
|
Buffer<char> buf = val.As<Buffer<char>>();
|
|
InstanceData* data = InstanceData::Get(val.Env());
|
|
if (data != nullptr)
|
|
data->RegisterArrayBuffer(buf.ArrayBuffer());
|
|
return buf.Data();
|
|
}
|
|
|
|
napi_value InstanceData::WrapPointer(char* ptr, size_t length) {
|
|
return ::WrapPointer(env, ptr, length);
|
|
}
|
|
|
|
char* InstanceData::GetBufferData(napi_value val) {
|
|
return ::GetBufferData(Value(env, val));
|
|
}
|
|
|
|
char* AddressForArgs(const CallbackInfo& args, size_t offset_index = 1) {
|
|
Value buf = args[0];
|
|
if (!buf.IsBuffer()) {
|
|
throw TypeError::New(args.Env(), "Buffer instance expected");
|
|
}
|
|
|
|
int64_t offset = args[offset_index].ToNumber();
|
|
return GetBufferData(buf) + offset;
|
|
}
|
|
|
|
/**
|
|
* Returns the pointer address as a Number of the given Buffer instance.
|
|
* It's recommended to use `hexAddress()` in most cases instead of this function.
|
|
*
|
|
* WARNING: a JavaScript Number cannot precisely store a full 64-bit memory
|
|
* address, so there's a possibility of an inaccurate value being returned
|
|
* on 64-bit systems.
|
|
*
|
|
* args[0] - Buffer - the Buffer instance get the memory address of
|
|
* args[1] - Number - optional (0) - the offset of the Buffer start at
|
|
*/
|
|
|
|
Value Address (const CallbackInfo& args) {
|
|
char* ptr = AddressForArgs(args);
|
|
uintptr_t intptr = reinterpret_cast<uintptr_t>(ptr);
|
|
|
|
return Number::New(args.Env(), static_cast<double>(intptr));
|
|
}
|
|
|
|
/**
|
|
* Returns the pointer address as a hexadecimal String. This function
|
|
* is safe to use for displaying memory addresses, as compared to the
|
|
* `address()` function which could overflow since it returns a Number.
|
|
*
|
|
* args[0] - Buffer - the Buffer instance get the memory address of
|
|
* args[1] - Number - optional (0) - the offset of the Buffer start at
|
|
*/
|
|
|
|
Value HexAddress(const CallbackInfo& args) {
|
|
char* ptr = AddressForArgs(args);
|
|
char strbuf[30]; /* should be plenty... */
|
|
snprintf(strbuf, 30, "%p", ptr);
|
|
|
|
if (strbuf[0] == '0' && strbuf[1] == 'x') {
|
|
/* strip the leading "0x" from the address */
|
|
ptr = strbuf + 2;
|
|
} else {
|
|
ptr = strbuf;
|
|
}
|
|
|
|
return String::New(args.Env(), ptr);
|
|
}
|
|
|
|
/**
|
|
* Returns "true" if the given Buffer points to nullptr, "false" otherwise.
|
|
*
|
|
* args[0] - Buffer - the Buffer instance to check for nullptr
|
|
* args[1] - Number - optional (0) - the offset of the Buffer start at
|
|
*/
|
|
|
|
Value IsNull(const CallbackInfo& args) {
|
|
char* ptr = AddressForArgs(args);
|
|
return Boolean::New(args.Env(), ptr == nullptr);
|
|
}
|
|
|
|
/**
|
|
* Retreives a JS Object instance that was previously stored in
|
|
* the given Buffer instance at the given offset.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read from
|
|
* args[1] - Number - the offset from the "buf" buffer's address to read from
|
|
*/
|
|
|
|
Value ReadObject(const CallbackInfo& args) {
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
if (ptr == nullptr) {
|
|
throw Error::New(args.Env(), "readObject: Cannot read from nullptr pointer");
|
|
}
|
|
|
|
Reference<Object>* rptr = reinterpret_cast<Reference<Object>*>(ptr);
|
|
return rptr->Value();
|
|
}
|
|
|
|
/**
|
|
* Writes a weak reference to given Object to the given Buffer
|
|
* instance and offset.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to write to
|
|
* args[1] - Number - the offset from the "buf" buffer's address to write to
|
|
* args[2] - Object - the "obj" Object which will have a new Persistent reference
|
|
* created for the obj, whose memory address will be written.
|
|
*/
|
|
|
|
void WriteObject(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
if (ptr == nullptr) {
|
|
throw Error::New(env, "readObject: Cannot write to nullptr pointer");
|
|
}
|
|
|
|
Reference<Object>* rptr = reinterpret_cast<Reference<Object>*>(ptr);
|
|
if (args[2].IsObject()) {
|
|
Object val = args[2].As<Object>();
|
|
*rptr = std::move(Reference<Object>::New(val));
|
|
} else if (args[2].IsNull()) {
|
|
rptr->Reset();
|
|
} else {
|
|
throw TypeError::New(env, "WriteObject's 3rd argument needs to be an object");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads the memory address of the given "buf" pointer Buffer at the specified
|
|
* offset, and returns a new SlowBuffer instance from the memory address stored.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read from
|
|
* args[1] - Number - the offset from the "buf" buffer's address to read from
|
|
* args[2] - Number - the length in bytes of the returned SlowBuffer instance
|
|
*/
|
|
|
|
Value ReadPointer(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
if (ptr == nullptr) {
|
|
throw Error::New(env, "readPointer: Cannot read from nullptr pointer");
|
|
}
|
|
|
|
int64_t size = args[2].ToNumber();
|
|
|
|
char* val = *reinterpret_cast<char**>(ptr);
|
|
return WrapPointer(env, val, size);
|
|
}
|
|
|
|
/**
|
|
* Writes the memory address of the "input" buffer (and optional offset) to the
|
|
* specified "buf" buffer and offset. Essentially making "buf" hold a reference
|
|
* to the "input" Buffer.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to write to
|
|
* args[1] - Number - the offset from the "buf" buffer's address to write to
|
|
* args[2] - Buffer - the "input" Buffer whose memory address will be written
|
|
*/
|
|
|
|
void WritePointer(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
Value input = args[2];
|
|
|
|
if (!input.IsNull() && !input.IsBuffer()) {
|
|
throw TypeError::New(env, "writePointer: Buffer instance expected as third argument");
|
|
}
|
|
|
|
if (input.IsNull()) {
|
|
*reinterpret_cast<char**>(ptr) = nullptr;
|
|
} else {
|
|
if ((args.Length() == 4) && (args[3].As<Boolean>() == true)) {
|
|
// create a node-api reference and finalizer to ensure that
|
|
// the buffer whoes pointer is written can only be
|
|
// collected after the finalizers for the buffer
|
|
// to which the pointer was written have already run
|
|
Reference<Value>* ref = new Reference<Value>;
|
|
*ref = Persistent(args[2]);
|
|
args[0].As<Object>().AddFinalizer([](Env env, Reference<Value>* ref) {
|
|
delete ref;
|
|
}, ref);
|
|
}
|
|
|
|
char* input_ptr = GetBufferData(input);
|
|
*reinterpret_cast<char**>(ptr) = input_ptr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads a machine-endian int64_t from the given Buffer at the given offset.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read from
|
|
* args[1] - Number - the offset from the "buf" buffer's address to read from
|
|
*/
|
|
|
|
Value ReadInt64(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
if (ptr == nullptr) {
|
|
throw TypeError::New(env, "readInt64: Cannot read from nullptr pointer");
|
|
}
|
|
|
|
int64_t val = *reinterpret_cast<int64_t*>(ptr);
|
|
|
|
if (val < JS_MIN_INT || val > JS_MAX_INT) {
|
|
char strbuf[128];
|
|
snprintf(strbuf, 128, "%" PRId64, val);
|
|
return String::New(env, strbuf);
|
|
} else {
|
|
return Number::New(env, val);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the input Number/String int64 value as a machine-endian int64_t to
|
|
* the given Buffer at the given offset.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to write to
|
|
* args[1] - Number - the offset from the "buf" buffer's address to write to
|
|
* args[2] - String/Number - the "input" String or Number which will be written
|
|
*/
|
|
|
|
void WriteInt64(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
Value in = args[2];
|
|
int64_t val;
|
|
if (in.IsNumber()) {
|
|
val = in.As<Number>();
|
|
} else if (in.IsString()) {
|
|
char* endptr;
|
|
char* str;
|
|
int base = 0;
|
|
std::string _str = in.As<String>();
|
|
str = &_str[0];
|
|
|
|
errno = 0; /* To distinguish success/failure after call */
|
|
val = strtoll(str, &endptr, base);
|
|
|
|
if (endptr == str) {
|
|
throw TypeError::New(env, "writeInt64: no digits we found in input String");
|
|
} else if (errno == ERANGE && (val == INT64_MAX || val == INT64_MIN)) {
|
|
throw TypeError::New(env, "writeInt64: input String numerical value out of range");
|
|
} else if (errno != 0 && val == 0) {
|
|
char errmsg[200];
|
|
snprintf(errmsg, sizeof(errmsg), "writeInt64: %s", strerror(errno));
|
|
throw TypeError::New(env, errmsg);
|
|
}
|
|
} else {
|
|
throw TypeError::New(env, "writeInt64: Number/String 64-bit value required");
|
|
}
|
|
|
|
*reinterpret_cast<int64_t*>(ptr) = val;
|
|
}
|
|
|
|
/**
|
|
* Reads a machine-endian uint64_t from the given Buffer at the given offset.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read from
|
|
* args[1] - Number - the offset from the "buf" buffer's address to read from
|
|
*/
|
|
|
|
Value ReadUInt64(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
if (ptr == nullptr) {
|
|
throw TypeError::New(env, "readUInt64: Cannot read from nullptr pointer");
|
|
}
|
|
|
|
uint64_t val = *reinterpret_cast<uint64_t*>(ptr);
|
|
|
|
if (val > JS_MAX_INT) {
|
|
char strbuf[128];
|
|
snprintf(strbuf, 128, "%" PRIu64, val);
|
|
return String::New(env, strbuf);
|
|
} else {
|
|
return Number::New(env, val);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the input Number/String uint64 value as a machine-endian uint64_t to
|
|
* the given Buffer at the given offset.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to write to
|
|
* args[1] - Number - the offset from the "buf" buffer's address to write to
|
|
* args[2] - String/Number - the "input" String or Number which will be written
|
|
*/
|
|
|
|
void WriteUInt64(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
Value in = args[2];
|
|
uint64_t val;
|
|
if (in.IsNumber()) {
|
|
val = static_cast<int64_t>(in.As<Number>());
|
|
} else if (in.IsString()) {
|
|
char* endptr;
|
|
char* str;
|
|
int base = 0;
|
|
std::string _str = in.As<String>();
|
|
str = &_str[0];
|
|
|
|
errno = 0; /* To distinguish success/failure after call */
|
|
val = strtoull(str, &endptr, base);
|
|
|
|
if (endptr == str) {
|
|
throw TypeError::New(env, "writeUInt64: no digits we found in input String");
|
|
} else if (errno == ERANGE && (val == UINT64_MAX)) {
|
|
throw TypeError::New(env, "writeUInt64: input String numerical value out of range");
|
|
} else if (errno != 0 && val == 0) {
|
|
char errmsg[200];
|
|
snprintf(errmsg, sizeof(errmsg), "writeUInt64: %s", strerror(errno));
|
|
throw TypeError::New(env, errmsg);
|
|
}
|
|
} else {
|
|
throw TypeError::New(env, "writeUInt64: Number/String 64-bit value required");
|
|
}
|
|
|
|
*reinterpret_cast<uint64_t*>(ptr) = val;
|
|
}
|
|
|
|
/**
|
|
* Reads a Utf8 C String from the given pointer at the given offset (or 0).
|
|
* I didn't want to add this function but it ends up being necessary for reading
|
|
* past a 0 or 1 length Buffer's boundary in node-ffi :\
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read from
|
|
* args[1] - Number - the offset from the "buf" buffer's address to read from
|
|
*/
|
|
|
|
Value ReadCString(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args);
|
|
|
|
if (ptr == nullptr) {
|
|
throw Error::New(env, "readCString: Cannot read from nullptr pointer");
|
|
}
|
|
|
|
return String::New(env, ptr);
|
|
}
|
|
|
|
/**
|
|
* Returns a new Buffer instance that has the same memory address
|
|
* as the given buffer, but with the specified size.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read the address from
|
|
* args[1] - Number - the size in bytes that the returned Buffer should be
|
|
* args[2] - Number - the offset from the "buf" buffer's address to read from
|
|
*/
|
|
|
|
Value ReinterpretBuffer(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args, 2);
|
|
|
|
if (ptr == nullptr) {
|
|
throw Error::New(env, "reinterpret: Cannot reinterpret from nullptr pointer");
|
|
}
|
|
|
|
int64_t size = args[1].ToNumber();
|
|
|
|
return WrapPointer(env, ptr, size);
|
|
}
|
|
|
|
/**
|
|
* Returns a new Buffer instance that has the same memory address
|
|
* as the given buffer, but with a length up to the first aligned set of values of
|
|
* 0 in a row for the given length.
|
|
*
|
|
* args[0] - Buffer - the "buf" Buffer instance to read the address from
|
|
* args[1] - Number - the number of sequential 0-byte values that need to be read
|
|
* args[2] - Number - the offset from the "buf" buffer's address to read from
|
|
*/
|
|
|
|
Value ReinterpretBufferUntilZeros(const CallbackInfo& args) {
|
|
Env env = args.Env();
|
|
char* ptr = AddressForArgs(args, 2);
|
|
|
|
if (ptr == nullptr) {
|
|
throw Error::New(env, "reinterpretUntilZeros: Cannot reinterpret from nullptr pointer");
|
|
}
|
|
|
|
uint32_t numZeros = args[1].ToNumber();
|
|
uint32_t i = 0;
|
|
size_t size = 0;
|
|
bool end = false;
|
|
|
|
while (!end && size < kMaxLength) {
|
|
end = true;
|
|
for (i = 0; i < numZeros; i++) {
|
|
if (ptr[size + i] != 0) {
|
|
end = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!end) {
|
|
size += numZeros;
|
|
}
|
|
}
|
|
|
|
return WrapPointer(env, ptr, size);
|
|
}
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
Object Init(Env env, Object exports) {
|
|
InstanceData* data = new InstanceData(env);
|
|
{
|
|
Value buffer_ctor = env.Global()["Buffer"];
|
|
Value buffer_from = buffer_ctor.As<Object>()["from"];
|
|
data->buffer_from.Reset(buffer_from.As<Function>(), 1);
|
|
assert(!data->buffer_from.IsEmpty());
|
|
napi_status status = napix_set_instance_data(
|
|
env, data, [](napi_env env, void* data, void* hint) {
|
|
delete static_cast<InstanceData*>(data);
|
|
}, nullptr);
|
|
if (status != napi_ok) {
|
|
delete data;
|
|
data = nullptr;
|
|
} else {
|
|
// Hack around the fact that we can't reset buffer_from from the
|
|
// InstanceData dtor.
|
|
buffer_from.As<Object>().AddFinalizer([](Env env, InstanceData* data) {
|
|
data->buffer_from.Reset();
|
|
}, data);
|
|
}
|
|
}
|
|
exports["instance"] = External<RefNapi::Instance>::New(env, data);
|
|
|
|
// "sizeof" map
|
|
Object smap = Object::New(env);
|
|
// fixed sizes
|
|
#define SET_SIZEOF(name, type) \
|
|
smap[ #name ] = Number::New(env, sizeof(type));
|
|
SET_SIZEOF(int8, int8_t);
|
|
SET_SIZEOF(uint8, uint8_t);
|
|
SET_SIZEOF(int16, int16_t);
|
|
SET_SIZEOF(uint16, uint16_t);
|
|
SET_SIZEOF(int32, int32_t);
|
|
SET_SIZEOF(uint32, uint32_t);
|
|
SET_SIZEOF(int64, int64_t);
|
|
SET_SIZEOF(uint64, uint64_t);
|
|
SET_SIZEOF(float, float);
|
|
SET_SIZEOF(double, double);
|
|
// (potentially) variable sizes
|
|
SET_SIZEOF(bool, bool);
|
|
SET_SIZEOF(byte, unsigned char);
|
|
SET_SIZEOF(char, char);
|
|
SET_SIZEOF(uchar, unsigned char);
|
|
SET_SIZEOF(short, short);
|
|
SET_SIZEOF(ushort, unsigned short);
|
|
SET_SIZEOF(int, int);
|
|
SET_SIZEOF(uint, unsigned int);
|
|
SET_SIZEOF(long, long);
|
|
SET_SIZEOF(ulong, unsigned long);
|
|
SET_SIZEOF(longlong, long long);
|
|
SET_SIZEOF(ulonglong, unsigned long long);
|
|
SET_SIZEOF(pointer, char *);
|
|
SET_SIZEOF(size_t, size_t);
|
|
// size of a weak handle to a JS object
|
|
SET_SIZEOF(Object, Reference<Object>);
|
|
|
|
// "alignof" map
|
|
Object amap = Object::New(env);
|
|
#define SET_ALIGNOF(name, type) \
|
|
struct s_##name { type a; }; \
|
|
amap[ #name ] = Number::New(env, alignof(struct s_##name));
|
|
SET_ALIGNOF(int8, int8_t);
|
|
SET_ALIGNOF(uint8, uint8_t);
|
|
SET_ALIGNOF(int16, int16_t);
|
|
SET_ALIGNOF(uint16, uint16_t);
|
|
SET_ALIGNOF(int32, int32_t);
|
|
SET_ALIGNOF(uint32, uint32_t);
|
|
SET_ALIGNOF(int64, int64_t);
|
|
SET_ALIGNOF(uint64, uint64_t);
|
|
SET_ALIGNOF(float, float);
|
|
SET_ALIGNOF(double, double);
|
|
SET_ALIGNOF(bool, bool);
|
|
SET_ALIGNOF(char, char);
|
|
SET_ALIGNOF(uchar, unsigned char);
|
|
SET_ALIGNOF(short, short);
|
|
SET_ALIGNOF(ushort, unsigned short);
|
|
SET_ALIGNOF(int, int);
|
|
SET_ALIGNOF(uint, unsigned int);
|
|
SET_ALIGNOF(long, long);
|
|
SET_ALIGNOF(ulong, unsigned long);
|
|
SET_ALIGNOF(longlong, long long);
|
|
SET_ALIGNOF(ulonglong, unsigned long long);
|
|
SET_ALIGNOF(pointer, char *);
|
|
SET_ALIGNOF(size_t, size_t);
|
|
SET_ALIGNOF(Object, Reference<Object>);
|
|
|
|
// exports
|
|
exports["sizeof"] = smap;
|
|
exports["alignof"] = amap;
|
|
exports["nullptr"] = exports["NULL"] = WrapPointer(env, nullptr, 0);
|
|
exports["address"] = Function::New(env, Address);
|
|
exports["hexAddress"] = Function::New(env, HexAddress);
|
|
exports["isNull"] = Function::New(env, IsNull);
|
|
exports["readObject"] = Function::New(env, ReadObject);
|
|
exports["_writeObject"] = Function::New(env, WriteObject);
|
|
exports["readPointer"] = Function::New(env, ReadPointer);
|
|
exports["_writePointer"] = Function::New(env, WritePointer);
|
|
exports["readInt64"] = Function::New(env, ReadInt64);
|
|
exports["writeInt64"] = Function::New(env, WriteInt64);
|
|
exports["readUInt64"] = Function::New(env, ReadUInt64);
|
|
exports["writeUInt64"] = Function::New(env, WriteUInt64);
|
|
exports["readCString"] = Function::New(env, ReadCString);
|
|
exports["_reinterpret"] = Function::New(env, ReinterpretBuffer);
|
|
exports["_reinterpretUntilZeros"] = Function::New(env, ReinterpretBufferUntilZeros);
|
|
return exports;
|
|
}
|
|
|
|
NODE_API_MODULE(binding, Init)
|