| #include <algorithm> |
| #include <climits> // INT_MAX |
| #include <cmath> |
| #ifndef NAPI_EXPERIMENTAL |
| #define NAPI_EXPERIMENTAL |
| #endif |
| #include "env-inl.h" |
| #include "js_native_api.h" |
| #include "js_native_api_v8.h" |
| #include "util-inl.h" |
| |
| #define CHECK_MAYBE_NOTHING(env, maybe, status) \ |
| RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) |
| |
| #define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \ |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsNothing()), (status)) |
| |
| #define CHECK_TO_NUMBER(env, context, result, src) \ |
| CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) |
| |
| // n-api defines NAPI_AUTO_LENGTH 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<int>(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); \ |
| RETURN_STATUS_IF_FALSE((env), (str) != nullptr, napi_invalid_arg); \ |
| auto str_maybe = v8::String::NewFromUtf8((env)->isolate, \ |
| (str), \ |
| v8::NewStringType::kInternalized, \ |
| static_cast<int>(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 CHECK_NEW_STRING_ARGS(env, str, length, result) \ |
| do { \ |
| CHECK_ENV_NOT_IN_GC((env)); \ |
| if ((length) > 0) CHECK_ARG((env), (str)); \ |
| CHECK_ARG((env), (result)); \ |
| RETURN_STATUS_IF_FALSE( \ |
| (env), \ |
| ((length) == NAPI_AUTO_LENGTH) || (length) <= INT_MAX, \ |
| napi_invalid_arg); \ |
| } 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) |
| |
| void napi_env__::InvokeFinalizerFromGC(v8impl::RefTracker* finalizer) { |
| if (module_api_version != NAPI_VERSION_EXPERIMENTAL) { |
| EnqueueFinalizer(finalizer); |
| } else { |
| // The experimental code calls finalizers immediately to release native |
| // objects as soon as possible. In that state any code that may affect GC |
| // state causes a fatal error. To work around this issue the finalizer code |
| // can call node_api_post_finalizer. |
| auto restore_state = node::OnScopeLeave( |
| [this, saved = in_gc_finalizer] { in_gc_finalizer = saved; }); |
| in_gc_finalizer = true; |
| finalizer->Finalize(); |
| } |
| } |
| |
| namespace v8impl { |
| namespace { |
| |
| template <typename CCharType, typename StringMaker> |
| napi_status NewString(napi_env env, |
| const CCharType* str, |
| size_t length, |
| napi_value* result, |
| StringMaker string_maker) { |
| CHECK_NEW_STRING_ARGS(env, str, length, result); |
| |
| auto isolate = env->isolate; |
| auto str_maybe = string_maker(isolate); |
| CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); |
| *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); |
| return napi_clear_last_error(env); |
| } |
| |
| template <typename CharType, typename CreateAPI, typename StringMaker> |
| napi_status NewExternalString(napi_env env, |
| CharType* str, |
| size_t length, |
| napi_finalize finalize_callback, |
| void* finalize_hint, |
| napi_value* result, |
| bool* copied, |
| CreateAPI create_api, |
| StringMaker string_maker) { |
| CHECK_NEW_STRING_ARGS(env, str, length, result); |
| |
| napi_status status; |
| #ifdef V8_ENABLE_SANDBOX |
| status = create_api(env, str, length, result); |
| if (status == napi_ok) { |
| if (copied != nullptr) { |
| *copied = true; |
| } |
| if (finalize_callback) { |
| env->CallFinalizer( |
| finalize_callback, static_cast<CharType*>(str), finalize_hint); |
| } |
| } |
| #else |
| status = NewString(env, str, length, result, string_maker); |
| if (status == napi_ok && copied != nullptr) { |
| *copied = false; |
| } |
| #endif // V8_ENABLE_SANDBOX |
| return status; |
| } |
| |
| class TrackedStringResource : private RefTracker { |
| public: |
| TrackedStringResource(napi_env env, |
| napi_finalize finalize_callback, |
| void* data, |
| void* finalize_hint) |
| : RefTracker(), finalizer_(env, finalize_callback, data, finalize_hint) { |
| Link(finalize_callback == nullptr ? &env->reflist |
| : &env->finalizing_reflist); |
| } |
| |
| protected: |
| // The only time Finalize() gets called before destructor is if the |
| // environment is dying. Finalize() expects that the item will be unlinked, |
| // so we do it here. V8 will still call destructor on us later, so we don't do |
| // any deleting here. We just null out env to avoid passing a stale pointer |
| // to the user's finalizer when V8 does finally call destructor. |
| void Finalize() override { |
| Unlink(); |
| finalizer_.ResetEnv(); |
| } |
| |
| ~TrackedStringResource() override { |
| Unlink(); |
| finalizer_.CallFinalizer(); |
| } |
| |
| private: |
| Finalizer finalizer_; |
| }; |
| |
| class ExternalOneByteStringResource final |
| : public v8::String::ExternalOneByteStringResource, |
| TrackedStringResource { |
| public: |
| ExternalOneByteStringResource(napi_env env, |
| char* string, |
| const size_t length, |
| napi_finalize finalize_callback, |
| void* finalize_hint) |
| : TrackedStringResource(env, finalize_callback, string, finalize_hint), |
| string_(string), |
| length_(length) {} |
| |
| const char* data() const override { return string_; } |
| size_t length() const override { return length_; } |
| |
| private: |
| const char* string_; |
| const size_t length_; |
| }; |
| |
| class ExternalStringResource final : public v8::String::ExternalStringResource, |
| TrackedStringResource { |
| public: |
| ExternalStringResource(napi_env env, |
| char16_t* string, |
| const size_t length, |
| napi_finalize finalize_callback, |
| void* finalize_hint) |
| : TrackedStringResource(env, finalize_callback, string, finalize_hint), |
| string_(reinterpret_cast<uint16_t*>(string)), |
| length_(length) {} |
| |
| const uint16_t* data() const override { return string_; } |
| size_t length() const override { return length_; } |
| |
| private: |
| const uint16_t* string_; |
| const size_t length_; |
| }; |
| |
| inline napi_status V8NameFromPropertyDescriptor( |
| napi_env env, |
| const napi_property_descriptor* p, |
| v8::Local<v8::Name>* result) { |
| if (p->utf8name != nullptr) { |
| CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); |
| } else { |
| v8::Local<v8::Value> property_value = |
| v8impl::V8LocalValueFromJsValue(p->name); |
| |
| RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); |
| *result = property_value.As<v8::Name>(); |
| } |
| |
| return napi_ok; |
| } |
| |
| // convert from n-api property attributes to v8::PropertyAttribute |
| inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( |
| const napi_property_descriptor* descriptor) { |
| unsigned int attribute_flags = v8::PropertyAttribute::None; |
| |
| // The napi_writable attribute is ignored for accessor descriptors, but |
| // V8 would throw `TypeError`s on assignment with nonexistence of a setter. |
| if ((descriptor->getter == nullptr && descriptor->setter == nullptr) && |
| (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<v8::PropertyAttribute>(attribute_flags); |
| } |
| |
| inline napi_deferred JsDeferredFromNodePersistent( |
| v8impl::Persistent<v8::Value>* local) { |
| return reinterpret_cast<napi_deferred>(local); |
| } |
| |
| inline v8impl::Persistent<v8::Value>* NodePersistentFromJsDeferred( |
| napi_deferred local) { |
| return reinterpret_cast<v8impl::Persistent<v8::Value>*>(local); |
| } |
| |
| 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<T> v) to mimic the behavior |
| // of a EscapableHandleScope::Escape(Local<T> 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 <typename T> |
| v8::Local<T> Escape(v8::Local<T> handle) { |
| escape_called_ = true; |
| return scope.Escape(handle); |
| } |
| |
| private: |
| v8::EscapableHandleScope scope; |
| bool escape_called_; |
| }; |
| |
| inline napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) { |
| return reinterpret_cast<napi_handle_scope>(s); |
| } |
| |
| inline HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) { |
| return reinterpret_cast<HandleScopeWrapper*>(s); |
| } |
| |
| inline napi_escapable_handle_scope |
| JsEscapableHandleScopeFromV8EscapableHandleScope( |
| EscapableHandleScopeWrapper* s) { |
| return reinterpret_cast<napi_escapable_handle_scope>(s); |
| } |
| |
| inline EscapableHandleScopeWrapper* |
| V8EscapableHandleScopeFromJsEscapableHandleScope( |
| napi_escapable_handle_scope s) { |
| return reinterpret_cast<EscapableHandleScopeWrapper*>(s); |
| } |
| |
| inline napi_status ConcludeDeferred(napi_env env, |
| napi_deferred deferred, |
| napi_value result, |
| bool is_resolved) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8impl::Persistent<v8::Value>* deferred_ref = |
| NodePersistentFromJsDeferred(deferred); |
| v8::Local<v8::Value> v8_deferred = |
| v8::Local<v8::Value>::New(env->isolate, *deferred_ref); |
| |
| auto v8_resolver = v8_deferred.As<v8::Promise::Resolver>(); |
| |
| v8::Maybe<bool> 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); |
| } |
| |
| enum UnwrapAction { KeepWrap, RemoveWrap }; |
| |
| inline 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::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object); |
| RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); |
| v8::Local<v8::Object> obj = value.As<v8::Object>(); |
| |
| 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<v8impl::Reference*>(val.As<v8::External>()->Value()); |
| |
| if (result) { |
| *result = reference->Data(); |
| } |
| |
| if (action == RemoveWrap) { |
| CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) |
| .FromJust()); |
| if (reference->ownership() == ReferenceOwnership::kUserland) { |
| // When the wrap is been removed, the finalizer should be reset. |
| reference->ResetFinalizer(); |
| } else { |
| delete reference; |
| } |
| } |
| |
| return GET_RETURN_STATUS(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 |
| class CallbackBundle { |
| public: |
| // Creates an object to be made available to the static function callback |
| // wrapper, used to retrieve the native callback function and data pointer. |
| static inline v8::Local<v8::Value> New(napi_env env, |
| napi_callback cb, |
| void* data) { |
| CallbackBundle* bundle = new CallbackBundle(); |
| bundle->cb = cb; |
| bundle->cb_data = data; |
| bundle->env = env; |
| |
| v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle); |
| ReferenceWithFinalizer::New( |
| env, cbdata, 0, ReferenceOwnership::kRuntime, Delete, bundle, nullptr); |
| return cbdata; |
| } |
| |
| static CallbackBundle* FromCallbackData(v8::Local<v8::Value> data) { |
| return reinterpret_cast<CallbackBundle*>(data.As<v8::External>()->Value()); |
| } |
| |
| public: |
| napi_env env; // Necessary to invoke C++ NAPI callback |
| void* cb_data; // The user provided callback data |
| napi_callback cb; |
| |
| private: |
| static void Delete(napi_env env, void* data, void* hint) { |
| CallbackBundle* bundle = static_cast<CallbackBundle*>(data); |
| delete bundle; |
| } |
| }; |
| |
| // Wraps up v8::FunctionCallbackInfo. |
| // The class must be stack allocated. |
| class FunctionCallbackWrapper { |
| public: |
| static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| FunctionCallbackWrapper cbwrapper(info); |
| cbwrapper.InvokeCallback(); |
| } |
| |
| static inline napi_status NewFunction(napi_env env, |
| napi_callback cb, |
| void* cb_data, |
| v8::Local<v8::Function>* result) { |
| v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, cb, cb_data); |
| RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); |
| |
| v8::MaybeLocal<v8::Function> maybe_function = |
| v8::Function::New(env->context(), Invoke, cbdata); |
| CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); |
| |
| *result = maybe_function.ToLocalChecked(); |
| return napi_clear_last_error(env); |
| } |
| |
| static inline napi_status NewTemplate( |
| napi_env env, |
| napi_callback cb, |
| void* cb_data, |
| v8::Local<v8::FunctionTemplate>* result, |
| v8::Local<v8::Signature> sig = v8::Local<v8::Signature>()) { |
| v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, cb, cb_data); |
| RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); |
| |
| *result = v8::FunctionTemplate::New(env->isolate, Invoke, cbdata, sig); |
| return napi_clear_last_error(env); |
| } |
| |
| napi_value GetNewTarget() { |
| if (cbinfo_.IsConstructCall()) { |
| return v8impl::JsValueFromV8LocalValue(cbinfo_.NewTarget()); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void Args(napi_value* buffer, size_t buffer_length) { |
| size_t i = 0; |
| size_t min_arg_count = std::min(buffer_length, ArgsLength()); |
| |
| for (; i < min_arg_count; ++i) { |
| buffer[i] = JsValueFromV8LocalValue(cbinfo_[i]); |
| } |
| |
| if (i < buffer_length) { |
| napi_value undefined = |
| JsValueFromV8LocalValue(v8::Undefined(cbinfo_.GetIsolate())); |
| for (; i < buffer_length; ++i) { |
| buffer[i] = undefined; |
| } |
| } |
| } |
| |
| napi_value This() { return JsValueFromV8LocalValue(cbinfo_.This()); } |
| |
| size_t ArgsLength() { return static_cast<size_t>(cbinfo_.Length()); } |
| |
| void* Data() { return bundle_->cb_data; } |
| |
| private: |
| explicit FunctionCallbackWrapper( |
| const v8::FunctionCallbackInfo<v8::Value>& cbinfo) |
| : cbinfo_(cbinfo), |
| bundle_(CallbackBundle::FromCallbackData(cbinfo.Data())) {} |
| |
| void InvokeCallback() { |
| napi_callback_info cbinfo_wrapper = |
| reinterpret_cast<napi_callback_info>(this); |
| |
| // All other pointers we need are stored in `_bundle` |
| napi_env env = bundle_->env; |
| napi_callback cb = bundle_->cb; |
| |
| napi_value result = nullptr; |
| bool exceptionOccurred = false; |
| env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); }, |
| [&](napi_env env, v8::Local<v8::Value> value) { |
| exceptionOccurred = true; |
| if (env->terminatedOrTerminating()) { |
| return; |
| } |
| env->isolate->ThrowException(value); |
| }); |
| |
| if (!exceptionOccurred && (result != nullptr)) { |
| cbinfo_.GetReturnValue().Set(V8LocalValueFromJsValue(result)); |
| } |
| } |
| |
| private: |
| const v8::FunctionCallbackInfo<v8::Value>& cbinfo_; |
| CallbackBundle* bundle_; |
| }; |
| |
| 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::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object); |
| RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); |
| v8::Local<v8::Object> obj = value.As<v8::Object>(); |
| |
| // 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); |
| |
| 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::ReferenceWithFinalizer::New( |
| env, |
| obj, |
| 0, |
| v8impl::ReferenceOwnership::kUserland, |
| finalize_cb, |
| native_object, |
| finalize_hint); |
| *result = reinterpret_cast<napi_ref>(reference); |
| } else if (finalize_cb != nullptr) { |
| // Create a self-deleting reference. |
| reference = v8impl::ReferenceWithFinalizer::New( |
| env, |
| obj, |
| 0, |
| v8impl::ReferenceOwnership::kRuntime, |
| finalize_cb, |
| native_object, |
| finalize_hint); |
| } else { |
| // Create a self-deleting reference. |
| reference = v8impl::ReferenceWithData::New( |
| env, obj, 0, v8impl::ReferenceOwnership::kRuntime, native_object); |
| } |
| |
| CHECK(obj->SetPrivate(context, |
| NAPI_PRIVATE_KEY(context, wrapper), |
| v8::External::New(env->isolate, reference)) |
| .FromJust()); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| // In JavaScript, weak references can be created for object types (Object, |
| // Function, and external Object) and for local symbols that are created with |
| // the `Symbol` function call. Global symbols created with the `Symbol.for` |
| // method cannot be weak references because they are never collected. |
| // |
| // Currently, V8 has no API to detect if a symbol is local or global. |
| // Until we have a V8 API for it, we consider that all symbols can be weak. |
| // This matches the current Node-API behavior. |
| inline bool CanBeHeldWeakly(v8::Local<v8::Value> value) { |
| return value->IsObject() || value->IsSymbol(); |
| } |
| |
| } // end of anonymous namespace |
| |
| void Finalizer::ResetEnv() { |
| env_ = nullptr; |
| } |
| |
| void Finalizer::ResetFinalizer() { |
| finalize_callback_ = nullptr; |
| finalize_data_ = nullptr; |
| finalize_hint_ = nullptr; |
| } |
| |
| void Finalizer::CallFinalizer() { |
| napi_finalize finalize_callback = finalize_callback_; |
| void* finalize_data = finalize_data_; |
| void* finalize_hint = finalize_hint_; |
| ResetFinalizer(); |
| |
| if (finalize_callback == nullptr) return; |
| if (env_ == nullptr) { |
| // The environment is dead. Call the finalizer directly. |
| finalize_callback(nullptr, finalize_data, finalize_hint); |
| } else { |
| env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint); |
| } |
| } |
| |
| TrackedFinalizer::TrackedFinalizer(napi_env env, |
| napi_finalize finalize_callback, |
| void* finalize_data, |
| void* finalize_hint) |
| : RefTracker(), |
| finalizer_(env, finalize_callback, finalize_data, finalize_hint) {} |
| |
| TrackedFinalizer* TrackedFinalizer::New(napi_env env, |
| napi_finalize finalize_callback, |
| void* finalize_data, |
| void* finalize_hint) { |
| TrackedFinalizer* finalizer = new TrackedFinalizer( |
| env, finalize_callback, finalize_data, finalize_hint); |
| finalizer->Link(&env->finalizing_reflist); |
| return finalizer; |
| } |
| |
| // When a TrackedFinalizer is being deleted, it may have been queued to call its |
| // finalizer. |
| TrackedFinalizer::~TrackedFinalizer() { |
| // Remove from the env's tracked list. |
| Unlink(); |
| // Try to remove the finalizer from the scheduled second pass callback. |
| finalizer_.env()->DequeueFinalizer(this); |
| } |
| |
| void TrackedFinalizer::Finalize() { |
| Unlink(); |
| finalizer_.CallFinalizer(); |
| delete this; |
| } |
| |
| Reference::Reference(napi_env env, |
| v8::Local<v8::Value> value, |
| uint32_t initial_refcount, |
| ReferenceOwnership ownership) |
| : RefTracker(), |
| persistent_(env->isolate, value), |
| refcount_(initial_refcount), |
| ownership_(ownership), |
| can_be_weak_(CanBeHeldWeakly(value)) { |
| if (refcount_ == 0) { |
| SetWeak(); |
| } |
| } |
| |
| Reference::~Reference() { |
| // Reset the handle. And no weak callback will be invoked. |
| persistent_.Reset(); |
| |
| // Remove from the env's tracked list. |
| Unlink(); |
| } |
| |
| Reference* Reference::New(napi_env env, |
| v8::Local<v8::Value> value, |
| uint32_t initial_refcount, |
| ReferenceOwnership ownership) { |
| Reference* reference = new Reference(env, value, initial_refcount, ownership); |
| reference->Link(&env->reflist); |
| return reference; |
| } |
| |
| uint32_t Reference::Ref() { |
| // When the persistent_ is cleared in the WeakCallback, and a second pass |
| // callback is pending, return 0 unconditionally. |
| if (persistent_.IsEmpty()) { |
| return 0; |
| } |
| if (++refcount_ == 1 && can_be_weak_) { |
| persistent_.ClearWeak(); |
| } |
| return refcount_; |
| } |
| |
| uint32_t Reference::Unref() { |
| // When the persistent_ is cleared in the WeakCallback, and a second pass |
| // callback is pending, return 0 unconditionally. |
| if (persistent_.IsEmpty() || refcount_ == 0) { |
| return 0; |
| } |
| if (--refcount_ == 0) { |
| SetWeak(); |
| } |
| return refcount_; |
| } |
| |
| v8::Local<v8::Value> Reference::Get(napi_env env) { |
| if (persistent_.IsEmpty()) { |
| return v8::Local<v8::Value>(); |
| } else { |
| return v8::Local<v8::Value>::New(env->isolate, persistent_); |
| } |
| } |
| |
| void Reference::Finalize() { |
| // Unconditionally reset the persistent handle so that no weak callback will |
| // be invoked again. |
| persistent_.Reset(); |
| |
| // If the Reference is not ReferenceOwnership::kRuntime, userland code should |
| // delete it. Delete it if it is ReferenceOwnership::kRuntime. |
| bool deleteMe = ownership_ == ReferenceOwnership::kRuntime; |
| |
| // Whether the Reference is going to be deleted in the finalize_callback |
| // or not, it should be removed from the tracked list. |
| Unlink(); |
| |
| // If the finalize_callback is present, it should either delete the |
| // derived Reference, or the Reference ownership was set to |
| // ReferenceOwnership::kRuntime and the deleteMe parameter is true. |
| CallUserFinalizer(); |
| |
| if (deleteMe) { |
| delete this; |
| } |
| } |
| |
| // Call the Finalize immediately since there is no user finalizer to call. |
| void Reference::InvokeFinalizerFromGC() { |
| Finalize(); |
| } |
| |
| // Mark the reference as weak and eligible for collection by the GC. |
| void Reference::SetWeak() { |
| if (can_be_weak_) { |
| persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); |
| } else { |
| persistent_.Reset(); |
| } |
| } |
| |
| // Static function called by GC. Delegate the call to the reference instance. |
| void Reference::WeakCallback(const v8::WeakCallbackInfo<Reference>& data) { |
| Reference* reference = data.GetParameter(); |
| // The reference must be reset during the weak callback per V8 API protocol. |
| reference->persistent_.Reset(); |
| reference->InvokeFinalizerFromGC(); |
| } |
| |
| ReferenceWithData* ReferenceWithData::New(napi_env env, |
| v8::Local<v8::Value> value, |
| uint32_t initial_refcount, |
| ReferenceOwnership ownership, |
| void* data) { |
| ReferenceWithData* reference = |
| new ReferenceWithData(env, value, initial_refcount, ownership, data); |
| reference->Link(&env->reflist); |
| return reference; |
| } |
| |
| ReferenceWithData::ReferenceWithData(napi_env env, |
| v8::Local<v8::Value> value, |
| uint32_t initial_refcount, |
| ReferenceOwnership ownership, |
| void* data) |
| : Reference(env, value, initial_refcount, ownership), data_(data) {} |
| |
| ReferenceWithFinalizer* ReferenceWithFinalizer::New( |
| napi_env env, |
| v8::Local<v8::Value> value, |
| uint32_t initial_refcount, |
| ReferenceOwnership ownership, |
| napi_finalize finalize_callback, |
| void* finalize_data, |
| void* finalize_hint) { |
| ReferenceWithFinalizer* reference = |
| new ReferenceWithFinalizer(env, |
| value, |
| initial_refcount, |
| ownership, |
| finalize_callback, |
| finalize_data, |
| finalize_hint); |
| reference->Link(&env->finalizing_reflist); |
| return reference; |
| } |
| |
| ReferenceWithFinalizer::ReferenceWithFinalizer(napi_env env, |
| v8::Local<v8::Value> value, |
| uint32_t initial_refcount, |
| ReferenceOwnership ownership, |
| napi_finalize finalize_callback, |
| void* finalize_data, |
| void* finalize_hint) |
| : Reference(env, value, initial_refcount, ownership), |
| finalizer_(env, finalize_callback, finalize_data, finalize_hint) {} |
| |
| ReferenceWithFinalizer::~ReferenceWithFinalizer() { |
| // Try to remove the finalizer from the scheduled second pass callback. |
| finalizer_.env()->DequeueFinalizer(this); |
| } |
| |
| void ReferenceWithFinalizer::CallUserFinalizer() { |
| finalizer_.CallFinalizer(); |
| } |
| |
| // The Node-API finalizer callback may make calls into the engine. V8's heap is |
| // not in a consistent state during the weak callback, and therefore it does |
| // not support calls back into it. Enqueue the invocation of the finalizer. |
| void ReferenceWithFinalizer::InvokeFinalizerFromGC() { |
| finalizer_.env()->InvokeFinalizerFromGC(this); |
| } |
| |
| /** |
| * A wrapper for `v8::External` to support type-tagging. `v8::External` doesn't |
| * support defining any properties and private properties on it, even though it |
| * is an object. This wrapper is used to store the type tag and the data of the |
| * external value. |
| */ |
| class ExternalWrapper { |
| private: |
| explicit ExternalWrapper(void* data) : data_(data), type_tag_{0, 0} {} |
| |
| static void WeakCallback(const v8::WeakCallbackInfo<ExternalWrapper>& data) { |
| ExternalWrapper* wrapper = data.GetParameter(); |
| delete wrapper; |
| } |
| |
| public: |
| static v8::Local<v8::External> New(napi_env env, void* data) { |
| ExternalWrapper* wrapper = new ExternalWrapper(data); |
| v8::Local<v8::External> external = v8::External::New(env->isolate, wrapper); |
| wrapper->persistent_.Reset(env->isolate, external); |
| wrapper->persistent_.SetWeak( |
| wrapper, WeakCallback, v8::WeakCallbackType::kParameter); |
| |
| return external; |
| } |
| |
| static ExternalWrapper* From(v8::Local<v8::External> external) { |
| return static_cast<ExternalWrapper*>(external->Value()); |
| } |
| |
| void* Data() { return data_; } |
| |
| bool TypeTag(const napi_type_tag* type_tag) { |
| if (has_tag_) { |
| return false; |
| } |
| type_tag_ = *type_tag; |
| has_tag_ = true; |
| return true; |
| } |
| |
| bool CheckTypeTag(const napi_type_tag* type_tag) { |
| return has_tag_ && type_tag->lower == type_tag_.lower && |
| type_tag->upper == type_tag_.upper; |
| } |
| |
| private: |
| v8impl::Persistent<v8::Value> persistent_; |
| void* data_; |
| napi_type_tag type_tag_; |
| bool has_tag_ = false; |
| }; |
| |
| } // end of namespace v8impl |
| |
| // 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", |
| "A date was expected", |
| "An arraybuffer was expected", |
| "A detachable arraybuffer was expected", |
| "Main thread would deadlock", |
| "External buffers are not allowed", |
| "Cannot run JavaScript", |
| }; |
| |
| napi_status NAPI_CDECL napi_get_last_error_info( |
| node_api_basic_env basic_env, const napi_extended_error_info** result) { |
| napi_env env = const_cast<napi_env>(basic_env); |
| CHECK_ENV(env); |
| CHECK_ARG(env, result); |
| |
| // The value of the constant below must be updated 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. |
| const int last_status = napi_cannot_run_js; |
| |
| static_assert(NAPI_ARRAYSIZE(error_messages) == last_status + 1, |
| "Count of error messages must match count of error values"); |
| CHECK_LE(env->last_error.error_code, last_status); |
| // 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]; |
| |
| if (env->last_error.error_code == napi_ok) { |
| napi_clear_last_error(env); |
| } |
| *result = &(env->last_error); |
| return napi_ok; |
| } |
| |
| napi_status NAPI_CDECL 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::Local<v8::Function> return_value; |
| v8::EscapableHandleScope scope(env->isolate); |
| v8::Local<v8::Function> fn; |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction( |
| env, cb, callback_data, &fn)); |
| return_value = scope.Escape(fn); |
| |
| if (utf8name != nullptr) { |
| v8::Local<v8::String> 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_CDECL |
| 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); |
| |
| if (property_count > 0) { |
| CHECK_ARG(env, properties); |
| } |
| |
| v8::Isolate* isolate = env->isolate; |
| |
| v8::EscapableHandleScope scope(isolate); |
| v8::Local<v8::FunctionTemplate> tpl; |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( |
| env, constructor, callback_data, &tpl)); |
| |
| v8::Local<v8::String> 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<v8::Name> property_name; |
| STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name)); |
| |
| 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, |
| // and preferred PropertyAttribute for lack of PropertyDescriptor |
| // support on ObjectTemplate. |
| if (p->getter != nullptr || p->setter != nullptr) { |
| v8::Local<v8::FunctionTemplate> getter_tpl; |
| v8::Local<v8::FunctionTemplate> setter_tpl; |
| if (p->getter != nullptr) { |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( |
| env, p->getter, p->data, &getter_tpl)); |
| } |
| if (p->setter != nullptr) { |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( |
| env, p->setter, p->data, &setter_tpl)); |
| } |
| |
| tpl->PrototypeTemplate()->SetAccessorProperty( |
| property_name, getter_tpl, setter_tpl, attributes); |
| } else if (p->method != nullptr) { |
| v8::Local<v8::FunctionTemplate> t; |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( |
| env, p->method, p->data, &t, v8::Signature::New(isolate, tpl))); |
| |
| tpl->PrototypeTemplate()->Set(property_name, t, attributes); |
| } else { |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value); |
| tpl->PrototypeTemplate()->Set(property_name, value, attributes); |
| } |
| } |
| |
| v8::Local<v8::Context> context = env->context(); |
| *result = v8impl::JsValueFromV8LocalValue( |
| scope.Escape(tpl->GetFunction(context).ToLocalChecked())); |
| |
| if (static_property_count > 0) { |
| std::vector<napi_property_descriptor> 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); |
| } |
| } |
| |
| STATUS_CALL(napi_define_properties( |
| env, *result, static_descriptors.size(), static_descriptors.data())); |
| } |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_property_names(napi_env env, |
| napi_value object, |
| napi_value* result) { |
| return napi_get_all_property_names( |
| env, |
| object, |
| napi_key_include_prototypes, |
| static_cast<napi_key_filter>(napi_key_enumerable | napi_key_skip_symbols), |
| napi_key_numbers_to_strings, |
| result); |
| } |
| |
| napi_status NAPI_CDECL |
| napi_get_all_property_names(napi_env env, |
| napi_value object, |
| napi_key_collection_mode key_mode, |
| napi_key_filter key_filter, |
| napi_key_conversion key_conversion, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES; |
| if (key_filter & napi_key_writable) { |
| filter = static_cast<v8::PropertyFilter>(filter | |
| v8::PropertyFilter::ONLY_WRITABLE); |
| } |
| if (key_filter & napi_key_enumerable) { |
| filter = static_cast<v8::PropertyFilter>( |
| filter | v8::PropertyFilter::ONLY_ENUMERABLE); |
| } |
| if (key_filter & napi_key_configurable) { |
| filter = static_cast<v8::PropertyFilter>( |
| filter | v8::PropertyFilter::ONLY_CONFIGURABLE); |
| } |
| if (key_filter & napi_key_skip_strings) { |
| filter = static_cast<v8::PropertyFilter>(filter | |
| v8::PropertyFilter::SKIP_STRINGS); |
| } |
| if (key_filter & napi_key_skip_symbols) { |
| filter = static_cast<v8::PropertyFilter>(filter | |
| v8::PropertyFilter::SKIP_SYMBOLS); |
| } |
| v8::KeyCollectionMode collection_mode; |
| v8::KeyConversionMode conversion_mode; |
| |
| switch (key_mode) { |
| case napi_key_include_prototypes: |
| collection_mode = v8::KeyCollectionMode::kIncludePrototypes; |
| break; |
| case napi_key_own_only: |
| collection_mode = v8::KeyCollectionMode::kOwnOnly; |
| break; |
| default: |
| return napi_set_last_error(env, napi_invalid_arg); |
| } |
| |
| switch (key_conversion) { |
| case napi_key_keep_numbers: |
| conversion_mode = v8::KeyConversionMode::kKeepNumbers; |
| break; |
| case napi_key_numbers_to_strings: |
| conversion_mode = v8::KeyConversionMode::kConvertToString; |
| break; |
| default: |
| return napi_set_last_error(env, napi_invalid_arg); |
| } |
| |
| v8::MaybeLocal<v8::Array> maybe_all_propertynames = |
| obj->GetPropertyNames(context, |
| collection_mode, |
| filter, |
| v8::IndexFilter::kIncludeIndices, |
| conversion_mode); |
| |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE( |
| env, maybe_all_propertynames, napi_generic_failure); |
| |
| *result = |
| v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked()); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL 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::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key); |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| v8::Maybe<bool> set_maybe = obj->Set(context, k, val); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, set_maybe.FromMaybe(false), napi_generic_failure); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL 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::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key); |
| v8::Maybe<bool> has_maybe = obj->Has(context, k); |
| |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, has_maybe, napi_generic_failure); |
| |
| *result = has_maybe.FromMaybe(false); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL 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::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| auto get_maybe = obj->Get(context, k); |
| |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, get_maybe, napi_generic_failure); |
| |
| v8::Local<v8::Value> val = get_maybe.ToLocalChecked(); |
| *result = v8impl::JsValueFromV8LocalValue(val); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_delete_property(napi_env env, |
| napi_value object, |
| napi_value key, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, key); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| v8::Maybe<bool> delete_maybe = obj->Delete(context, k); |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, delete_maybe, napi_generic_failure); |
| |
| if (result != nullptr) *result = delete_maybe.FromMaybe(false); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_has_own_property(napi_env env, |
| napi_value object, |
| napi_value key, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, key); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key); |
| RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected); |
| v8::Maybe<bool> has_maybe = obj->HasOwnProperty(context, k.As<v8::Name>()); |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, has_maybe, napi_generic_failure); |
| *result = has_maybe.FromMaybe(false); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_set_named_property(napi_env env, |
| napi_value object, |
| const char* utf8name, |
| napi_value value) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, value); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Local<v8::Name> key; |
| CHECK_NEW_FROM_UTF8(env, key, utf8name); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| v8::Maybe<bool> set_maybe = obj->Set(context, key, val); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, set_maybe.FromMaybe(false), napi_generic_failure); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_has_named_property(napi_env env, |
| napi_value object, |
| const char* utf8name, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Local<v8::Name> key; |
| CHECK_NEW_FROM_UTF8(env, key, utf8name); |
| |
| v8::Maybe<bool> has_maybe = obj->Has(context, key); |
| |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, has_maybe, napi_generic_failure); |
| |
| *result = has_maybe.FromMaybe(false); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_named_property(napi_env env, |
| napi_value object, |
| const char* utf8name, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Name> key; |
| CHECK_NEW_FROM_UTF8(env, key, utf8name); |
| |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| auto get_maybe = obj->Get(context, key); |
| |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, get_maybe, napi_generic_failure); |
| |
| v8::Local<v8::Value> val = get_maybe.ToLocalChecked(); |
| *result = v8impl::JsValueFromV8LocalValue(val); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_set_element(napi_env env, |
| napi_value object, |
| uint32_t index, |
| napi_value value) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, value); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| auto set_maybe = obj->Set(context, index, val); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, set_maybe.FromMaybe(false), napi_generic_failure); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_has_element(napi_env env, |
| napi_value object, |
| uint32_t index, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Maybe<bool> has_maybe = obj->Has(context, index); |
| |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, has_maybe, napi_generic_failure); |
| |
| *result = has_maybe.FromMaybe(false); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_element(napi_env env, |
| napi_value object, |
| uint32_t index, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| auto get_maybe = obj->Get(context, index); |
| |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, get_maybe, napi_generic_failure); |
| |
| *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_delete_element(napi_env env, |
| napi_value object, |
| uint32_t index, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| v8::Maybe<bool> delete_maybe = obj->Delete(context, index); |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, delete_maybe, napi_generic_failure); |
| |
| if (result != nullptr) *result = delete_maybe.FromMaybe(false); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL |
| 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::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Object> 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<v8::Name> property_name; |
| STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name)); |
| |
| if (p->getter != nullptr || p->setter != nullptr) { |
| v8::Local<v8::Function> local_getter; |
| v8::Local<v8::Function> local_setter; |
| |
| if (p->getter != nullptr) { |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction( |
| env, p->getter, p->data, &local_getter)); |
| } |
| if (p->setter != nullptr) { |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction( |
| env, p->setter, p->data, &local_setter)); |
| } |
| |
| v8::PropertyDescriptor descriptor(local_getter, local_setter); |
| descriptor.set_enumerable((p->attributes & napi_enumerable) != 0); |
| descriptor.set_configurable((p->attributes & napi_configurable) != 0); |
| |
| auto define_maybe = |
| obj->DefineProperty(context, property_name, descriptor); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, define_maybe.FromMaybe(false), napi_invalid_arg); |
| } else if (p->method != nullptr) { |
| v8::Local<v8::Function> method; |
| STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction( |
| env, p->method, p->data, &method)); |
| v8::PropertyDescriptor descriptor(method, |
| (p->attributes & napi_writable) != 0); |
| descriptor.set_enumerable((p->attributes & napi_enumerable) != 0); |
| descriptor.set_configurable((p->attributes & napi_configurable) != 0); |
| |
| auto define_maybe = |
| obj->DefineProperty(context, property_name, descriptor); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, define_maybe.FromMaybe(false), napi_generic_failure); |
| } else { |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value); |
| v8::Maybe<bool> define_maybe = v8::Just(false); |
| |
| if ((p->attributes & napi_enumerable) && |
| (p->attributes & napi_writable) && |
| (p->attributes & napi_configurable)) { |
| // Use a fast path for this type of data property. |
| define_maybe = obj->CreateDataProperty(context, property_name, value); |
| } else { |
| v8::PropertyDescriptor descriptor(value, |
| (p->attributes & napi_writable) != 0); |
| descriptor.set_enumerable((p->attributes & napi_enumerable) != 0); |
| descriptor.set_configurable((p->attributes & napi_configurable) != 0); |
| |
| define_maybe = obj->DefineProperty(context, property_name, descriptor); |
| } |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, define_maybe.FromMaybe(false), napi_invalid_arg); |
| } |
| } |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_object_freeze(napi_env env, napi_value object) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Maybe<bool> set_frozen = |
| obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, set_frozen.FromMaybe(false), napi_generic_failure); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_object_seal(napi_env env, napi_value object) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> obj; |
| |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| v8::Maybe<bool> set_sealed = |
| obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed); |
| |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, set_sealed.FromMaybe(false), napi_generic_failure); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_is_array(napi_env env, |
| napi_value value, |
| bool* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| *result = val->IsArray(); |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected); |
| |
| v8::Local<v8::Array> arr = val.As<v8::Array>(); |
| *result = arr->Length(); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL 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<v8::Value> a = v8impl::V8LocalValueFromJsValue(lhs); |
| v8::Local<v8::Value> b = v8impl::V8LocalValueFromJsValue(rhs); |
| |
| *result = a->StrictEquals(b); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_prototype(napi_env env, |
| napi_value object, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Object> obj; |
| CHECK_TO_OBJECT(env, context, obj, object); |
| |
| // This doesn't invokes Proxy's [[GetPrototypeOf]] handler. |
| v8::Local<v8::Value> val = obj->GetPrototypeV2(); |
| *result = v8impl::JsValueFromV8LocalValue(val); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_object(napi_env env, napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue(v8::Object::New(env->isolate)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL |
| napi_create_object_with_properties(napi_env env, |
| napi_value prototype_or_null, |
| napi_value* property_names, |
| napi_value* property_values, |
| size_t property_count, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| if (property_count > 0) { |
| CHECK_ARG(env, property_names); |
| CHECK_ARG(env, property_values); |
| } |
| |
| v8::Local<v8::Value> v8_prototype_or_null; |
| if (prototype_or_null == nullptr) { |
| v8_prototype_or_null = v8::Null(env->isolate); |
| } else { |
| v8_prototype_or_null = v8impl::V8LocalValueFromJsValue(prototype_or_null); |
| } |
| |
| v8::LocalVector<v8::Name> v8_names(env->isolate, property_count); |
| v8::LocalVector<v8::Value> v8_values(env->isolate, property_count); |
| |
| for (size_t i = 0; i < property_count; i++) { |
| v8::Local<v8::Value> name_value = |
| v8impl::V8LocalValueFromJsValue(property_names[i]); |
| RETURN_STATUS_IF_FALSE(env, name_value->IsName(), napi_name_expected); |
| v8_names[i] = name_value.As<v8::Name>(); |
| v8_values[i] = v8impl::V8LocalValueFromJsValue(property_values[i]); |
| } |
| |
| v8::Local<v8::Object> obj = v8::Object::New(env->isolate, |
| v8_prototype_or_null, |
| v8_names.data(), |
| v8_values.data(), |
| property_count); |
| |
| RETURN_STATUS_IF_FALSE(env, !obj.IsEmpty(), napi_generic_failure); |
| *result = v8impl::JsValueFromV8LocalValue(obj); |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_array_with_length(napi_env env, |
| size_t length, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = |
| v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate, length)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_string_latin1(napi_env env, |
| const char* str, |
| size_t length, |
| napi_value* result) { |
| return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { |
| return v8::String::NewFromOneByte(isolate, |
| reinterpret_cast<const uint8_t*>(str), |
| v8::NewStringType::kNormal, |
| length); |
| }); |
| } |
| |
| napi_status NAPI_CDECL napi_create_string_utf8(napi_env env, |
| const char* str, |
| size_t length, |
| napi_value* result) { |
| return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { |
| return v8::String::NewFromUtf8( |
| isolate, str, v8::NewStringType::kNormal, static_cast<int>(length)); |
| }); |
| } |
| |
| napi_status NAPI_CDECL napi_create_string_utf16(napi_env env, |
| const char16_t* str, |
| size_t length, |
| napi_value* result) { |
| return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { |
| return v8::String::NewFromTwoByte(isolate, |
| reinterpret_cast<const uint16_t*>(str), |
| v8::NewStringType::kNormal, |
| length); |
| }); |
| } |
| |
| napi_status NAPI_CDECL node_api_create_external_string_latin1( |
| napi_env env, |
| char* str, |
| size_t length, |
| node_api_basic_finalize basic_finalize_callback, |
| void* finalize_hint, |
| napi_value* result, |
| bool* copied) { |
| napi_finalize finalize_callback = |
| reinterpret_cast<napi_finalize>(basic_finalize_callback); |
| return v8impl::NewExternalString( |
| env, |
| str, |
| length, |
| finalize_callback, |
| finalize_hint, |
| result, |
| copied, |
| napi_create_string_latin1, |
| [&](v8::Isolate* isolate) { |
| if (length == NAPI_AUTO_LENGTH) { |
| length = (std::string_view(str)).length(); |
| } |
| auto resource = new v8impl::ExternalOneByteStringResource( |
| env, str, length, finalize_callback, finalize_hint); |
| return v8::String::NewExternalOneByte(isolate, resource); |
| }); |
| } |
| |
| napi_status NAPI_CDECL node_api_create_external_string_utf16( |
| napi_env env, |
| char16_t* str, |
| size_t length, |
| node_api_basic_finalize basic_finalize_callback, |
| void* finalize_hint, |
| napi_value* result, |
| bool* copied) { |
| napi_finalize finalize_callback = |
| reinterpret_cast<napi_finalize>(basic_finalize_callback); |
| return v8impl::NewExternalString( |
| env, |
| str, |
| length, |
| finalize_callback, |
| finalize_hint, |
| result, |
| copied, |
| napi_create_string_utf16, |
| [&](v8::Isolate* isolate) { |
| if (length == NAPI_AUTO_LENGTH) { |
| length = (std::u16string_view(str)).length(); |
| } |
| auto resource = new v8impl::ExternalStringResource( |
| env, str, length, finalize_callback, finalize_hint); |
| return v8::String::NewExternalTwoByte(isolate, resource); |
| }); |
| } |
| |
| napi_status node_api_create_property_key_latin1(napi_env env, |
| const char* str, |
| size_t length, |
| napi_value* result) { |
| return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { |
| return v8::String::NewFromOneByte(isolate, |
| reinterpret_cast<const uint8_t*>(str), |
| v8::NewStringType::kInternalized, |
| length); |
| }); |
| } |
| |
| napi_status node_api_create_property_key_utf8(napi_env env, |
| const char* str, |
| size_t length, |
| napi_value* result) { |
| return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { |
| return v8::String::NewFromUtf8(isolate, |
| str, |
| v8::NewStringType::kInternalized, |
| static_cast<int>(length)); |
| }); |
| } |
| |
| napi_status NAPI_CDECL node_api_create_property_key_utf16(napi_env env, |
| const char16_t* str, |
| size_t length, |
| napi_value* result) { |
| return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { |
| return v8::String::NewFromTwoByte(isolate, |
| reinterpret_cast<const uint16_t*>(str), |
| v8::NewStringType::kInternalized, |
| static_cast<int>(length)); |
| }); |
| } |
| |
| napi_status NAPI_CDECL napi_create_double(napi_env env, |
| double value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = |
| v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, value)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_int32(napi_env env, |
| int32_t value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = |
| v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_uint32(napi_env env, |
| uint32_t value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue( |
| v8::Integer::NewFromUnsigned(env->isolate, value)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_int64(napi_env env, |
| int64_t value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue( |
| v8::Number::New(env->isolate, static_cast<double>(value))); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env, |
| int64_t value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = |
| v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_bigint_uint64(napi_env env, |
| uint64_t value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue( |
| v8::BigInt::NewFromUnsigned(env->isolate, value)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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<v8::Context> context = env->context(); |
| |
| RETURN_STATUS_IF_FALSE(env, word_count <= INT_MAX, napi_invalid_arg); |
| |
| v8::MaybeLocal<v8::BigInt> b = |
| v8::BigInt::NewFromWords(context, sign_bit, word_count, words); |
| |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, b, napi_generic_failure); |
| |
| *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_boolean(napi_env env, |
| bool value, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(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_CDECL napi_create_symbol(napi_env env, |
| napi_value description, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| v8::Isolate* isolate = env->isolate; |
| |
| if (description == nullptr) { |
| *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); |
| } else { |
| v8::Local<v8::Value> desc = v8impl::V8LocalValueFromJsValue(description); |
| RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected); |
| |
| *result = v8impl::JsValueFromV8LocalValue( |
| v8::Symbol::New(isolate, desc.As<v8::String>())); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL node_api_symbol_for(napi_env env, |
| const char* utf8description, |
| size_t length, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| napi_value js_description_string; |
| STATUS_CALL(napi_create_string_utf8( |
| env, utf8description, length, &js_description_string)); |
| v8::Local<v8::String> description_string = |
| v8impl::V8LocalValueFromJsValue(js_description_string).As<v8::String>(); |
| |
| *result = v8impl::JsValueFromV8LocalValue( |
| v8::Symbol::For(env->isolate, description_string)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| static inline napi_status set_error_code(napi_env env, |
| v8::Local<v8::Value> error, |
| napi_value code, |
| const char* code_cstring) { |
| if ((code != nullptr) || (code_cstring != nullptr)) { |
| v8::Local<v8::Context> context = env->context(); |
| v8::Local<v8::Object> err_object = error.As<v8::Object>(); |
| |
| v8::Local<v8::Value> 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<v8::Name> code_key; |
| CHECK_NEW_FROM_UTF8(env, code_key, "code"); |
| |
| v8::Maybe<bool> set_maybe = err_object->Set(context, code_key, code_value); |
| RETURN_STATUS_IF_FALSE( |
| env, set_maybe.FromMaybe(false), napi_generic_failure); |
| } |
| return napi_ok; |
| } |
| |
| napi_status NAPI_CDECL napi_create_error(napi_env env, |
| napi_value code, |
| napi_value msg, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, msg); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg); |
| RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); |
| |
| v8::Local<v8::Value> error_obj = |
| v8::Exception::Error(message_value.As<v8::String>()); |
| STATUS_CALL(set_error_code(env, error_obj, code, nullptr)); |
| |
| *result = v8impl::JsValueFromV8LocalValue(error_obj); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_type_error(napi_env env, |
| napi_value code, |
| napi_value msg, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, msg); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg); |
| RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); |
| |
| v8::Local<v8::Value> error_obj = |
| v8::Exception::TypeError(message_value.As<v8::String>()); |
| STATUS_CALL(set_error_code(env, error_obj, code, nullptr)); |
| |
| *result = v8impl::JsValueFromV8LocalValue(error_obj); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_range_error(napi_env env, |
| napi_value code, |
| napi_value msg, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, msg); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg); |
| RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); |
| |
| v8::Local<v8::Value> error_obj = |
| v8::Exception::RangeError(message_value.As<v8::String>()); |
| STATUS_CALL(set_error_code(env, error_obj, code, nullptr)); |
| |
| *result = v8impl::JsValueFromV8LocalValue(error_obj); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL node_api_create_syntax_error(napi_env env, |
| napi_value code, |
| napi_value msg, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, msg); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg); |
| RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); |
| |
| v8::Local<v8::Value> error_obj = |
| v8::Exception::SyntaxError(message_value.As<v8::String>()); |
| STATUS_CALL(set_error_code(env, error_obj, code, nullptr)); |
| |
| *result = v8impl::JsValueFromV8LocalValue(error_obj); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> 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_CDECL napi_get_undefined(napi_env env, napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue(v8::Undefined(env->isolate)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_null(napi_env env, napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(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_CDECL 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::FunctionCallbackWrapper* info = |
| reinterpret_cast<v8impl::FunctionCallbackWrapper*>(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_CDECL napi_get_new_target(napi_env env, |
| napi_callback_info cbinfo, |
| napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, cbinfo); |
| CHECK_ARG(env, result); |
| |
| v8impl::FunctionCallbackWrapper* info = |
| reinterpret_cast<v8impl::FunctionCallbackWrapper*>(cbinfo); |
| |
| *result = info->GetNewTarget(); |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Value> v8recv = v8impl::V8LocalValueFromJsValue(recv); |
| |
| v8::Local<v8::Function> v8func; |
| CHECK_TO_FUNCTION(env, v8func, func); |
| |
| auto maybe = v8func->Call( |
| context, |
| v8recv, |
| argc, |
| reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv))); |
| |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, napi_generic_failure); |
| if (result != nullptr) { |
| *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); |
| } |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_global(napi_env env, napi_value* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsValueFromV8LocalValue(env->context()->Global()); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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_CDECL napi_throw_error(napi_env env, |
| const char* code, |
| const char* msg) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::String> str; |
| CHECK_NEW_FROM_UTF8(env, str, msg); |
| |
| v8::Local<v8::Value> error_obj = v8::Exception::Error(str); |
| STATUS_CALL(set_error_code(env, error_obj, nullptr, code)); |
| |
| 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_CDECL napi_throw_type_error(napi_env env, |
| const char* code, |
| const char* msg) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::String> str; |
| CHECK_NEW_FROM_UTF8(env, str, msg); |
| |
| v8::Local<v8::Value> error_obj = v8::Exception::TypeError(str); |
| STATUS_CALL(set_error_code(env, error_obj, nullptr, code)); |
| |
| 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_CDECL napi_throw_range_error(napi_env env, |
| const char* code, |
| const char* msg) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::String> str; |
| CHECK_NEW_FROM_UTF8(env, str, msg); |
| |
| v8::Local<v8::Value> error_obj = v8::Exception::RangeError(str); |
| STATUS_CALL(set_error_code(env, error_obj, nullptr, code)); |
| |
| 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_CDECL node_api_throw_syntax_error(napi_env env, |
| const char* code, |
| const char* msg) { |
| NAPI_PREAMBLE(env); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::String> str; |
| CHECK_NEW_FROM_UTF8(env, str, msg); |
| |
| v8::Local<v8::Value> error_obj = v8::Exception::SyntaxError(str); |
| STATUS_CALL(set_error_code(env, error_obj, nullptr, code)); |
| |
| 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_CDECL 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_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| *result = val->IsNativeError(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); |
| |
| *result = val.As<v8::Number>()->Value(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL 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_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| if (val->IsInt32()) { |
| *result = val.As<v8::Int32>()->Value(); |
| } else { |
| RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); |
| |
| // Empty context: https://github.com/nodejs/node/issues/14379 |
| v8::Local<v8::Context> context; |
| *result = val->Int32Value(context).FromJust(); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_uint32(napi_env env, |
| napi_value value, |
| uint32_t* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| if (val->IsUint32()) { |
| *result = val.As<v8::Uint32>()->Value(); |
| } else { |
| RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); |
| |
| // Empty context: https://github.com/nodejs/node/issues/14379 |
| v8::Local<v8::Context> context; |
| *result = val->Uint32Value(context).FromJust(); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_int64(napi_env env, |
| napi_value value, |
| int64_t* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| // This is still a fast path very likely to be taken. |
| if (val->IsInt32()) { |
| *result = val.As<v8::Int32>()->Value(); |
| return napi_clear_last_error(env); |
| } |
| |
| RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); |
| |
| // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN, |
| // inconsistent with v8::Value::Int32Value() which converts those values to 0. |
| // Special-case all non-finite values to match that behavior. |
| double doubleValue = val.As<v8::Number>()->Value(); |
| if (std::isfinite(doubleValue)) { |
| // Empty context: https://github.com/nodejs/node/issues/14379 |
| v8::Local<v8::Context> context; |
| *result = val->IntegerValue(context).FromJust(); |
| } else { |
| *result = 0; |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env, |
| napi_value value, |
| int64_t* result, |
| bool* lossless) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| CHECK_ARG(env, lossless); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); |
| |
| *result = val.As<v8::BigInt>()->Int64Value(lossless); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_bigint_uint64(napi_env env, |
| napi_value value, |
| uint64_t* result, |
| bool* lossless) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| CHECK_ARG(env, lossless); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); |
| |
| *result = val.As<v8::BigInt>()->Uint64Value(lossless); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_bigint_words(napi_env env, |
| napi_value value, |
| int* sign_bit, |
| size_t* word_count, |
| uint64_t* words) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, word_count); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| |
| RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); |
| |
| v8::Local<v8::BigInt> big = val.As<v8::BigInt>(); |
| |
| int word_count_int = *word_count; |
| |
| if (sign_bit == nullptr && words == nullptr) { |
| word_count_int = big->WordCount(); |
| } else { |
| CHECK_ARG(env, sign_bit); |
| CHECK_ARG(env, words); |
| big->ToWordsArray(sign_bit, &word_count_int, words); |
| } |
| |
| *word_count = word_count_int; |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_bool(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_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), napi_boolean_expected); |
| |
| *result = val.As<v8::Boolean>()->Value(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Copies a JavaScript string into a LATIN-1 string buffer. The result is the |
| // number of bytes (excluding the null terminator) copied into buf. |
| // A sufficient buffer size should be greater than the length of string, |
| // reserving space for null terminator. |
| // If bufsize is insufficient, the string will be truncated and null terminated. |
| // If buf is NULL, this method returns the length of the string (in bytes) |
| // via the result parameter. |
| // The result argument is optional unless buf is NULL. |
| napi_status NAPI_CDECL napi_get_value_string_latin1( |
| napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); |
| v8::Local<v8::String> str = val.As<v8::String>(); |
| |
| if (!buf) { |
| CHECK_ARG(env, result); |
| *result = str->Length(); |
| } else if (bufsize != 0) { |
| uint32_t length = static_cast<uint32_t>( |
| std::min(bufsize - 1, static_cast<size_t>(str->Length()))); |
| str->WriteOneByteV2(env->isolate, |
| 0, |
| length, |
| reinterpret_cast<uint8_t*>(buf), |
| v8::String::WriteFlags::kNullTerminate); |
| if (result != nullptr) { |
| *result = length; |
| } |
| } else if (result != nullptr) { |
| *result = 0; |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Copies a JavaScript string into a UTF-8 string buffer. The result is the |
| // number of bytes (excluding the null terminator) copied into buf. |
| // A sufficient buffer size should be greater than the length of string, |
| // reserving space for null terminator. |
| // If bufsize is insufficient, the string will be truncated and null terminated. |
| // If buf is NULL, this method returns the length of the string (in bytes) |
| // via the result parameter. |
| // The result argument is optional unless buf is NULL. |
| napi_status NAPI_CDECL napi_get_value_string_utf8( |
| napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); |
| v8::Local<v8::String> str = val.As<v8::String>(); |
| |
| if (!buf) { |
| CHECK_ARG(env, result); |
| *result = str->Utf8LengthV2(env->isolate); |
| } else if (bufsize != 0) { |
| size_t copied = |
| str->WriteUtf8V2(env->isolate, |
| buf, |
| bufsize - 1, |
| v8::String::WriteFlags::kReplaceInvalidUtf8); |
| |
| buf[copied] = '\0'; |
| if (result != nullptr) { |
| *result = copied; |
| } |
| } else if (result != nullptr) { |
| *result = 0; |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Copies a JavaScript string into a UTF-16 string buffer. The result is the |
| // number of 2-byte code units (excluding the null terminator) copied into buf. |
| // A sufficient buffer size should be greater than the length of string, |
| // reserving space for null terminator. |
| // If bufsize is insufficient, the string will be truncated and null terminated. |
| // If buf is NULL, this method returns the length of the string (in 2-byte |
| // code units) via the result parameter. |
| // The result argument is optional unless buf is NULL. |
| napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env, |
| napi_value value, |
| char16_t* buf, |
| size_t bufsize, |
| size_t* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); |
| v8::Local<v8::String> str = val.As<v8::String>(); |
| |
| if (!buf) { |
| CHECK_ARG(env, result); |
| // V8 assumes UTF-16 length is the same as the number of characters. |
| *result = str->Length(); |
| } else if (bufsize != 0) { |
| uint32_t length = static_cast<uint32_t>( |
| std::min(bufsize - 1, static_cast<size_t>(str->Length()))); |
| str->WriteV2(env->isolate, |
| 0, |
| length, |
| reinterpret_cast<uint16_t*>(buf), |
| v8::String::WriteFlags::kNullTerminate); |
| |
| if (result != nullptr) { |
| *result = length; |
| } |
| } else if (result != nullptr) { |
| *result = 0; |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env, |
| napi_value value, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::Boolean> b = |
| v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate); |
| *result = v8impl::JsValueFromV8LocalValue(b); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| #define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \ |
| napi_status NAPI_CDECL napi_coerce_to_##LowerCaseName( \ |
| napi_env env, napi_value value, napi_value* result) { \ |
| NAPI_PREAMBLE(env); \ |
| CHECK_ARG(env, value); \ |
| CHECK_ARG(env, result); \ |
| \ |
| v8::Local<v8::Context> context = env->context(); \ |
| v8::Local<v8::MixedCaseName> str; \ |
| \ |
| CHECK_TO_##UpperCaseName(env, context, str, value); \ |
| \ |
| *result = v8impl::JsValueFromV8LocalValue(str); \ |
| return GET_RETURN_STATUS(env); \ |
| } |
| |
| GEN_COERCE_FUNCTION(NUMBER, Number, number) |
| GEN_COERCE_FUNCTION(OBJECT, Object, object) |
| GEN_COERCE_FUNCTION(STRING, String, string) |
| |
| #undef GEN_COERCE_FUNCTION |
| |
| napi_status NAPI_CDECL napi_wrap(napi_env env, |
| napi_value js_object, |
| void* native_object, |
| node_api_basic_finalize basic_finalize_cb, |
| void* finalize_hint, |
| napi_ref* result) { |
| napi_finalize finalize_cb = |
| reinterpret_cast<napi_finalize>(basic_finalize_cb); |
| return v8impl::Wrap( |
| env, js_object, native_object, finalize_cb, finalize_hint, result); |
| } |
| |
| napi_status NAPI_CDECL napi_unwrap(napi_env env, |
| napi_value obj, |
| void** result) { |
| return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap); |
| } |
| |
| napi_status NAPI_CDECL napi_remove_wrap(napi_env env, |
| napi_value obj, |
| void** result) { |
| return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap); |
| } |
| |
| napi_status NAPI_CDECL |
| napi_create_external(napi_env env, |
| void* data, |
| node_api_basic_finalize basic_finalize_cb, |
| void* finalize_hint, |
| napi_value* result) { |
| napi_finalize finalize_cb = |
| reinterpret_cast<napi_finalize>(basic_finalize_cb); |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::External> external_value = |
| v8impl::ExternalWrapper::New(env, data); |
| |
| if (finalize_cb) { |
| // The Reference object will delete itself after invoking the finalizer |
| // callback. |
| v8impl::ReferenceWithFinalizer::New(env, |
| external_value, |
| 0, |
| v8impl::ReferenceOwnership::kRuntime, |
| finalize_cb, |
| data, |
| finalize_hint); |
| } |
| |
| *result = v8impl::JsValueFromV8LocalValue(external_value); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_type_tag_object(napi_env env, |
| napi_value object_or_external, |
| const napi_type_tag* type_tag) { |
| NAPI_PREAMBLE(env); |
| v8::Local<v8::Context> context = env->context(); |
| |
| CHECK_ARG(env, object_or_external); |
| v8::Local<v8::Value> val = |
| v8impl::V8LocalValueFromJsValue(object_or_external); |
| if (val->IsExternal()) { |
| v8impl::ExternalWrapper* wrapper = |
| v8impl::ExternalWrapper::From(val.As<v8::External>()); |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, wrapper->TypeTag(type_tag), napi_invalid_arg); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| v8::Local<v8::Object> obj; |
| CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object_or_external); |
| CHECK_ARG_WITH_PREAMBLE(env, type_tag); |
| |
| auto key = NAPI_PRIVATE_KEY(context, type_tag); |
| auto maybe_has = obj->HasPrivate(context, key); |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_has, napi_generic_failure); |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, !maybe_has.FromJust(), napi_invalid_arg); |
| |
| auto tag = v8::BigInt::NewFromWords( |
| context, 0, 2, reinterpret_cast<const uint64_t*>(type_tag)); |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, tag, napi_generic_failure); |
| |
| auto maybe_set = obj->SetPrivate(context, key, tag.ToLocalChecked()); |
| CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_set, napi_generic_failure); |
| RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( |
| env, maybe_set.FromJust(), napi_generic_failure); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_check_object_type_tag(napi_env env, |
| napi_value object_or_external, |
| const napi_type_tag* type_tag, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| v8::Local<v8::Context> context = env->context(); |
| |
| CHECK_ARG(env, object_or_external); |
| v8::Local<v8::Value> obj_val = |
| v8impl::V8LocalValueFromJsValue(object_or_external); |
| if (obj_val->IsExternal()) { |
| v8impl::ExternalWrapper* wrapper = |
| v8impl::ExternalWrapper::From(obj_val.As<v8::External>()); |
| *result = wrapper->CheckTypeTag(type_tag); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| v8::Local<v8::Object> obj; |
| CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object_or_external); |
| CHECK_ARG_WITH_PREAMBLE(env, type_tag); |
| CHECK_ARG_WITH_PREAMBLE(env, result); |
| |
| auto maybe_value = |
| obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, type_tag)); |
| CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe_value, napi_generic_failure); |
| v8::Local<v8::Value> val = maybe_value.ToLocalChecked(); |
| |
| // We consider the type check to have failed unless we reach the line below |
| // where we set whether the type check succeeded or not based on the |
| // comparison of the two type tags. |
| *result = false; |
| if (val->IsBigInt()) { |
| int sign; |
| int size = 2; |
| napi_type_tag tag; |
| val.As<v8::BigInt>()->ToWordsArray( |
| &sign, &size, reinterpret_cast<uint64_t*>(&tag)); |
| if (sign == 0) { |
| if (size == 2) { |
| *result = |
| (tag.lower == type_tag->lower && tag.upper == type_tag->upper); |
| } else if (size == 1) { |
| *result = (tag.lower == type_tag->lower && 0 == type_tag->upper); |
| } else if (size == 0) { |
| *result = (0 == type_tag->lower && 0 == type_tag->upper); |
| } |
| } |
| } |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_value_external(napi_env env, |
| napi_value value, |
| void** result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); |
| |
| v8::Local<v8::External> external_value = val.As<v8::External>(); |
| *result = v8impl::ExternalWrapper::From(external_value)->Data(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. |
| napi_status NAPI_CDECL napi_create_reference(napi_env env, |
| napi_value value, |
| uint32_t initial_refcount, |
| napi_ref* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value); |
| if (env->module_api_version < 10) { |
| if (!(v8_value->IsObject() || v8_value->IsFunction() || |
| v8_value->IsSymbol())) { |
| return napi_set_last_error(env, napi_invalid_arg); |
| } |
| } |
| |
| v8impl::Reference* reference = v8impl::Reference::New( |
| env, v8_value, initial_refcount, v8impl::ReferenceOwnership::kUserland); |
| |
| *result = reinterpret_cast<napi_ref>(reference); |
| return napi_clear_last_error(env); |
| } |
| |
| // Deletes a reference. The referenced value is released, and may be GC'd unless |
| // there are other references to it. |
| // For a napi_reference returned from `napi_wrap`, this must be called in the |
| // finalizer. |
| napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env, |
| napi_ref ref) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV(env); |
| CHECK_ARG(env, ref); |
| |
| delete reinterpret_cast<v8impl::Reference*>(ref); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Increments the reference count, optionally returning the resulting count. |
| // After this call the reference will be a strong reference because its |
| // refcount is >0, and the referenced object is effectively "pinned". |
| // Calling this when the refcount is 0 and the object is unavailable |
| // results in an error. |
| napi_status NAPI_CDECL napi_reference_ref(napi_env env, |
| napi_ref ref, |
| uint32_t* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, ref); |
| |
| v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref); |
| uint32_t count = reference->Ref(); |
| |
| if (result != nullptr) { |
| *result = count; |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Decrements the reference count, optionally returning the resulting count. If |
| // the result is 0 the reference is now weak and the object may be GC'd at any |
| // time if there are no other references. Calling this when the refcount is |
| // already 0 results in an error. |
| napi_status NAPI_CDECL napi_reference_unref(napi_env env, |
| napi_ref ref, |
| uint32_t* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, ref); |
| |
| v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref); |
| |
| if (reference->refcount() == 0) { |
| return napi_set_last_error(env, napi_generic_failure); |
| } |
| |
| uint32_t count = reference->Unref(); |
| |
| if (result != nullptr) { |
| *result = count; |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| // Attempts to get a referenced value. If the reference is weak, the value might |
| // no longer be available, in that case the call is still successful but the |
| // result is NULL. |
| napi_status NAPI_CDECL napi_get_reference_value(napi_env env, |
| napi_ref ref, |
| napi_value* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, ref); |
| CHECK_ARG(env, result); |
| |
| v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref); |
| *result = v8impl::JsValueFromV8LocalValue(reference->Get(env)); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_open_handle_scope(napi_env env, |
| napi_handle_scope* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsHandleScopeFromV8HandleScope( |
| new v8impl::HandleScopeWrapper(env->isolate)); |
| env->open_handle_scopes++; |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_close_handle_scope(napi_env env, |
| napi_handle_scope scope) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, scope); |
| if (env->open_handle_scopes == 0) { |
| return napi_handle_scope_mismatch; |
| } |
| |
| env->open_handle_scopes--; |
| delete v8impl::V8HandleScopeFromJsHandleScope(scope); |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_open_escapable_handle_scope( |
| napi_env env, napi_escapable_handle_scope* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( |
| new v8impl::EscapableHandleScopeWrapper(env->isolate)); |
| env->open_handle_scopes++; |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_close_escapable_handle_scope( |
| napi_env env, napi_escapable_handle_scope scope) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, scope); |
| if (env->open_handle_scopes == 0) { |
| return napi_handle_scope_mismatch; |
| } |
| |
| delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); |
| env->open_handle_scopes--; |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_escape_handle(napi_env env, |
| napi_escapable_handle_scope scope, |
| napi_value escapee, |
| napi_value* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, scope); |
| CHECK_ARG(env, escapee); |
| CHECK_ARG(env, result); |
| |
| v8impl::EscapableHandleScopeWrapper* s = |
| v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); |
| if (!s->escape_called()) { |
| *result = v8impl::JsValueFromV8LocalValue( |
| s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); |
| return napi_clear_last_error(env); |
| } |
| return napi_set_last_error(env, napi_escape_called_twice); |
| } |
| |
| napi_status NAPI_CDECL napi_new_instance(napi_env env, |
| napi_value constructor, |
| size_t argc, |
| const napi_value* argv, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, constructor); |
| if (argc > 0) { |
| CHECK_ARG(env, argv); |
| } |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Context> context = env->context(); |
| |
| v8::Local<v8::Function> ctor; |
| CHECK_TO_FUNCTION(env, ctor, constructor); |
| |
| auto maybe = ctor->NewInstance( |
| context, |
| argc, |
| reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv))); |
| |
| CHECK_MAYBE_EMPTY(env, maybe, napi_pending_exception); |
| |
| *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_instanceof(napi_env env, |
| napi_value object, |
| napi_value constructor, |
| bool* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, object); |
| CHECK_ARG(env, result); |
| |
| *result = false; |
| |
| v8::Local<v8::Object> ctor; |
| v8::Local<v8::Context> context = env->context(); |
| |
| CHECK_TO_OBJECT(env, context, ctor, constructor); |
| |
| if (!ctor->IsFunction()) { |
| napi_throw_type_error( |
| env, "ERR_NAPI_CONS_FUNCTION", "Constructor must be a function"); |
| |
| return napi_set_last_error(env, napi_function_expected); |
| } |
| |
| napi_status status = napi_generic_failure; |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(object); |
| auto maybe_result = val->InstanceOf(context, ctor); |
| CHECK_MAYBE_NOTHING(env, maybe_result, status); |
| *result = maybe_result.FromJust(); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| // Methods to support catching exceptions |
| napi_status NAPI_CDECL napi_is_exception_pending(napi_env env, bool* result) { |
| // NAPI_PREAMBLE is not used here: this function must execute when there is a |
| // pending exception. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| *result = !env->last_exception.IsEmpty(); |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_and_clear_last_exception(napi_env env, |
| napi_value* result) { |
| // NAPI_PREAMBLE is not used here: this function must execute when there is a |
| // pending exception. |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, result); |
| |
| if (env->last_exception.IsEmpty()) { |
| return napi_get_undefined(env, result); |
| } else { |
| *result = v8impl::JsValueFromV8LocalValue( |
| v8::Local<v8::Value>::New(env->isolate, env->last_exception)); |
| env->last_exception.Reset(); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env, |
| napi_value value, |
| bool* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| *result = val->IsArrayBuffer(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env, |
| size_t byte_length, |
| void** data, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::ArrayBuffer> buffer = |
| v8::ArrayBuffer::New(isolate, byte_length); |
| |
| // Optionally return a pointer to the buffer's data, to avoid another call to |
| // retrieve it. |
| if (data != nullptr) { |
| *data = buffer->Data(); |
| } |
| |
| *result = v8impl::JsValueFromV8LocalValue(buffer); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL |
| napi_create_external_arraybuffer(napi_env env, |
| void* external_data, |
| size_t byte_length, |
| node_api_basic_finalize finalize_cb, |
| void* finalize_hint, |
| napi_value* result) { |
| // The API contract here is that the cleanup function runs on the JS thread, |
| // and is able to use napi_env. Implementing that properly is hard, so use the |
| // `Buffer` variant for easier implementation. |
| napi_value buffer; |
| STATUS_CALL(napi_create_external_buffer( |
| env, byte_length, external_data, finalize_cb, finalize_hint, &buffer)); |
| return napi_get_typedarray_info( |
| env, buffer, nullptr, nullptr, nullptr, result, nullptr); |
| } |
| |
| napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env, |
| napi_value arraybuffer, |
| void** data, |
| size_t* byte_length) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, arraybuffer); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer); |
| |
| if (value->IsArrayBuffer()) { |
| v8::Local<v8::ArrayBuffer> ab = value.As<v8::ArrayBuffer>(); |
| |
| if (data != nullptr) { |
| *data = ab->Data(); |
| } |
| |
| if (byte_length != nullptr) { |
| *byte_length = ab->ByteLength(); |
| } |
| } else if (value->IsSharedArrayBuffer()) { |
| v8::Local<v8::SharedArrayBuffer> sab = value.As<v8::SharedArrayBuffer>(); |
| |
| if (data != nullptr) { |
| *data = sab->Data(); |
| } |
| |
| if (byte_length != nullptr) { |
| *byte_length = sab->ByteLength(); |
| } |
| } else { |
| return napi_set_last_error(env, napi_invalid_arg); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL node_api_is_sharedarraybuffer(napi_env env, |
| napi_value value, |
| bool* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| *result = val->IsSharedArrayBuffer(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL node_api_create_sharedarraybuffer(napi_env env, |
| size_t byte_length, |
| void** data, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::Isolate* isolate = env->isolate; |
| v8::Local<v8::SharedArrayBuffer> buffer = |
| v8::SharedArrayBuffer::New(isolate, byte_length); |
| |
| // Optionally return a pointer to the buffer's data, to avoid another call to |
| // retrieve it. |
| if (data != nullptr) { |
| *data = buffer->Data(); |
| } |
| |
| *result = v8impl::JsValueFromV8LocalValue(buffer); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_is_typedarray(napi_env env, |
| napi_value value, |
| bool* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| *result = val->IsTypedArray(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_typedarray(napi_env env, |
| napi_typedarray_type type, |
| size_t length, |
| napi_value arraybuffer, |
| size_t byte_offset, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, arraybuffer); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer); |
| RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); |
| |
| v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>(); |
| v8::Local<v8::TypedArray> typedArray; |
| |
| switch (type) { |
| case napi_int8_array: |
| CREATE_TYPED_ARRAY( |
| env, Int8Array, 1, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_uint8_array: |
| CREATE_TYPED_ARRAY( |
| env, Uint8Array, 1, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_uint8_clamped_array: |
| CREATE_TYPED_ARRAY( |
| env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_int16_array: |
| CREATE_TYPED_ARRAY( |
| env, Int16Array, 2, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_uint16_array: |
| CREATE_TYPED_ARRAY( |
| env, Uint16Array, 2, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_int32_array: |
| CREATE_TYPED_ARRAY( |
| env, Int32Array, 4, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_uint32_array: |
| CREATE_TYPED_ARRAY( |
| env, Uint32Array, 4, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_float32_array: |
| CREATE_TYPED_ARRAY( |
| env, Float32Array, 4, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_float64_array: |
| CREATE_TYPED_ARRAY( |
| env, Float64Array, 8, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_bigint64_array: |
| CREATE_TYPED_ARRAY( |
| env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_biguint64_array: |
| CREATE_TYPED_ARRAY( |
| env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); |
| break; |
| case napi_float16_array: |
| CREATE_TYPED_ARRAY( |
| env, Float16Array, 2, buffer, byte_offset, length, typedArray); |
| break; |
| default: |
| return napi_set_last_error(env, napi_invalid_arg); |
| } |
| |
| *result = v8impl::JsValueFromV8LocalValue(typedArray); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env, |
| napi_value typedarray, |
| napi_typedarray_type* type, |
| size_t* length, |
| void** data, |
| napi_value* arraybuffer, |
| size_t* byte_offset) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, typedarray); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(typedarray); |
| RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), napi_invalid_arg); |
| |
| v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>(); |
| |
| if (type != nullptr) { |
| if (value->IsInt8Array()) { |
| *type = napi_int8_array; |
| } else if (value->IsUint8Array()) { |
| *type = napi_uint8_array; |
| } else if (value->IsUint8ClampedArray()) { |
| *type = napi_uint8_clamped_array; |
| } else if (value->IsInt16Array()) { |
| *type = napi_int16_array; |
| } else if (value->IsUint16Array()) { |
| *type = napi_uint16_array; |
| } else if (value->IsInt32Array()) { |
| *type = napi_int32_array; |
| } else if (value->IsUint32Array()) { |
| *type = napi_uint32_array; |
| } else if (value->IsFloat16Array()) { |
| *type = napi_float16_array; |
| } else if (value->IsFloat32Array()) { |
| *type = napi_float32_array; |
| } else if (value->IsFloat64Array()) { |
| *type = napi_float64_array; |
| } else if (value->IsBigInt64Array()) { |
| *type = napi_bigint64_array; |
| } else if (value->IsBigUint64Array()) { |
| *type = napi_biguint64_array; |
| } |
| } |
| |
| if (length != nullptr) { |
| *length = array->Length(); |
| } |
| |
| v8::Local<v8::ArrayBuffer> buffer; |
| if (data != nullptr || arraybuffer != nullptr) { |
| // Calling Buffer() may have the side effect of allocating the buffer, |
| // so only do this when it’s needed. |
| buffer = array->Buffer(); |
| } |
| |
| if (data != nullptr) { |
| *data = static_cast<uint8_t*>(buffer->Data()) + array->ByteOffset(); |
| } |
| |
| if (arraybuffer != nullptr) { |
| *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); |
| } |
| |
| if (byte_offset != nullptr) { |
| *byte_offset = array->ByteOffset(); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_dataview(napi_env env, |
| size_t byte_length, |
| napi_value arraybuffer, |
| size_t byte_offset, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, arraybuffer); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer); |
| |
| auto create_dataview = [&](auto buffer) -> napi_status { |
| if (byte_length + byte_offset > buffer->ByteLength()) { |
| napi_throw_range_error( |
| env, |
| "ERR_NAPI_INVALID_DATAVIEW_ARGS", |
| "byte_offset + byte_length should be less than or " |
| "equal to the size in bytes of the array passed in"); |
| return napi_set_last_error(env, napi_pending_exception); |
| } |
| |
| v8::Local<v8::DataView> data_view = |
| v8::DataView::New(buffer, byte_offset, byte_length); |
| *result = v8impl::JsValueFromV8LocalValue(data_view); |
| return GET_RETURN_STATUS(env); |
| }; |
| |
| if (value->IsArrayBuffer()) { |
| return create_dataview(value.As<v8::ArrayBuffer>()); |
| } else if (value->IsSharedArrayBuffer()) { |
| return create_dataview(value.As<v8::SharedArrayBuffer>()); |
| } else { |
| return napi_set_last_error(env, napi_invalid_arg); |
| } |
| } |
| |
| napi_status NAPI_CDECL napi_is_dataview(napi_env env, |
| napi_value value, |
| bool* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| *result = val->IsDataView(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_dataview_info(napi_env env, |
| napi_value dataview, |
| size_t* byte_length, |
| void** data, |
| napi_value* arraybuffer, |
| size_t* byte_offset) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, dataview); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(dataview); |
| RETURN_STATUS_IF_FALSE(env, value->IsDataView(), napi_invalid_arg); |
| |
| v8::Local<v8::DataView> array = value.As<v8::DataView>(); |
| |
| if (byte_length != nullptr) { |
| *byte_length = array->ByteLength(); |
| } |
| |
| v8::Local<v8::ArrayBuffer> buffer; |
| if (data != nullptr || arraybuffer != nullptr) { |
| // Calling Buffer() may have the side effect of allocating the buffer, |
| // so only do this when it’s needed. |
| buffer = array->Buffer(); |
| } |
| |
| if (data != nullptr) { |
| *data = static_cast<uint8_t*>(buffer->Data()) + array->ByteOffset(); |
| } |
| |
| if (arraybuffer != nullptr) { |
| *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); |
| } |
| |
| if (byte_offset != nullptr) { |
| *byte_offset = array->ByteOffset(); |
| } |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_version(node_api_basic_env env, |
| uint32_t* result) { |
| CHECK_ENV(env); |
| CHECK_ARG(env, result); |
| *result = NODE_API_SUPPORTED_VERSION_MAX; |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_promise(napi_env env, |
| napi_deferred* deferred, |
| napi_value* promise) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, deferred); |
| CHECK_ARG(env, promise); |
| |
| auto maybe = v8::Promise::Resolver::New(env->context()); |
| CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); |
| |
| auto v8_resolver = maybe.ToLocalChecked(); |
| auto v8_deferred = new v8impl::Persistent<v8::Value>(); |
| v8_deferred->Reset(env->isolate, v8_resolver); |
| |
| *deferred = v8impl::JsDeferredFromNodePersistent(v8_deferred); |
| *promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise()); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_resolve_deferred(napi_env env, |
| napi_deferred deferred, |
| napi_value resolution) { |
| return v8impl::ConcludeDeferred(env, deferred, resolution, true); |
| } |
| |
| napi_status NAPI_CDECL napi_reject_deferred(napi_env env, |
| napi_deferred deferred, |
| napi_value resolution) { |
| return v8impl::ConcludeDeferred(env, deferred, resolution, false); |
| } |
| |
| napi_status NAPI_CDECL napi_is_promise(napi_env env, |
| napi_value value, |
| bool* is_promise) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, is_promise); |
| |
| *is_promise = v8impl::V8LocalValueFromJsValue(value)->IsPromise(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_create_date(napi_env env, |
| double time, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, result); |
| |
| v8::MaybeLocal<v8::Value> maybe_date = v8::Date::New(env->context(), time); |
| CHECK_MAYBE_EMPTY(env, maybe_date, napi_generic_failure); |
| |
| *result = v8impl::JsValueFromV8LocalValue(maybe_date.ToLocalChecked()); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_is_date(napi_env env, |
| napi_value value, |
| bool* is_date) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, is_date); |
| |
| *is_date = v8impl::V8LocalValueFromJsValue(value)->IsDate(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_date_value(napi_env env, |
| napi_value value, |
| double* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, value); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); |
| RETURN_STATUS_IF_FALSE(env, val->IsDate(), napi_date_expected); |
| |
| v8::Local<v8::Date> date = val.As<v8::Date>(); |
| *result = date->ValueOf(); |
| |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL napi_run_script(napi_env env, |
| napi_value script, |
| napi_value* result) { |
| NAPI_PREAMBLE(env); |
| CHECK_ARG(env, script); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script); |
| |
| if (!v8_script->IsString()) { |
| return napi_set_last_error(env, napi_string_expected); |
| } |
| |
| v8::Local<v8::Context> context = env->context(); |
| |
| auto maybe_script = v8::Script::Compile(context, v8_script.As<v8::String>()); |
| CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); |
| |
| auto script_result = maybe_script.ToLocalChecked()->Run(context); |
| CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); |
| |
| *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); |
| return GET_RETURN_STATUS(env); |
| } |
| |
| napi_status NAPI_CDECL |
| napi_add_finalizer(napi_env env, |
| napi_value js_object, |
| void* finalize_data, |
| node_api_basic_finalize basic_finalize_cb, |
| void* finalize_hint, |
| napi_ref* result) { |
| // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw |
| // JS exceptions. |
| napi_finalize finalize_cb = |
| reinterpret_cast<napi_finalize>(basic_finalize_cb); |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, js_object); |
| CHECK_ARG(env, finalize_cb); |
| |
| v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(js_object); |
| RETURN_STATUS_IF_FALSE(env, v8_value->IsObject(), napi_invalid_arg); |
| |
| // Create a self-deleting reference if the optional out-param result is not |
| // set. |
| v8impl::ReferenceOwnership ownership = |
| result == nullptr ? v8impl::ReferenceOwnership::kRuntime |
| : v8impl::ReferenceOwnership::kUserland; |
| v8impl::Reference* reference = v8impl::ReferenceWithFinalizer::New( |
| env, v8_value, 0, ownership, finalize_cb, finalize_data, finalize_hint); |
| |
| if (result != nullptr) { |
| *result = reinterpret_cast<napi_ref>(reference); |
| } |
| return napi_clear_last_error(env); |
| } |
| |
| #ifdef NAPI_EXPERIMENTAL |
| |
| napi_status NAPI_CDECL node_api_post_finalizer(node_api_basic_env basic_env, |
| napi_finalize finalize_cb, |
| void* finalize_data, |
| void* finalize_hint) { |
| napi_env env = const_cast<napi_env>(basic_env); |
| CHECK_ENV(env); |
| env->EnqueueFinalizer(v8impl::TrackedFinalizer::New( |
| env, finalize_cb, finalize_data, finalize_hint)); |
| return napi_clear_last_error(env); |
| } |
| |
| #endif |
| |
| napi_status NAPI_CDECL napi_adjust_external_memory(node_api_basic_env env, |
| int64_t change_in_bytes, |
| int64_t* adjusted_value) { |
| CHECK_ENV(env); |
| CHECK_ARG(env, adjusted_value); |
| |
| *adjusted_value = |
| env->isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_set_instance_data(node_api_basic_env basic_env, |
| void* data, |
| napi_finalize finalize_cb, |
| void* finalize_hint) { |
| napi_env env = const_cast<napi_env>(basic_env); |
| CHECK_ENV(env); |
| |
| v8impl::TrackedFinalizer* old_data = |
| static_cast<v8impl::TrackedFinalizer*>(env->instance_data); |
| if (old_data != nullptr) { |
| // Our contract so far has been to not finalize any old data there may be. |
| // So we simply delete it. |
| delete old_data; |
| } |
| |
| env->instance_data = |
| v8impl::TrackedFinalizer::New(env, finalize_cb, data, finalize_hint); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_get_instance_data(node_api_basic_env env, |
| void** data) { |
| CHECK_ENV(env); |
| CHECK_ARG(env, data); |
| |
| v8impl::TrackedFinalizer* idata = |
| static_cast<v8impl::TrackedFinalizer*>(env->instance_data); |
| |
| *data = (idata == nullptr ? nullptr : idata->data()); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_detach_arraybuffer(napi_env env, |
| napi_value arraybuffer) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, arraybuffer); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer); |
| RETURN_STATUS_IF_FALSE( |
| env, value->IsArrayBuffer(), napi_arraybuffer_expected); |
| |
| v8::Local<v8::ArrayBuffer> it = value.As<v8::ArrayBuffer>(); |
| RETURN_STATUS_IF_FALSE( |
| env, it->IsDetachable(), napi_detachable_arraybuffer_expected); |
| |
| it->Detach(v8::Local<v8::Value>()).Check(); |
| |
| return napi_clear_last_error(env); |
| } |
| |
| napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env, |
| napi_value arraybuffer, |
| bool* result) { |
| CHECK_ENV_NOT_IN_GC(env); |
| CHECK_ARG(env, arraybuffer); |
| CHECK_ARG(env, result); |
| |
| v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer); |
| |
| *result = |
| value->IsArrayBuffer() && value.As<v8::ArrayBuffer>()->WasDetached(); |
| |
| return napi_clear_last_error(env); |
| } |