#ifndef SRC_NAPI_INL_H_
#define SRC_NAPI_INL_H_
////////////////////////////////////////////////////////////////////////////////
// Node-API C++ Wrapper Classes
//
// Inline header-only implementations for "Node-API" ABI-stable C APIs for
// Node.js.
////////////////////////////////////////////////////////////////////////////////
// Note: Do not include this file directly! Include "napi.h" instead.
#include <algorithm>
#include <cstring>
#include <mutex>
#include <type_traits>
#include <utility>
namespace Napi {
#ifdef NAPI_CPP_CUSTOM_NAMESPACE
namespace NAPI_CPP_CUSTOM_NAMESPACE {
#endif
// Helpers to handle functions exposed from C++.
namespace details {
// Attach a data item to an object and delete it when the object gets
// garbage-collected.
// TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
// available on all supported versions of Node.js.
template <typename FreeType>
inline napi_status AttachData(napi_env env,
napi_value obj,
FreeType* data,
napi_finalize finalizer = nullptr,
void* hint = nullptr) {
napi_status status;
if (finalizer == nullptr) {
finalizer = [](napi_env /*env*/, void* data, void* /*hint*/) {
delete static_cast<FreeType*>(data);
};
}
#if (NAPI_VERSION < 5)
napi_value symbol, external;
status = napi_create_symbol(env, nullptr, &symbol);
if (status == napi_ok) {
status = napi_create_external(env, data, finalizer, hint, &external);
if (status == napi_ok) {
napi_property_descriptor desc = {nullptr,
symbol,
nullptr,
nullptr,
nullptr,
external,
napi_default,
nullptr};
status = napi_define_properties(env, obj, 1, &desc);
}
}
#else // NAPI_VERSION >= 5
status = napi_add_finalizer(env, obj, data, finalizer, hint, nullptr);
#endif
return status;
}
// For use in JS to C++ callback wrappers to catch any Napi::Error exceptions
// and rethrow them as JavaScript exceptions before returning from the callback.
template <typename Callable>
inline napi_value WrapCallback(Callable callback) {
#ifdef NAPI_CPP_EXCEPTIONS
try {
return callback();
} catch (const Error& e) {
e.ThrowAsJavaScriptException();
return nullptr;
}
#else // NAPI_CPP_EXCEPTIONS
// When C++ exceptions are disabled, errors are immediately thrown as JS
// exceptions, so there is no need to catch and rethrow them here.
return callback();
#endif // NAPI_CPP_EXCEPTIONS
}
// For use in JS to C++ void callback wrappers to catch any Napi::Error
// exceptions and rethrow them as JavaScript exceptions before returning from
// the callback.
template <typename Callable>
inline void WrapVoidCallback(Callable callback) {
#ifdef NAPI_CPP_EXCEPTIONS
try {
callback();
} catch (const Error& e) {
e.ThrowAsJavaScriptException();
}
#else // NAPI_CPP_EXCEPTIONS
// When C++ exceptions are disabled, errors are immediately thrown as JS
// exceptions, so there is no need to catch and rethrow them here.
callback();
#endif // NAPI_CPP_EXCEPTIONS
}
template <typename Callable, typename Return>
struct CallbackData {
static inline napi_value Wrapper(napi_env env, napi_callback_info info) {
return details::WrapCallback([&] {
CallbackInfo callbackInfo(env, info);
CallbackData* callbackData =
static_cast<CallbackData*>(callbackInfo.Data());
callbackInfo.SetData(callbackData->data);
return callbackData->callback(callbackInfo);
});
}
Callable callback;
void* data;
};
template <typename Callable>
struct CallbackData<Callable, void> {
static inline napi_value Wrapper(napi_env env, napi_callback_info info) {
return details::WrapCallback([&] {
CallbackInfo callbackInfo(env, info);
CallbackData* callbackData =
static_cast<CallbackData*>(callbackInfo.Data());
callbackInfo.SetData(callbackData->data);
callbackData->callback(callbackInfo);
return nullptr;
});
}
Callable callback;
void* data;
};
template <void (*Callback)(const CallbackInfo& info)>
napi_value TemplatedVoidCallback(napi_env env,
napi_callback_info info) NAPI_NOEXCEPT {
return details::WrapCallback([&] {
CallbackInfo cbInfo(env, info);
Callback(cbInfo);
return nullptr;
});
}
template <Napi::Value (*Callback)(const CallbackInfo& info)>
napi_value TemplatedCallback(napi_env env,
napi_callback_info info) NAPI_NOEXCEPT {
return details::WrapCallback([&] {
CallbackInfo cbInfo(env, info);
return Callback(cbInfo);
});
}
template <typename T,
Napi::Value (T::*UnwrapCallback)(const CallbackInfo& info)>
napi_value TemplatedInstanceCallback(napi_env env,
napi_callback_info info) NAPI_NOEXCEPT {
return details::WrapCallback([&] {
CallbackInfo cbInfo(env, info);
T* instance = T::Unwrap(cbInfo.This().As<Object>());
return (instance->*UnwrapCallback)(cbInfo);
});
}
template <typename T, void (T::*UnwrapCallback)(const CallbackInfo& info)>
napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info)
NAPI_NOEXCEPT {
return details::WrapCallback([&] {
CallbackInfo cbInfo(env, info);
T* instance = T::Unwrap(cbInfo.This().As<Object>());
(instance->*UnwrapCallback)(cbInfo);
return nullptr;
});
}
template <typename T, typename Finalizer, typename Hint = void>
struct FinalizeData {
static inline void Wrapper(napi_env env,
void* data,
void* finalizeHint) NAPI_NOEXCEPT {
WrapVoidCallback([&] {
FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
finalizeData->callback(Env(env), static_cast<T*>(data));
delete finalizeData;
});
}
static inline void WrapperWithHint(napi_env env,
void* data,
void* finalizeHint) NAPI_NOEXCEPT {
WrapVoidCallback([&] {
FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
finalizeData->callback(
Env(env), static_cast<T*>(data), finalizeData->hint);
delete finalizeData;
});
}
Finalizer callback;
Hint* hint;
};
#if (NAPI_VERSION > 3 && !defined(__wasm32__))
template <typename ContextType = void,
typename Finalizer = std::function<void(Env, void*, ContextType*)>,
typename FinalizerDataType = void>
struct ThreadSafeFinalize {
static inline void Wrapper(napi_env env,
void* rawFinalizeData,
void* /* rawContext */) {
if (rawFinalizeData == nullptr) return;
ThreadSafeFinalize* finalizeData =
static_cast<ThreadSafeFinalize*>(rawFinalizeData);
finalizeData->callback(Env(env));
delete finalizeData;
}
static inline void FinalizeWrapperWithData(napi_env env,
void* rawFinalizeData,
void* /* rawContext */) {
if (rawFinalizeData == nullptr) return;
ThreadSafeFinalize* finalizeData =
static_cast<ThreadSafeFinalize*>(rawFinalizeData);
finalizeData->callback(Env(env), finalizeData->data);
delete finalizeData;
}
static inline void FinalizeWrapperWithContext(napi_env env,
void* rawFinalizeData,
void* rawContext) {
if (rawFinalizeData == nullptr) return;
ThreadSafeFinalize* finalizeData =
static_cast<ThreadSafeFinalize*>(rawFinalizeData);
finalizeData->callback(Env(env