#include
#include
#include // INT_MAX
#include
#include
#include
#include
#define NAPI_EXPERIMENTAL
#include "node_api.h"
#include "node_internals.h"
#include "env.h"
static
napi_status napi_set_last_error(napi_env env, napi_status error_code,
uint32_t engine_error_code = 0,
void* engine_reserved = nullptr);
static
napi_status napi_clear_last_error(napi_env env);
struct napi_env__ {
explicit napi_env__(v8::Isolate* _isolate, uv_loop_t* _loop)
: isolate(_isolate),
last_error(),
loop(_loop) {}
v8::Isolate* isolate;
node::Persistent last_exception;
napi_extended_error_info last_error;
int open_handle_scopes = 0;
int open_callback_scopes = 0;
uv_loop_t* loop = nullptr;
};
#define NAPI_PRIVATE_KEY(context, suffix) \
(node::Environment::GetCurrent((context))->napi_ ## suffix())
#define RETURN_STATUS_IF_FALSE(env, condition, status) \
do { \
if (!(condition)) { \
return napi_set_last_error((env), (status)); \
} \
} while (0)
#define CHECK_ENV(env) \
do { \
if ((env) == nullptr) { \
return napi_invalid_arg; \
} \
} while (0)
#define CHECK_ARG(env, arg) \
RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg)
#define CHECK_MAYBE_EMPTY(env, maybe, status) \
RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status))
#define CHECK_MAYBE_NOTHING(env, maybe, status) \
RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status))
// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope
#define NAPI_PREAMBLE(env) \
CHECK_ENV((env)); \
RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \
napi_pending_exception); \
napi_clear_last_error((env)); \
v8impl::TryCatch try_catch((env))
#define CHECK_TO_TYPE(env, type, context, result, src, status) \
do { \
CHECK_ARG((env), (src)); \
auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
CHECK_MAYBE_EMPTY((env), maybe, (status)); \
(result) = maybe.ToLocalChecked(); \
} while (0)
#define CHECK_TO_FUNCTION(env, result, src) \
do { \
CHECK_ARG((env), (src)); \
v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \
RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \
(result) = v8value.As(); \
} while (0)
#define CHECK_TO_OBJECT(env, context, result, src) \
CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected)
#define CHECK_TO_STRING(env, context, result, src) \
CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected)
#define CHECK_TO_NUMBER(env, context, result, src) \
CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected)
#define CHECK_TO_BOOL(env, context, result, src) \
CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \
napi_boolean_expected)
// n-api defines NAPI_AUTO_LENGHTH as the indicator that a string
// is null terminated. For V8 the equivalent is -1. The assert
// validates that our cast of NAPI_AUTO_LENGTH results in -1 as
// needed by V8.
#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \
do { \
static_assert(static_cast(NAPI_AUTO_LENGTH) == -1, \
"Casting NAPI_AUTO_LENGTH to int must result in -1"); \
RETURN_STATUS_IF_FALSE((env), \
(len == NAPI_AUTO_LENGTH) || len <= INT_MAX, \
napi_invalid_arg); \
auto str_maybe = v8::String::NewFromUtf8( \
(env)->isolate, (str), v8::NewStringType::kInternalized, \
static_cast(len)); \
CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \
(result) = str_maybe.ToLocalChecked(); \
} while (0)
#define CHECK_NEW_FROM_UTF8(env, result, str) \
CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH)
#define GET_RETURN_STATUS(env) \
(!try_catch.HasCaught() ? napi_ok \
: napi_set_last_error((env), napi_pending_exception))
#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \
do { \
if (!(condition)) { \
napi_throw_range_error((env), (error), (message)); \
return napi_set_last_error((env), napi_generic_failure); \
} \
} while (0)
#define CREATE_TYPED_ARRAY( \
env, type, size_of_element, buffer, byte_offset, length, out) \
do { \
if ((size_of_element) > 1) { \
THROW_RANGE_ERROR_IF_FALSE( \
(env), (byte_offset) % (size_of_element) == 0, \
"ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \
"start offset of "#type" should be a multiple of "#size_of_element); \
} \
THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + \
(byte_offset) <= buffer->ByteLength(), \
"ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \
"Invalid typed array length"); \
(out) = v8::type::New((buffer), (byte_offset), (length)); \
} while (0)
#define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \
do { \
int open_handle_scopes = (env)->open_handle_scopes; \
int open_callback_scopes = (env)->open_callback_scopes; \
napi_clear_last_error((env)); \
call; \
CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \
CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \
if (!(env)->last_exception.IsEmpty()) { \
handle_exception( \
v8::Local::New((env)->isolate, (env)->last_exception)); \
(env)->last_exception.Reset(); \
} \
} while (0)
#define NAPI_CALL_INTO_MODULE_THROW(env, call) \
NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException)
namespace {
namespace v8impl {
// convert from n-api property attributes to v8::PropertyAttribute
static inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor(
const napi_property_descriptor* descriptor) {
unsigned int attribute_flags = v8::PropertyAttribute::None;
if (descriptor->getter != nullptr || descriptor->setter != nullptr) {
// The napi_writable attribute is ignored for accessor descriptors, but
// V8 requires the ReadOnly attribute to match nonexistence of a setter.
attribute_flags |= (descriptor->setter == nullptr ?
v8::PropertyAttribute::ReadOnly : v8::PropertyAttribute::None);
} else if ((descriptor->attributes & napi_writable) == 0) {
attribute_flags |= v8::PropertyAttribute::ReadOnly;
}
if ((descriptor->attributes & napi_enumerable) == 0) {
attribute_flags |= v8::PropertyAttribute::DontEnum;
}
if ((descriptor->attributes & napi_configurable) == 0) {
attribute_flags |= v8::PropertyAttribute::DontDelete;
}
return static_cast(attribute_flags);
}
class HandleScopeWrapper {
public:
explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {}
private:
v8::HandleScope scope;
};
// In node v0.10 version of v8, there is no EscapableHandleScope and the
// node v0.10 port use HandleScope::Close(Local v) to mimic the behavior
// of a EscapableHandleScope::Escape(Local v), but it is not the same
// semantics. This is an example of where the api abstraction fail to work
// across different versions.
class EscapableHandleScopeWrapper {
public:
explicit EscapableHandleScopeWrapper(v8::Isolate* isolate)
: scope(isolate), escape_called_(false) {}
bool escape_called() const {
return escape_called_;
}
template
v8::Local Escape(v8::Local handle) {
escape_called_ = true;
return scope.Escape(handle);
}
private:
v8::EscapableHandleScope scope;
bool escape_called_;
};
static
napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) {
return reinterpret_cast(s);
}
static
HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) {
return reinterpret_cast(s);
}
static
napi_escapable_handle_scope JsEscapableHandleScopeFromV8EscapableHandleScope(
EscapableHandleScopeWrapper* s) {
return reinterpret_cast(s);
}
static
EscapableHandleScopeWrapper*
V8EscapableHandleScopeFromJsEscapableHandleScope(
napi_escapable_handle_scope s) {
return reinterpret_cast(s);
}
static
napi_callback_scope JsCallbackScopeFromV8CallbackScope(
node::CallbackScope* s) {
return reinterpret_cast(s);
}
static
node::CallbackScope* V8CallbackScopeFromJsCallbackScope(
napi_callback_scope s) {
return reinterpret_cast(s);
}
//=== Conversion between V8 Handles and napi_value ========================
// This asserts v8::Local<> will always be implemented with a single
// pointer field so that we can pass it around as a void*.
static_assert(sizeof(v8::Local) == sizeof(napi_value),
"Cannot convert between v8::Local and napi_value");
static
napi_deferred JsDeferredFromNodePersistent(node::Persistent* local) {
return reinterpret_cast(local);
}
static
node::Persistent* NodePersistentFromJsDeferred(napi_deferred local) {
return reinterpret_cast*>(local);
}
static
napi_value JsValueFromV8LocalValue(v8::Local local) {
return reinterpret_cast(*local);
}
static
v8::Local V8LocalValueFromJsValue(napi_value v) {
v8::Local local;
memcpy(&local, &v, sizeof(v));
return local;
}
static inline void trigger_fatal_exception(
napi_env env, v8::Local local_err) {
v8::Local local_msg =
v8::Exception::CreateMessage(env->isolate, local_err);
node::FatalException(env->isolate, local_err, local_msg);
}
static inline napi_status V8NameFromPropertyDescriptor(napi_env env,
const napi_property_descriptor* p,
v8::Local* result) {
if (p->utf8name != nullptr) {
CHECK_NEW_FROM_UTF8(env, *result, p->utf8name);
} else {
v8::Local property_value =
v8impl::V8LocalValueFromJsValue(p->name);
RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected);
*result = property_value.As();
}
return napi_ok;
}
// Adapter for napi_finalize callbacks.
class Finalizer {
protected:
Finalizer(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint)
: _env(env),
_finalize_callback(finalize_callback),
_finalize_data(finalize_data),
_finalize_hint(finalize_hint) {
}
~Finalizer() {
}
public:
static Finalizer* New(napi_env env,
napi_finalize finalize_callback = nullptr,
void* finalize_data = nullptr,
void* finalize_hint = nullptr) {
return new Finalizer(
env, finalize_callback, finalize_data, finalize_hint);
}
static void Delete(Finalizer* finalizer) {
delete finalizer;
}
// node::Buffer::FreeCallback
static void FinalizeBufferCallback(char* data, void* hint) {
Finalizer* finalizer = static_cast(hint);
if (finalizer->_finalize_callback != nullptr) {
NAPI_CALL_INTO_MODULE_THROW(finalizer->_env,
finalizer->_finalize_callback(
finalizer->_env,
data,
finalizer->_finalize_hint));
}
Delete(finalizer);
}
protected:
napi_env _env;
napi_finalize _finalize_callback;
void* _finalize_data;
void* _finalize_hint;
};
// Wrapper around node::Persistent that implements reference counting.
class Reference : private Finalizer {
private:
Reference(napi_env env,
v8::Local value,
uint32_t initial_refcount,
bool delete_self,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint)
: Finalizer(env, finalize_callback, finalize_data, finalize_hint),
_persistent(env->isolate, value),
_refcount(initial_refcount),
_delete_self(delete_self) {
if (initial_refcount == 0) {
_persistent.SetWeak(
this, FinalizeCallback, v8::WeakCallbackType::kParameter);
}
}
public:
void* Data() {
return _finalize_data;
}
static Reference* New(napi_env env,
v8::Local value,
uint32_t initial_refcount,
bool delete_self,
napi_finalize finalize_callback = nullptr,
void* finalize_data = nullptr,
void* finalize_hint = nullptr) {
return new Reference(env,
value,
initial_refcount,
delete_self,
finalize_callback,
finalize_data,
finalize_hint);
}
static void Delete(Reference* reference) {
delete reference;
}
uint32_t Ref() {
if (++_refcount == 1) {
_persistent.ClearWeak();
}
return _refcount;
}
uint32_t Unref() {
if (_refcount == 0) {
return 0;
}
if (--_refcount == 0) {
_persistent.SetWeak(
this, FinalizeCallback, v8::WeakCallbackType::kParameter);
}
return _refcount;
}
uint32_t RefCount() {
return _refcount;
}
v8::Local Get() {
if (_persistent.IsEmpty()) {
return v8::Local();
} else {
return v8::Local::New(_env->isolate, _persistent);
}
}
private:
static void FinalizeCallback(const v8::WeakCallbackInfo& data) {
Reference* reference = data.GetParameter();
reference->_persistent.Reset();
// Check before calling the finalize callback, because the callback might
// delete it.
bool delete_self = reference->_delete_self;
napi_env env = reference->_env;
if (reference->_finalize_callback != nullptr) {
NAPI_CALL_INTO_MODULE_THROW(env,
reference->_finalize_callback(
reference->_env,
reference->_finalize_data,
reference->_finalize_hint));
}
if (delete_self) {
Delete(reference);
}
}
node::Persistent _persistent;
uint32_t _refcount;
bool _delete_self;
};
class TryCatch : public v8::TryCatch {
public:
explicit TryCatch(napi_env env)
: v8::TryCatch(env->isolate), _env(env) {}
~TryCatch() {
if (HasCaught()) {
_env->last_exception.Reset(_env->isolate, Exception());
}
}
private:
napi_env _env;
};
//=== Function napi_callback wrapper =================================
// Use this data structure to associate callback data with each N-API function
// exposed to JavaScript. The structure is stored in a v8::External which gets
// passed into our callback wrapper. This reduces the performance impact of
// calling through N-API.
// Ref: benchmark/misc/function_call
// Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072
struct CallbackBundle {
// Bind the lifecycle of `this` C++ object to a JavaScript object.
// We never delete a CallbackBundle C++ object directly.
void BindLifecycleTo(v8::Isolate* isolate, v8::Local target) {
handle.Reset(isolate, target);
handle.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
}
napi_env env; // Necessary to invoke C++ NAPI callback
void* cb_data; // The user provided callback data
napi_callback function_or_getter;
napi_callback setter;
node::Persistent handle; // Die with this JavaScript object
private:
static void WeakCallback(v8::WeakCallbackInfo const& info) {
// Use the "WeakCallback mechanism" to delete the C++ `bundle` object.
// This will be called when the v8::External containing `this` pointer
// is being GC-ed.
CallbackBundle* bundle = info.GetParameter();
if (bundle != nullptr) {
delete bundle;
}
}
};
// Base class extended by classes that wrap V8 function and property callback
// info.
class CallbackWrapper {
public:
CallbackWrapper(napi_value this_arg, size_t args_length, void* data)
: _this(this_arg), _args_length(args_length), _data(data) {}
virtual napi_value GetNewTarget() = 0;
virtual void Args(napi_value* buffer, size_t bufferlength) = 0;
virtual void SetReturnValue(napi_value value) = 0;
napi_value This() { return _this; }
size_t ArgsLength() { return _args_length; }
void* Data() { return _data; }
protected:
const napi_value _this;
const size_t _args_length;
void* _data;
};
template
class CallbackWrapperBase : public CallbackWrapper {
public:
CallbackWrapperBase(const Info& cbinfo, const size_t args_length)
: CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()),
args_length,
nullptr),
_cbinfo(cbinfo) {
_bundle = reinterpret_cast(
v8::Local::Cast(cbinfo.Data())->Value());
_data = _bundle->cb_data;
}
napi_value GetNewTarget() override { return nullptr; }
protected:
void InvokeCallback() {
napi_callback_info cbinfo_wrapper = reinterpret_cast(
static_cast(this));
// All other pointers we need are stored in `_bundle`
napi_env env = _bundle->env;
napi_callback cb = _bundle->*FunctionField;
napi_value result;
NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper));
if (result != nullptr) {
this->SetReturnValue(result);
}
}
const Info& _cbinfo;
CallbackBundle* _bundle;
};
class FunctionCallbackWrapper
: public CallbackWrapperBase,
&CallbackBundle::function_or_getter> {
public:
static void Invoke(const v8::FunctionCallbackInfo& info) {
FunctionCallbackWrapper cbwrapper(info);
cbwrapper.InvokeCallback();
}
explicit FunctionCallbackWrapper(
const v8::FunctionCallbackInfo& cbinfo)
: CallbackWrapperBase(cbinfo, cbinfo.Length()) {}
napi_value GetNewTarget() override {
if (_cbinfo.IsConstructCall()) {
return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget());
} else {
return nullptr;
}
}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
size_t i = 0;
size_t min = std::min(buffer_length, _args_length);
for (; i < min; i += 1) {
buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]);
}
if (i < buffer_length) {
napi_value undefined =
v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
for (; i < buffer_length; i += 1) {
buffer[i] = undefined;
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
_cbinfo.GetReturnValue().Set(val);
}
};
class GetterCallbackWrapper
: public CallbackWrapperBase,
&CallbackBundle::function_or_getter> {
public:
static void Invoke(v8::Local property,
const v8::PropertyCallbackInfo& info) {
GetterCallbackWrapper cbwrapper(info);
cbwrapper.InvokeCallback();
}
explicit GetterCallbackWrapper(
const v8::PropertyCallbackInfo& cbinfo)
: CallbackWrapperBase(cbinfo, 0) {}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
if (buffer_length > 0) {
napi_value undefined =
v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
for (size_t i = 0; i < buffer_length; i += 1) {
buffer[i] = undefined;
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
_cbinfo.GetReturnValue().Set(val);
}
};
class SetterCallbackWrapper
: public CallbackWrapperBase,
&CallbackBundle::setter> {
public:
static void Invoke(v8::Local property,
v8::Local value,
const v8::PropertyCallbackInfo& info) {
SetterCallbackWrapper cbwrapper(info, value);
cbwrapper.InvokeCallback();
}
SetterCallbackWrapper(const v8::PropertyCallbackInfo& cbinfo,
const v8::Local& value)
: CallbackWrapperBase(cbinfo, 1), _value(value) {}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
if (buffer_length > 0) {
buffer[0] = v8impl::JsValueFromV8LocalValue(_value);
if (buffer_length > 1) {
napi_value undefined = v8impl::JsValueFromV8LocalValue(
v8::Undefined(_cbinfo.GetIsolate()));
for (size_t i = 1; i < buffer_length; i += 1) {
buffer[i] = undefined;
}
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
// Ignore any value returned from a setter callback.
}
private:
const v8::Local& _value;
};
// Creates an object to be made available to the static function callback
// wrapper, used to retrieve the native callback function and data pointer.
static
v8::Local CreateFunctionCallbackData(napi_env env,
napi_callback cb,
void* data) {
CallbackBundle* bundle = new CallbackBundle();
bundle->function_or_getter = cb;
bundle->cb_data = data;
bundle->env = env;
v8::Local cbdata = v8::External::New(env->isolate, bundle);
bundle->BindLifecycleTo(env->isolate, cbdata);
return cbdata;
}
// Creates an object to be made available to the static getter/setter
// callback wrapper, used to retrieve the native getter/setter callback
// function and data pointer.
static
v8::Local CreateAccessorCallbackData(napi_env env,
napi_callback getter,
napi_callback setter,
void* data) {
CallbackBundle* bundle = new CallbackBundle();
bundle->function_or_getter = getter;
bundle->setter = setter;
bundle->cb_data = data;
bundle->env = env;
v8::Local cbdata = v8::External::New(env->isolate, bundle);
bundle->BindLifecycleTo(env->isolate, cbdata);
return cbdata;
}
static void DeleteEnv(napi_env env, void* data, void* hint) {
delete env;
}
static
napi_env GetEnv(v8::Local context) {
napi_env result;
auto isolate = context->GetIsolate();
auto global = context->Global();
// In the case of the string for which we grab the private and the value of
// the private on the global object we can call .ToLocalChecked() directly
// because we need to stop hard if either of them is empty.
//
// Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env))
.ToLocalChecked();
if (value->IsExternal()) {
result = static_cast(value.As()->Value());
} else {
result = new napi_env__(isolate, node::GetCurrentEventLoop(isolate));
auto external = v8::External::New(isolate, result);
// We must also stop hard if the result of assigning the env to the global
// is either nothing or false.
CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external)
.FromJust());
// Create a self-destructing reference to external that will get rid of the
// napi_env when external goes out of scope.
Reference::New(result, external, 0, true, DeleteEnv, nullptr, nullptr);
}
return result;
}
enum UnwrapAction {
KeepWrap,
RemoveWrap
};
static
napi_status Unwrap(napi_env env,
napi_value js_object,
void** result,
UnwrapAction action) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
if (action == KeepWrap) {
CHECK_ARG(env, result);
}
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local value = v8impl::V8LocalValueFromJsValue(js_object);
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
v8::Local obj = value.As();
auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
.ToLocalChecked();
RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg);
Reference* reference =
static_cast(val.As()->Value());
if (result) {
*result = reference->Data();
}
if (action == RemoveWrap) {
CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
.FromJust());
Reference::Delete(reference);
}
return GET_RETURN_STATUS(env);
}
static
napi_status ConcludeDeferred(napi_env env,
napi_deferred deferred,
napi_value result,
bool is_resolved) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Local context = env->isolate->GetCurrentContext();
node::Persistent* deferred_ref =
NodePersistentFromJsDeferred(deferred);
v8::Local v8_deferred =
v8::Local::New(env->isolate, *deferred_ref);
auto v8_resolver = v8::Local::Cast(v8_deferred);
v8::Maybe success = is_resolved ?
v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) :
v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result));
delete deferred_ref;
RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure);
return GET_RETURN_STATUS(env);
}
class ThreadSafeFunction : public node::AsyncResource {
public:
ThreadSafeFunction(v8::Local func,
v8::Local resource,
v8::Local name,
size_t thread_count_,
void* context_,
size_t max_queue_size_,
napi_env env_,
void* finalize_data_,
napi_finalize finalize_cb_,
napi_threadsafe_function_call_js call_js_cb_):
AsyncResource(env_->isolate,
resource,
*v8::String::Utf8Value(env_->isolate, name)),
thread_count(thread_count_),
is_closing(false),
context(context_),
max_queue_size(max_queue_size_),
env(env_),
finalize_data(finalize_data_),
finalize_cb(finalize_cb_),
call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
handles_closing(false) {
ref.Reset(env->isolate, func);
node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
}
~ThreadSafeFunction() {
node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
}
// These methods can be called from any thread.
napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
node::Mutex::ScopedLock lock(this->mutex);
while (queue.size() >= max_queue_size &&
max_queue_size > 0 &&
!is_closing) {
if (mode == napi_tsfn_nonblocking) {
return napi_queue_full;
}
cond->Wait(lock);
}
if (is_closing) {
if (thread_count == 0) {
return napi_invalid_arg;
} else {
thread_count--;
return napi_closing;
}
} else {
if (uv_async_send(&async) != 0) {
return napi_generic_failure;
}
queue.push(data);
return napi_ok;
}
}
napi_status Acquire() {
node::Mutex::ScopedLock lock(this->mutex);
if (is_closing) {
return napi_closing;
}
thread_count++;
return napi_ok;
}
napi_status Release(napi_threadsafe_function_release_mode mode) {
node::Mutex::ScopedLock lock(this->mutex);
if (thread_count == 0) {
return napi_invalid_arg;
}
thread_count--;
if (thread_count == 0 || mode == napi_tsfn_abort) {
if (!is_closing) {
is_closing = (mode == napi_tsfn_abort);
if (is_closing && max_queue_size > 0) {
cond->Signal(lock);
}
if (uv_async_send(&async) != 0) {
return napi_generic_failure;
}
}
}
return napi_ok;
}
void EmptyQueueAndDelete() {
for (; !queue.empty() ; queue.pop()) {
call_js_cb(nullptr, nullptr, context, queue.front());
}
delete this;
}
// These methods must only be called from the loop thread.
napi_status Init() {
ThreadSafeFunction* ts_fn = this;
if (uv_async_init(env->loop, &async, AsyncCb) == 0) {
if (max_queue_size > 0) {
cond.reset(new node::ConditionVariable);
}
if ((max_queue_size == 0 || cond.get() != nullptr) &&
uv_idle_init(env->loop, &idle) == 0) {
return napi_ok;
}
NodeEnv()->CloseHandle(
reinterpret_cast(&async),
[](uv_handle_t* handle) -> void {
ThreadSafeFunction* ts_fn =
node::ContainerOf(&ThreadSafeFunction::async,
reinterpret_cast(handle));
delete ts_fn;
});
// Prevent the thread-safe function from being deleted here, because
// the callback above will delete it.
ts_fn = nullptr;
}
delete ts_fn;
return napi_generic_failure;
}
napi_status Unref() {
uv_unref(reinterpret_cast(&async));
uv_unref(reinterpret_cast(&idle));
return napi_ok;
}
napi_status Ref() {
uv_ref(reinterpret_cast(&async));
uv_ref(reinterpret_cast(&idle));
return napi_ok;
}
void DispatchOne() {
void* data = nullptr;
bool popped_value = false;
bool idle_stop_failed = false;
{
node::Mutex::ScopedLock lock(this->mutex);
if (is_closing) {
CloseHandlesAndMaybeDelete();
} else {
size_t size = queue.size();
if (size > 0) {
data = queue.front();
queue.pop();
popped_value = true;
if (size == max_queue_size && max_queue_size > 0) {
cond->Signal(lock);
}
size--;
}
if (size == 0) {
if (thread_count == 0) {
is_closing = true;
if (max_queue_size > 0) {
cond->Signal(lock);
}
CloseHandlesAndMaybeDelete();
} else {
if (uv_idle_stop(&idle) != 0) {
idle_stop_failed = true;
}
}
}
}
}
if (popped_value || idle_stop_failed) {
v8::HandleScope scope(env->isolate);
CallbackScope cb_scope(this);
if (idle_stop_failed) {
CHECK(napi_throw_error(env,
"ERR_NAPI_TSFN_STOP_IDLE_LOOP",
"Failed to stop the idle loop") == napi_ok);
} else {
v8::Local js_cb =
v8::Local::New(env->isolate, ref);
call_js_cb(env,
v8impl::JsValueFromV8LocalValue(js_cb),
context,
data);
}
}
}
node::Environment* NodeEnv() {
// Grabbing the Node.js environment requires a handle scope because it
// looks up fields on the current context.
v8::HandleScope scope(env->isolate);
node::Environment* node_env = node::Environment::GetCurrent(env->isolate);
CHECK_NOT_NULL(node_env);
return node_env;
}
void MaybeStartIdle() {
if (uv_idle_start(&idle, IdleCb) != 0) {
v8::HandleScope scope(env->isolate);
CallbackScope cb_scope(this);
CHECK(napi_throw_error(env,
"ERR_NAPI_TSFN_START_IDLE_LOOP",
"Failed to start the idle loop") == napi_ok);
}
}
void Finalize() {
v8::HandleScope scope(env->isolate);
if (finalize_cb) {
CallbackScope cb_scope(this);
finalize_cb(env, finalize_data, context);
}
EmptyQueueAndDelete();
}
inline void* Context() {
return context;
}
void CloseHandlesAndMaybeDelete(bool set_closing = false) {
if (set_closing) {
node::Mutex::ScopedLock lock(this->mutex);
is_closing = true;
if (max_queue_size > 0) {
cond->Signal(lock);
}
}
if (handles_closing) {
return;
}
handles_closing = true;
NodeEnv()->CloseHandle(
reinterpret_cast(&async),
[](uv_handle_t* handle) -> void {
ThreadSafeFunction* ts_fn =
node::ContainerOf(&ThreadSafeFunction::async,
reinterpret_cast(handle));
ts_fn->NodeEnv()->CloseHandle(
reinterpret_cast(&ts_fn->idle),
[](uv_handle_t* handle) -> void {
ThreadSafeFunction* ts_fn =
node::ContainerOf(&ThreadSafeFunction::idle,
reinterpret_cast(handle));
ts_fn->Finalize();
});
});
}
// Default way of calling into JavaScript. Used when ThreadSafeFunction is
// without a call_js_cb_.
static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
if (!(env == nullptr || cb == nullptr)) {
napi_value recv;
napi_status status;
status = napi_get_undefined(env, &recv);
if (status != napi_ok) {
napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
"Failed to retrieve undefined value");
return;
}
status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
if (status != napi_ok && status != napi_pending_exception) {
napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS",
"Failed to call JS callback");
return;
}
}
}
static void IdleCb(uv_idle_t* idle) {
ThreadSafeFunction* ts_fn =
node::ContainerOf(&ThreadSafeFunction::idle, idle);
ts_fn->DispatchOne();
}
static void AsyncCb(uv_async_t* async) {
ThreadSafeFunction* ts_fn =
node::ContainerOf(&ThreadSafeFunction::async, async);
ts_fn->MaybeStartIdle();
}
static void Cleanup(void* data) {
reinterpret_cast(data)
->CloseHandlesAndMaybeDelete(true);
}
private:
// These are variables protected by the mutex.
node::Mutex mutex;
std::unique_ptr cond;
std::queue queue;
uv_async_t async;
uv_idle_t idle;
size_t thread_count;
bool is_closing;
// These are variables set once, upon creation, and then never again, which
// means we don't need the mutex to read them.
void* context;
size_t max_queue_size;
// These are variables accessed only from the loop thread.
node::Persistent ref;
napi_env env;
void* finalize_data;
napi_finalize finalize_cb;
napi_threadsafe_function_call_js call_js_cb;
bool handles_closing;
};
enum WrapType {
retrievable,
anonymous
};
template static inline
napi_status Wrap(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
void* finalize_hint,
napi_ref* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local value = v8impl::V8LocalValueFromJsValue(js_object);
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
v8::Local obj = value.As();
if (wrap_type == retrievable) {
// If we've already wrapped this object, we error out.
RETURN_STATUS_IF_FALSE(env,
!obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
.FromJust(),
napi_invalid_arg);
} else if (wrap_type == anonymous) {
// If no finalize callback is provided, we error out.
CHECK_ARG(env, finalize_cb);
}
v8impl::Reference* reference = nullptr;
if (result != nullptr) {
// The returned reference should be deleted via napi_delete_reference()
// ONLY in response to the finalize callback invocation. (If it is deleted
// before then, then the finalize callback will never be invoked.)
// Therefore a finalize callback is required when returning a reference.
CHECK_ARG(env, finalize_cb);
reference = v8impl::Reference::New(
env, obj, 0, false, finalize_cb, native_object, finalize_hint);
*result = reinterpret_cast(reference);
} else {
// Create a self-deleting reference.
reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb,
native_object, finalize_cb == nullptr ? nullptr : finalize_hint);
}
if (wrap_type == retrievable) {
CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper),
v8::External::New(isolate, reference)).FromJust());
}
return GET_RETURN_STATUS(env);
}
} // end of namespace v8impl
// Intercepts the Node-V8 module registration callback. Converts parameters
// to NAPI equivalents and then calls the registration callback specified
// by the NAPI module.
void napi_module_register_cb(v8::Local exports,
v8::Local module,
v8::Local context,
void* priv) {
napi_module_register_by_symbol(exports, module, context,
static_cast(priv)->nm_register_func);
}
} // end of anonymous namespace
void napi_module_register_by_symbol(v8::Local exports,
v8::Local module,
v8::Local context,
napi_addon_register_func init) {
if (init == nullptr) {
node::Environment* node_env = node::Environment::GetCurrent(context);
CHECK_NOT_NULL(node_env);
node_env->ThrowError(
"Module has no declared entry point.");
return;
}
// Create a new napi_env for this module or reference one if a pre-existing
// one is found.
napi_env env = v8impl::GetEnv(context);
napi_value _exports;
NAPI_CALL_INTO_MODULE_THROW(env,
_exports = init(env, v8impl::JsValueFromV8LocalValue(exports)));
// If register function returned a non-null exports object different from
// the exports object we passed it, set that as the "exports" property of
// the module.
if (_exports != nullptr &&
_exports != v8impl::JsValueFromV8LocalValue(exports)) {
napi_value _module = v8impl::JsValueFromV8LocalValue(module);
napi_set_named_property(env, _module, "exports", _exports);
}
}
// Registers a NAPI module.
void napi_module_register(napi_module* mod) {
node::node_module* nm = new node::node_module {
-1,
mod->nm_flags,
nullptr,
mod->nm_filename,
nullptr,
napi_module_register_cb,
mod->nm_modname,
mod, // priv
nullptr,
};
node::node_module_register(nm);
}
napi_status napi_add_env_cleanup_hook(napi_env env,
void (*fun)(void* arg),
void* arg) {
CHECK_ENV(env);
CHECK_ARG(env, fun);
node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
return napi_ok;
}
napi_status napi_remove_env_cleanup_hook(napi_env env,
void (*fun)(void* arg),
void* arg) {
CHECK_ENV(env);
CHECK_ARG(env, fun);
node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
return napi_ok;
}
// Warning: Keep in-sync with napi_status enum
static
const char* error_messages[] = {nullptr,
"Invalid argument",
"An object was expected",
"A string was expected",
"A string or symbol was expected",
"A function was expected",
"A number was expected",
"A boolean was expected",
"An array was expected",
"Unknown failure",
"An exception is pending",
"The async work item was cancelled",
"napi_escape_handle already called on scope",
"Invalid handle scope usage",
"Invalid callback scope usage",
"Thread-safe function queue is full",
"Thread-safe function handle is closing",
"A bigint was expected",
};
static inline napi_status napi_clear_last_error(napi_env env) {
env->last_error.error_code = napi_ok;
// TODO(boingoing): Should this be a callback?
env->last_error.engine_error_code = 0;
env->last_error.engine_reserved = nullptr;
return napi_ok;
}
static inline
napi_status napi_set_last_error(napi_env env, napi_status error_code,
uint32_t engine_error_code,
void* engine_reserved) {
env->last_error.error_code = error_code;
env->last_error.engine_error_code = engine_error_code;
env->last_error.engine_reserved = engine_reserved;
return error_code;
}
napi_status napi_get_last_error_info(napi_env env,
const napi_extended_error_info** result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
// you must update this assert to reference the last message
// in the napi_status enum each time a new error message is added.
// We don't have a napi_status_last as this would result in an ABI
// change each time a message was added.
static_assert(
node::arraysize(error_messages) == napi_bigint_expected + 1,
"Count of error messages must match count of error values");
CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch);
// Wait until someone requests the last error information to fetch the error
// message string
env->last_error.error_message =
error_messages[env->last_error.error_code];
*result = &(env->last_error);
return napi_ok;
}
napi_status napi_fatal_exception(napi_env env, napi_value err) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, err);
v8::Local local_err = v8impl::V8LocalValueFromJsValue(err);
v8impl::trigger_fatal_exception(env, local_err);
return napi_clear_last_error(env);
}
NAPI_NO_RETURN void napi_fatal_error(const char* location,
size_t location_len,
const char* message,
size_t message_len) {
std::string location_string;
std::string message_string;
if (location_len != NAPI_AUTO_LENGTH) {
location_string.assign(
const_cast(location), location_len);
} else {
location_string.assign(
const_cast(location), strlen(location));
}
if (message_len != NAPI_AUTO_LENGTH) {
message_string.assign(
const_cast(message), message_len);
} else {
message_string.assign(
const_cast(message), strlen(message));
}
node::FatalError(location_string.c_str(), message_string.c_str());
}
napi_status napi_create_function(napi_env env,
const char* utf8name,
size_t length,
napi_callback cb,
void* callback_data,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
CHECK_ARG(env, cb);
v8::Isolate* isolate = env->isolate;
v8::Local return_value;
v8::EscapableHandleScope scope(isolate);
v8::Local cbdata =
v8impl::CreateFunctionCallbackData(env, cb, callback_data);
RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure);
v8::Local context = isolate->GetCurrentContext();
v8::MaybeLocal maybe_function =
v8::Function::New(context,
v8impl::FunctionCallbackWrapper::Invoke,
cbdata);
CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure);
return_value = scope.Escape(maybe_function.ToLocalChecked());
if (utf8name != nullptr) {
v8::Local name_string;
CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
return_value->SetName(name_string);
}
*result = v8impl::JsValueFromV8LocalValue(return_value);
return GET_RETURN_STATUS(env);
}
napi_status napi_define_class(napi_env env,
const char* utf8name,
size_t length,
napi_callback constructor,
void* callback_data,
size_t property_count,
const napi_property_descriptor* properties,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
CHECK_ARG(env, constructor);
v8::Isolate* isolate = env->isolate;
v8::EscapableHandleScope scope(isolate);
v8::Local cbdata =
v8impl::CreateFunctionCallbackData(env, constructor, callback_data);
RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure);
v8::Local tpl = v8::FunctionTemplate::New(
isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);
v8::Local name_string;
CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
tpl->SetClassName(name_string);
size_t static_property_count = 0;
for (size_t i = 0; i < property_count; i++) {
const napi_property_descriptor* p = properties + i;
if ((p->attributes & napi_static) != 0) {
// Static properties are handled separately below.
static_property_count++;
continue;
}
v8::Local property_name;
napi_status status =
v8impl::V8NameFromPropertyDescriptor(env, p, &property_name);
if (status != napi_ok) {
return napi_set_last_error(env, status);
}
v8::PropertyAttribute attributes =
v8impl::V8PropertyAttributesFromDescriptor(p);
// This code is similar to that in napi_define_properties(); the
// difference is it applies to a template instead of an object.
if (p->getter != nullptr || p->setter != nullptr) {
v8::Local cbdata = v8impl::CreateAccessorCallbackData(
env, p->getter, p->setter, p->data);
tpl->PrototypeTemplate()->SetAccessor(
property_name,
p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr,
p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr,
cbdata,
v8::AccessControl::DEFAULT,
attributes);
} else if (p->method != nullptr) {
v8::Local cbdata =
v8impl::CreateFunctionCallbackData(env, p->method, p->data);
RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure);
v8::Local t =
v8::FunctionTemplate::New(isolate,
v8impl::FunctionCallbackWrapper::Invoke,
cbdata,
v8::Signature::New(isolate, tpl));
tpl->PrototypeTemplate()->Set(property_name, t, attributes);
} else {
v8::Local value = v8impl::V8LocalValueFromJsValue(p->value);
tpl->PrototypeTemplate()->Set(property_name, value, attributes);
}
}
v8::Local context = isolate->GetCurrentContext();
*result = v8impl::JsValueFromV8LocalValue(
scope.Escape(tpl->GetFunction(context).ToLocalChecked()));
if (static_property_count > 0) {
std::vector static_descriptors;
static_descriptors.reserve(static_property_count);
for (size_t i = 0; i < property_count; i++) {
const napi_property_descriptor* p = properties + i;
if ((p->attributes & napi_static) != 0) {
static_descriptors.push_back(*p);
}
}
napi_status status =
napi_define_properties(env,
*result,
static_descriptors.size(),
static_descriptors.data());
if (status != napi_ok) return status;
}
return GET_RETURN_STATUS(env);
}
napi_status napi_get_property_names(napi_env env,
napi_value object,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
auto maybe_propertynames = obj->GetPropertyNames(context);
CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(
maybe_propertynames.ToLocalChecked());
return GET_RETURN_STATUS(env);
}
napi_status napi_set_property(napi_env env,
napi_value object,
napi_value key,
napi_value value) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, key);
CHECK_ARG(env, value);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local k = v8impl::V8LocalValueFromJsValue(key);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
v8::Maybe set_maybe = obj->Set(context, k, val);
RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure);
return GET_RETURN_STATUS(env);
}
napi_status napi_has_property(napi_env env,
napi_value object,
napi_value key,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
CHECK_ARG(env, key);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local k = v8impl::V8LocalValueFromJsValue(key);
v8::Maybe has_maybe = obj->Has(context, k);
CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
*result = has_maybe.FromMaybe(false);
return GET_RETURN_STATUS(env);
}
napi_status napi_get_property(napi_env env,
napi_value object,
napi_value key,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, key);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local k = v8impl::V8LocalValueFromJsValue(key);
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
auto get_maybe = obj->Get(context, k);
CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure);
v8::Local val = get_maybe.ToLocalChecked();
*result = v8impl::JsValueFromV8LocalValue(val);
return GET_RETURN_STATUS(env);
}
napi_status napi_delete_property(napi_env env,
napi_value object,
napi_value key,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, key);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local k = v8impl::V8LocalValueFromJsValue(key);
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Maybe delete_maybe = obj->Delete(context, k);
CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure);
if (result != nullptr)
*result = delete_maybe.FromMaybe(false);
return GET_RETURN_STATUS(env);
}
napi_status napi_has_own_property(napi_env env,
napi_value object,
napi_value key,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, key);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local k = v8impl::V8LocalValueFromJsValue(key);
RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected);
v8::Maybe has_maybe = obj->HasOwnProperty(context, k.As());
CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
*result = has_maybe.FromMaybe(false);
return GET_RETURN_STATUS(env);
}
napi_status napi_set_named_property(napi_env env,
napi_value object,
const char* utf8name,
napi_value value) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, value);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local key;
CHECK_NEW_FROM_UTF8(env, key, utf8name);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
v8::Maybe set_maybe = obj->Set(context, key, val);
RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure);
return GET_RETURN_STATUS(env);
}
napi_status napi_has_named_property(napi_env env,
napi_value object,
const char* utf8name,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local key;
CHECK_NEW_FROM_UTF8(env, key, utf8name);
v8::Maybe has_maybe = obj->Has(context, key);
CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
*result = has_maybe.FromMaybe(false);
return GET_RETURN_STATUS(env);
}
napi_status napi_get_named_property(napi_env env,
napi_value object,
const char* utf8name,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local key;
CHECK_NEW_FROM_UTF8(env, key, utf8name);
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
auto get_maybe = obj->Get(context, key);
CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure);
v8::Local val = get_maybe.ToLocalChecked();
*result = v8impl::JsValueFromV8LocalValue(val);
return GET_RETURN_STATUS(env);
}
napi_status napi_set_element(napi_env env,
napi_value object,
uint32_t index,
napi_value value) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, value);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
auto set_maybe = obj->Set(context, index, val);
RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure);
return GET_RETURN_STATUS(env);
}
napi_status napi_has_element(napi_env env,
napi_value object,
uint32_t index,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Maybe has_maybe = obj->Has(context, index);
CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
*result = has_maybe.FromMaybe(false);
return GET_RETURN_STATUS(env);
}
napi_status napi_get_element(napi_env env,
napi_value object,
uint32_t index,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
auto get_maybe = obj->Get(context, index);
CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked());
return GET_RETURN_STATUS(env);
}
napi_status napi_delete_element(napi_env env,
napi_value object,
uint32_t index,
bool* result) {
NAPI_PREAMBLE(env);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Maybe delete_maybe = obj->Delete(context, index);
CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure);
if (result != nullptr)
*result = delete_maybe.FromMaybe(false);
return GET_RETURN_STATUS(env);
}
napi_status napi_define_properties(napi_env env,
napi_value object,
size_t property_count,
const napi_property_descriptor* properties) {
NAPI_PREAMBLE(env);
if (property_count > 0) {
CHECK_ARG(env, properties);
}
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
for (size_t i = 0; i < property_count; i++) {
const napi_property_descriptor* p = &properties[i];
v8::Local property_name;
napi_status status =
v8impl::V8NameFromPropertyDescriptor(env, p, &property_name);
if (status != napi_ok) {
return napi_set_last_error(env, status);
}
v8::PropertyAttribute attributes =
v8impl::V8PropertyAttributesFromDescriptor(p);
if (p->getter != nullptr || p->setter != nullptr) {
v8::Local cbdata = v8impl::CreateAccessorCallbackData(
env,
p->getter,
p->setter,
p->data);
auto set_maybe = obj->SetAccessor(
context,
property_name,
p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr,
p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr,
cbdata,
v8::AccessControl::DEFAULT,
attributes);
if (!set_maybe.FromMaybe(false)) {
return napi_set_last_error(env, napi_invalid_arg);
}
} else if (p->method != nullptr) {
v8::Local cbdata =
v8impl::CreateFunctionCallbackData(env, p->method, p->data);
CHECK_MAYBE_EMPTY(env, cbdata, napi_generic_failure);
v8::MaybeLocal maybe_fn =
v8::Function::New(context,
v8impl::FunctionCallbackWrapper::Invoke,
cbdata);
CHECK_MAYBE_EMPTY(env, maybe_fn, napi_generic_failure);
auto define_maybe = obj->DefineOwnProperty(
context, property_name, maybe_fn.ToLocalChecked(), attributes);
if (!define_maybe.FromMaybe(false)) {
return napi_set_last_error(env, napi_generic_failure);
}
} else {
v8::Local value = v8impl::V8LocalValueFromJsValue(p->value);
auto define_maybe =
obj->DefineOwnProperty(context, property_name, value, attributes);
if (!define_maybe.FromMaybe(false)) {
return napi_set_last_error(env, napi_invalid_arg);
}
}
}
return GET_RETURN_STATUS(env);
}
napi_status napi_is_array(napi_env env, napi_value value, bool* result) {
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
*result = val->IsArray();
return napi_clear_last_error(env);
}
napi_status napi_get_array_length(napi_env env,
napi_value value,
uint32_t* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected);
v8::Local arr = val.As();
*result = arr->Length();
return GET_RETURN_STATUS(env);
}
napi_status napi_strict_equals(napi_env env,
napi_value lhs,
napi_value rhs,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, lhs);
CHECK_ARG(env, rhs);
CHECK_ARG(env, result);
v8::Local a = v8impl::V8LocalValueFromJsValue(lhs);
v8::Local b = v8impl::V8LocalValueFromJsValue(rhs);
*result = a->StrictEquals(b);
return GET_RETURN_STATUS(env);
}
napi_status napi_get_prototype(napi_env env,
napi_value object,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local obj;
CHECK_TO_OBJECT(env, context, obj, object);
v8::Local val = obj->GetPrototype();
*result = v8impl::JsValueFromV8LocalValue(val);
return GET_RETURN_STATUS(env);
}
napi_status napi_create_object(napi_env env, napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Object::New(env->isolate));
return napi_clear_last_error(env);
}
napi_status napi_create_array(napi_env env, napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Array::New(env->isolate));
return napi_clear_last_error(env);
}
napi_status napi_create_array_with_length(napi_env env,
size_t length,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Array::New(env->isolate, length));
return napi_clear_last_error(env);
}
napi_status napi_create_string_latin1(napi_env env,
const char* str,
size_t length,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
auto isolate = env->isolate;
auto str_maybe =
v8::String::NewFromOneByte(isolate,
reinterpret_cast(str),
v8::NewStringType::kInternalized,
length);
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
return napi_clear_last_error(env);
}
napi_status napi_create_string_utf8(napi_env env,
const char* str,
size_t length,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
v8::Local s;
CHECK_NEW_FROM_UTF8_LEN(env, s, str, length);
*result = v8impl::JsValueFromV8LocalValue(s);
return napi_clear_last_error(env);
}
napi_status napi_create_string_utf16(napi_env env,
const char16_t* str,
size_t length,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
auto isolate = env->isolate;
auto str_maybe =
v8::String::NewFromTwoByte(isolate,
reinterpret_cast(str),
v8::NewStringType::kInternalized,
length);
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
return napi_clear_last_error(env);
}
napi_status napi_create_double(napi_env env,
double value,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Number::New(env->isolate, value));
return napi_clear_last_error(env);
}
napi_status napi_create_int32(napi_env env,
int32_t value,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Integer::New(env->isolate, value));
return napi_clear_last_error(env);
}
napi_status napi_create_uint32(napi_env env,
uint32_t value,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Integer::NewFromUnsigned(env->isolate, value));
return napi_clear_last_error(env);
}
napi_status napi_create_int64(napi_env env,
int64_t value,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Number::New(env->isolate, static_cast(value)));
return napi_clear_last_error(env);
}
napi_status napi_create_bigint_int64(napi_env env,
int64_t value,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::BigInt::New(env->isolate, value));
return napi_clear_last_error(env);
}
napi_status napi_create_bigint_uint64(napi_env env,
uint64_t value,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::BigInt::NewFromUnsigned(env->isolate, value));
return napi_clear_last_error(env);
}
napi_status napi_create_bigint_words(napi_env env,
int sign_bit,
size_t word_count,
const uint64_t* words,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, words);
CHECK_ARG(env, result);
v8::Local context = env->isolate->GetCurrentContext();
if (word_count > INT_MAX) {
napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded");
return napi_set_last_error(env, napi_pending_exception);
}
v8::MaybeLocal b = v8::BigInt::NewFromWords(
context, sign_bit, word_count, words);
if (try_catch.HasCaught()) {
return napi_set_last_error(env, napi_pending_exception);
} else {
CHECK_MAYBE_EMPTY(env, b, napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked());
return napi_clear_last_error(env);
}
}
napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
if (value) {
*result = v8impl::JsValueFromV8LocalValue(v8::True(isolate));
} else {
*result = v8impl::JsValueFromV8LocalValue(v8::False(isolate));
}
return napi_clear_last_error(env);
}
napi_status napi_create_symbol(napi_env env,
napi_value description,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
if (description == nullptr) {
*result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate));
} else {
v8::Local desc = v8impl::V8LocalValueFromJsValue(description);
RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected);
*result = v8impl::JsValueFromV8LocalValue(
v8::Symbol::New(isolate, desc.As()));
}
return napi_clear_last_error(env);
}
static napi_status set_error_code(napi_env env,
v8::Local error,
napi_value code,
const char* code_cstring) {
if ((code != nullptr) || (code_cstring != nullptr)) {
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local err_object = error.As();
v8::Local code_value = v8impl::V8LocalValueFromJsValue(code);
if (code != nullptr) {
code_value = v8impl::V8LocalValueFromJsValue(code);
RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected);
} else {
CHECK_NEW_FROM_UTF8(env, code_value, code_cstring);
}
v8::Local code_key;
CHECK_NEW_FROM_UTF8(env, code_key, "code");
v8::Maybe set_maybe = err_object->Set(context, code_key, code_value);
RETURN_STATUS_IF_FALSE(env,
set_maybe.FromMaybe(false),
napi_generic_failure);
// now update the name to be "name [code]" where name is the
// original name and code is the code associated with the Error
v8::Local name_string;
CHECK_NEW_FROM_UTF8(env, name_string, "");
v8::Local name_key;
CHECK_NEW_FROM_UTF8(env, name_key, "name");
auto maybe_name = err_object->Get(context, name_key);
if (!maybe_name.IsEmpty()) {
v8::Local name = maybe_name.ToLocalChecked();
if (name->IsString()) {
name_string =
v8::String::Concat(isolate, name_string, name.As());
}
}
name_string = v8::String::Concat(
isolate, name_string, node::FIXED_ONE_BYTE_STRING(isolate, " ["));
name_string =
v8::String::Concat(isolate, name_string, code_value.As());
name_string = v8::String::Concat(
isolate, name_string, node::FIXED_ONE_BYTE_STRING(isolate, "]"));
set_maybe = err_object->Set(context, name_key, name_string);
RETURN_STATUS_IF_FALSE(env,
set_maybe.FromMaybe(false),
napi_generic_failure);
}
return napi_ok;
}
napi_status napi_create_error(napi_env env,
napi_value code,
napi_value msg,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, msg);
CHECK_ARG(env, result);
v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg);
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
v8::Local error_obj =
v8::Exception::Error(message_value.As());
napi_status status = set_error_code(env, error_obj, code, nullptr);
if (status != napi_ok) return status;
*result = v8impl::JsValueFromV8LocalValue(error_obj);
return napi_clear_last_error(env);
}
napi_status napi_create_type_error(napi_env env,
napi_value code,
napi_value msg,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, msg);
CHECK_ARG(env, result);
v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg);
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
v8::Local error_obj =
v8::Exception::TypeError(message_value.As());
napi_status status = set_error_code(env, error_obj, code, nullptr);
if (status != napi_ok) return status;
*result = v8impl::JsValueFromV8LocalValue(error_obj);
return napi_clear_last_error(env);
}
napi_status napi_create_range_error(napi_env env,
napi_value code,
napi_value msg,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, msg);
CHECK_ARG(env, result);
v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg);
RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
v8::Local error_obj =
v8::Exception::RangeError(message_value.As());
napi_status status = set_error_code(env, error_obj, code, nullptr);
if (status != napi_ok) return status;
*result = v8impl::JsValueFromV8LocalValue(error_obj);
return napi_clear_last_error(env);
}
napi_status napi_typeof(napi_env env,
napi_value value,
napi_valuetype* result) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local v = v8impl::V8LocalValueFromJsValue(value);
if (v->IsNumber()) {
*result = napi_number;
} else if (v->IsBigInt()) {
*result = napi_bigint;
} else if (v->IsString()) {
*result = napi_string;
} else if (v->IsFunction()) {
// This test has to come before IsObject because IsFunction
// implies IsObject
*result = napi_function;
} else if (v->IsExternal()) {
// This test has to come before IsObject because IsExternal
// implies IsObject
*result = napi_external;
} else if (v->IsObject()) {
*result = napi_object;
} else if (v->IsBoolean()) {
*result = napi_boolean;
} else if (v->IsUndefined()) {
*result = napi_undefined;
} else if (v->IsSymbol()) {
*result = napi_symbol;
} else if (v->IsNull()) {
*result = napi_null;
} else {
// Should not get here unless V8 has added some new kind of value.
return napi_set_last_error(env, napi_invalid_arg);
}
return napi_clear_last_error(env);
}
napi_status napi_get_undefined(napi_env env, napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Undefined(env->isolate));
return napi_clear_last_error(env);
}
napi_status napi_get_null(napi_env env, napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = v8impl::JsValueFromV8LocalValue(
v8::Null(env->isolate));
return napi_clear_last_error(env);
}
// Gets all callback info in a single call. (Ugly, but faster.)
napi_status napi_get_cb_info(
napi_env env, // [in] NAPI environment handle
napi_callback_info cbinfo, // [in] Opaque callback-info handle
size_t* argc, // [in-out] Specifies the size of the provided argv array
// and receives the actual count of args.
napi_value* argv, // [out] Array of values
napi_value* this_arg, // [out] Receives the JS 'this' arg for the call
void** data) { // [out] Receives the data pointer for the callback.
CHECK_ENV(env);
CHECK_ARG(env, cbinfo);
v8impl::CallbackWrapper* info =
reinterpret_cast(cbinfo);
if (argv != nullptr) {
CHECK_ARG(env, argc);
info->Args(argv, *argc);
}
if (argc != nullptr) {
*argc = info->ArgsLength();
}
if (this_arg != nullptr) {
*this_arg = info->This();
}
if (data != nullptr) {
*data = info->Data();
}
return napi_clear_last_error(env);
}
napi_status napi_get_new_target(napi_env env,
napi_callback_info cbinfo,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, cbinfo);
CHECK_ARG(env, result);
v8impl::CallbackWrapper* info =
reinterpret_cast(cbinfo);
*result = info->GetNewTarget();
return napi_clear_last_error(env);
}
napi_status napi_call_function(napi_env env,
napi_value recv,
napi_value func,
size_t argc,
const napi_value* argv,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, recv);
if (argc > 0) {
CHECK_ARG(env, argv);
}
v8::Isolate* isolate = env->isolate;
v8::Local context = isolate->GetCurrentContext();
v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv);
v8::Local v8func;
CHECK_TO_FUNCTION(env, v8func, func);
auto maybe = v8func->Call(context, v8recv, argc,
reinterpret_cast*>(const_cast(argv)));
if (try_catch.HasCaught()) {
return napi_set_last_error(env, napi_pending_exception);
} else {
if (result != nullptr) {
CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
}
return napi_clear_last_error(env);
}
}
napi_status napi_get_global(napi_env env, napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
// TODO(ianhall): what if we need the global object from a different
// context in the same isolate?
// Should napi_env be the current context rather than the current isolate?
v8::Local context = isolate->GetCurrentContext();
*result = v8impl::JsValueFromV8LocalValue(context->Global());
return napi_clear_last_error(env);
}
napi_status napi_throw(napi_env env, napi_value error) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, error);
v8::Isolate* isolate = env->isolate;
isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error));
// any VM calls after this point and before returning
// to the javascript invoker will fail
return napi_clear_last_error(env);
}
napi_status napi_throw_error(napi_env env,
const char* code,
const char* msg) {
NAPI_PREAMBLE(env);
v8::Isolate* isolate = env->isolate;
v8::Local str;
CHECK_NEW_FROM_UTF8(env, str, msg);
v8::Local error_obj = v8::Exception::Error(str);
napi_status status = set_error_code(env, error_obj, nullptr, code);
if (status != napi_ok) return status;
isolate->ThrowException(error_obj);
// any VM calls after this point and before returning
// to the javascript invoker will fail
return napi_clear_last_error(env);
}
napi_status napi_throw_type_error(napi_env env,
const char* code,
const char* msg) {
NAPI_PREAMBLE(env);
v8::Isolate* isolate = env->isolate;
v8::Local str;
CHECK_NEW_FROM_UTF8(env, str, msg);
v8::Local error_obj = v8::Exception::TypeError(str);
napi_status status = set_error_code(env, error_obj, nullptr, code);
if (status != napi_ok) return status;
isolate->ThrowException(error_obj);
// any VM calls after this point and before returning
// to the javascript invoker will fail
return napi_clear_last_error(env);
}
napi_status napi_throw_range_error(napi_env env,
const char* code,
const char* msg) {
NAPI_PREAMBLE(env);
v8::Isolate* isolate = env->isolate;
v8::Local str;
CHECK_NEW_FROM_UTF8(env, str, msg);
v8::Local error_obj = v8::Exception::RangeError(str);
napi_status status = set_error_code(env, error_obj, nullptr, code);
if (status != napi_ok) return status;
isolate->ThrowException(error_obj);
// any VM calls after this point and before returning
// to the javascript invoker will fail
return napi_clear_last_error(env);
}
napi_status napi_is_error(napi_env env, napi_value value, bool* result) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot
// throw JS exceptions.
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
*result = val->IsNativeError();
return napi_clear_last_error(env);
}
napi_status napi_get_value_double(napi_env env,
napi_value value,
double* result) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
*result = val.As()->Value();
return napi_clear_last_error(env);
}
napi_status napi_get_value_int32(napi_env env,
napi_value value,
int32_t* result) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local val = v8impl::V8LocalValueFromJsValue(value);
if (val->IsInt32()) {
*result = val.As