1 #include <algorithm>
2 #include <climits> // INT_MAX
3 #include <cmath>
4 #define NAPI_EXPERIMENTAL
5 #include "env-inl.h"
6 #include "js_native_api.h"
7 #include "js_native_api_v8.h"
8 #include "util-inl.h"
9
10 #define CHECK_MAYBE_NOTHING(env, maybe, status) \
11 RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status))
12
13 #define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \
14 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsNothing()), (status))
15
16 #define CHECK_TO_NUMBER(env, context, result, src) \
17 CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected)
18
19 // n-api defines NAPI_AUTO_LENGTH as the indicator that a string
20 // is null terminated. For V8 the equivalent is -1. The assert
21 // validates that our cast of NAPI_AUTO_LENGTH results in -1 as
22 // needed by V8.
23 #define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \
24 do { \
25 static_assert(static_cast<int>(NAPI_AUTO_LENGTH) == -1, \
26 "Casting NAPI_AUTO_LENGTH to int must result in -1"); \
27 RETURN_STATUS_IF_FALSE( \
28 (env), (len == NAPI_AUTO_LENGTH) || len <= INT_MAX, napi_invalid_arg); \
29 RETURN_STATUS_IF_FALSE((env), (str) != nullptr, napi_invalid_arg); \
30 auto str_maybe = v8::String::NewFromUtf8((env)->isolate, \
31 (str), \
32 v8::NewStringType::kInternalized, \
33 static_cast<int>(len)); \
34 CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \
35 (result) = str_maybe.ToLocalChecked(); \
36 } while (0)
37
38 #define CHECK_NEW_FROM_UTF8(env, result, str) \
39 CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), NAPI_AUTO_LENGTH)
40
41 #define CREATE_TYPED_ARRAY( \
42 env, type, size_of_element, buffer, byte_offset, length, out) \
43 do { \
44 if ((size_of_element) > 1) { \
45 THROW_RANGE_ERROR_IF_FALSE( \
46 (env), \
47 (byte_offset) % (size_of_element) == 0, \
48 "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \
49 "start offset of " #type \
50 " should be a multiple of " #size_of_element); \
51 } \
52 THROW_RANGE_ERROR_IF_FALSE( \
53 (env), \
54 (length) * (size_of_element) + (byte_offset) <= buffer->ByteLength(), \
55 "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \
56 "Invalid typed array length"); \
57 (out) = v8::type::New((buffer), (byte_offset), (length)); \
58 } while (0)
59
60 namespace v8impl {
61
62 namespace {
63
64 template <typename CCharType, typename StringMaker>
NewString(napi_env env,const CCharType * str,size_t length,napi_value * result,StringMaker string_maker)65 napi_status NewString(napi_env env,
66 const CCharType* str,
67 size_t length,
68 napi_value* result,
69 StringMaker string_maker) {
70 CHECK_ENV(env);
71 if (length > 0) CHECK_ARG(env, str);
72 CHECK_ARG(env, result);
73 RETURN_STATUS_IF_FALSE(
74 env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
75
76 auto isolate = env->isolate;
77 auto str_maybe = string_maker(isolate);
78 CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
79 *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
80 return napi_clear_last_error(env);
81 }
82
83 template <typename CharType, typename CreateAPI, typename StringMaker>
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)84 napi_status NewExternalString(napi_env env,
85 CharType* str,
86 size_t length,
87 napi_finalize finalize_callback,
88 void* finalize_hint,
89 napi_value* result,
90 bool* copied,
91 CreateAPI create_api,
92 StringMaker string_maker) {
93 napi_status status;
94 #if defined(V8_ENABLE_SANDBOX)
95 status = create_api(env, str, length, result);
96 if (status == napi_ok) {
97 if (copied != nullptr) {
98 *copied = true;
99 }
100 if (finalize_callback) {
101 env->CallFinalizer(
102 finalize_callback, static_cast<CharType*>(str), finalize_hint);
103 }
104 }
105 #else
106 status = NewString(env, str, length, result, string_maker);
107 if (status == napi_ok && copied != nullptr) {
108 *copied = false;
109 }
110 #endif // V8_ENABLE_SANDBOX
111 return status;
112 }
113
114 class TrackedStringResource : public Finalizer, RefTracker {
115 public:
TrackedStringResource(napi_env env,napi_finalize finalize_callback,void * data,void * finalize_hint)116 TrackedStringResource(napi_env env,
117 napi_finalize finalize_callback,
118 void* data,
119 void* finalize_hint)
120 : Finalizer(env, finalize_callback, data, finalize_hint) {
121 Link(finalize_callback == nullptr ? &env->reflist
122 : &env->finalizing_reflist);
123 }
124
125 protected:
126 // The only time Finalize() gets called before Dispose() is if the
127 // environment is dying. Finalize() expects that the item will be unlinked,
128 // so we do it here. V8 will still call Dispose() on us later, so we don't do
129 // any deleting here. We just null out env_ to avoid passing a stale pointer
130 // to the user's finalizer when V8 does finally call Dispose().
Finalize()131 void Finalize() override {
132 Unlink();
133 env_ = nullptr;
134 }
135
~TrackedStringResource()136 ~TrackedStringResource() {
137 if (finalize_callback_ == nullptr) return;
138 if (env_ == nullptr) {
139 // The environment is dead. Call the finalizer directly.
140 finalize_callback_(nullptr, finalize_data_, finalize_hint_);
141 } else {
142 // The environment is still alive. Let's remove ourselves from its list
143 // of references and call the user's finalizer.
144 Unlink();
145 env_->CallFinalizer(finalize_callback_, finalize_data_, finalize_hint_);
146 }
147 }
148 };
149
150 class ExternalOneByteStringResource
151 : public v8::String::ExternalOneByteStringResource,
152 TrackedStringResource {
153 public:
ExternalOneByteStringResource(napi_env env,char * string,const size_t length,napi_finalize finalize_callback,void * finalize_hint)154 ExternalOneByteStringResource(napi_env env,
155 char* string,
156 const size_t length,
157 napi_finalize finalize_callback,
158 void* finalize_hint)
159 : TrackedStringResource(env, finalize_callback, string, finalize_hint),
160 string_(string),
161 length_(length) {}
162
data() const163 const char* data() const override { return string_; }
length() const164 size_t length() const override { return length_; }
165
166 private:
167 const char* string_;
168 const size_t length_;
169 };
170
171 class ExternalStringResource : public v8::String::ExternalStringResource,
172 TrackedStringResource {
173 public:
ExternalStringResource(napi_env env,char16_t * string,const size_t length,napi_finalize finalize_callback,void * finalize_hint)174 ExternalStringResource(napi_env env,
175 char16_t* string,
176 const size_t length,
177 napi_finalize finalize_callback,
178 void* finalize_hint)
179 : TrackedStringResource(env, finalize_callback, string, finalize_hint),
180 string_(reinterpret_cast<uint16_t*>(string)),
181 length_(length) {}
182
data() const183 const uint16_t* data() const override { return string_; }
length() const184 size_t length() const override { return length_; }
185
186 private:
187 const uint16_t* string_;
188 const size_t length_;
189 };
190
V8NameFromPropertyDescriptor(napi_env env,const napi_property_descriptor * p,v8::Local<v8::Name> * result)191 inline napi_status V8NameFromPropertyDescriptor(
192 napi_env env,
193 const napi_property_descriptor* p,
194 v8::Local<v8::Name>* result) {
195 if (p->utf8name != nullptr) {
196 CHECK_NEW_FROM_UTF8(env, *result, p->utf8name);
197 } else {
198 v8::Local<v8::Value> property_value =
199 v8impl::V8LocalValueFromJsValue(p->name);
200
201 RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected);
202 *result = property_value.As<v8::Name>();
203 }
204
205 return napi_ok;
206 }
207
208 // convert from n-api property attributes to v8::PropertyAttribute
V8PropertyAttributesFromDescriptor(const napi_property_descriptor * descriptor)209 inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor(
210 const napi_property_descriptor* descriptor) {
211 unsigned int attribute_flags = v8::PropertyAttribute::None;
212
213 // The napi_writable attribute is ignored for accessor descriptors, but
214 // V8 would throw `TypeError`s on assignment with nonexistence of a setter.
215 if ((descriptor->getter == nullptr && descriptor->setter == nullptr) &&
216 (descriptor->attributes & napi_writable) == 0) {
217 attribute_flags |= v8::PropertyAttribute::ReadOnly;
218 }
219
220 if ((descriptor->attributes & napi_enumerable) == 0) {
221 attribute_flags |= v8::PropertyAttribute::DontEnum;
222 }
223 if ((descriptor->attributes & napi_configurable) == 0) {
224 attribute_flags |= v8::PropertyAttribute::DontDelete;
225 }
226
227 return static_cast<v8::PropertyAttribute>(attribute_flags);
228 }
229
JsDeferredFromNodePersistent(v8impl::Persistent<v8::Value> * local)230 inline napi_deferred JsDeferredFromNodePersistent(
231 v8impl::Persistent<v8::Value>* local) {
232 return reinterpret_cast<napi_deferred>(local);
233 }
234
NodePersistentFromJsDeferred(napi_deferred local)235 inline v8impl::Persistent<v8::Value>* NodePersistentFromJsDeferred(
236 napi_deferred local) {
237 return reinterpret_cast<v8impl::Persistent<v8::Value>*>(local);
238 }
239
240 class HandleScopeWrapper {
241 public:
HandleScopeWrapper(v8::Isolate * isolate)242 explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {}
243
244 private:
245 v8::HandleScope scope;
246 };
247
248 // In node v0.10 version of v8, there is no EscapableHandleScope and the
249 // node v0.10 port use HandleScope::Close(Local<T> v) to mimic the behavior
250 // of a EscapableHandleScope::Escape(Local<T> v), but it is not the same
251 // semantics. This is an example of where the api abstraction fail to work
252 // across different versions.
253 class EscapableHandleScopeWrapper {
254 public:
EscapableHandleScopeWrapper(v8::Isolate * isolate)255 explicit EscapableHandleScopeWrapper(v8::Isolate* isolate)
256 : scope(isolate), escape_called_(false) {}
escape_called() const257 bool escape_called() const { return escape_called_; }
258 template <typename T>
Escape(v8::Local<T> handle)259 v8::Local<T> Escape(v8::Local<T> handle) {
260 escape_called_ = true;
261 return scope.Escape(handle);
262 }
263
264 private:
265 v8::EscapableHandleScope scope;
266 bool escape_called_;
267 };
268
JsHandleScopeFromV8HandleScope(HandleScopeWrapper * s)269 inline napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) {
270 return reinterpret_cast<napi_handle_scope>(s);
271 }
272
V8HandleScopeFromJsHandleScope(napi_handle_scope s)273 inline HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) {
274 return reinterpret_cast<HandleScopeWrapper*>(s);
275 }
276
277 inline napi_escapable_handle_scope
JsEscapableHandleScopeFromV8EscapableHandleScope(EscapableHandleScopeWrapper * s)278 JsEscapableHandleScopeFromV8EscapableHandleScope(
279 EscapableHandleScopeWrapper* s) {
280 return reinterpret_cast<napi_escapable_handle_scope>(s);
281 }
282
283 inline EscapableHandleScopeWrapper*
V8EscapableHandleScopeFromJsEscapableHandleScope(napi_escapable_handle_scope s)284 V8EscapableHandleScopeFromJsEscapableHandleScope(
285 napi_escapable_handle_scope s) {
286 return reinterpret_cast<EscapableHandleScopeWrapper*>(s);
287 }
288
ConcludeDeferred(napi_env env,napi_deferred deferred,napi_value result,bool is_resolved)289 inline napi_status ConcludeDeferred(napi_env env,
290 napi_deferred deferred,
291 napi_value result,
292 bool is_resolved) {
293 NAPI_PREAMBLE(env);
294 CHECK_ARG(env, result);
295
296 v8::Local<v8::Context> context = env->context();
297 v8impl::Persistent<v8::Value>* deferred_ref =
298 NodePersistentFromJsDeferred(deferred);
299 v8::Local<v8::Value> v8_deferred =
300 v8::Local<v8::Value>::New(env->isolate, *deferred_ref);
301
302 auto v8_resolver = v8_deferred.As<v8::Promise::Resolver>();
303
304 v8::Maybe<bool> success =
305 is_resolved ? v8_resolver->Resolve(
306 context, v8impl::V8LocalValueFromJsValue(result))
307 : v8_resolver->Reject(
308 context, v8impl::V8LocalValueFromJsValue(result));
309
310 delete deferred_ref;
311
312 RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure);
313
314 return GET_RETURN_STATUS(env);
315 }
316
317 enum UnwrapAction { KeepWrap, RemoveWrap };
318
Unwrap(napi_env env,napi_value js_object,void ** result,UnwrapAction action)319 inline napi_status Unwrap(napi_env env,
320 napi_value js_object,
321 void** result,
322 UnwrapAction action) {
323 NAPI_PREAMBLE(env);
324 CHECK_ARG(env, js_object);
325 if (action == KeepWrap) {
326 CHECK_ARG(env, result);
327 }
328
329 v8::Local<v8::Context> context = env->context();
330
331 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object);
332 RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
333 v8::Local<v8::Object> obj = value.As<v8::Object>();
334
335 auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
336 .ToLocalChecked();
337 RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg);
338 Reference* reference =
339 static_cast<v8impl::Reference*>(val.As<v8::External>()->Value());
340
341 if (result) {
342 *result = reference->Data();
343 }
344
345 if (action == RemoveWrap) {
346 CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
347 .FromJust());
348 if (reference->ownership() == Ownership::kUserland) {
349 // When the wrap is been removed, the finalizer should be reset.
350 reference->ResetFinalizer();
351 } else {
352 delete reference;
353 }
354 }
355
356 return GET_RETURN_STATUS(env);
357 }
358
359 //=== Function napi_callback wrapper =================================
360
361 // Use this data structure to associate callback data with each N-API function
362 // exposed to JavaScript. The structure is stored in a v8::External which gets
363 // passed into our callback wrapper. This reduces the performance impact of
364 // calling through N-API.
365 // Ref: benchmark/misc/function_call
366 // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072
367 class CallbackBundle {
368 public:
369 // Creates an object to be made available to the static function callback
370 // wrapper, used to retrieve the native callback function and data pointer.
New(napi_env env,napi_callback cb,void * data)371 static inline v8::Local<v8::Value> New(napi_env env,
372 napi_callback cb,
373 void* data) {
374 CallbackBundle* bundle = new CallbackBundle();
375 bundle->cb = cb;
376 bundle->cb_data = data;
377 bundle->env = env;
378
379 v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle);
380 Reference::New(
381 env, cbdata, 0, Ownership::kRuntime, Delete, bundle, nullptr);
382 return cbdata;
383 }
384 napi_env env; // Necessary to invoke C++ NAPI callback
385 void* cb_data; // The user provided callback data
386 napi_callback cb;
387
388 private:
Delete(napi_env env,void * data,void * hint)389 static void Delete(napi_env env, void* data, void* hint) {
390 CallbackBundle* bundle = static_cast<CallbackBundle*>(data);
391 delete bundle;
392 }
393 };
394
395 // Base class extended by classes that wrap V8 function and property callback
396 // info.
397 class CallbackWrapper {
398 public:
CallbackWrapper(napi_value this_arg,size_t args_length,void * data)399 inline CallbackWrapper(napi_value this_arg, size_t args_length, void* data)
400 : _this(this_arg), _args_length(args_length), _data(data) {}
401
402 virtual napi_value GetNewTarget() = 0;
403 virtual void Args(napi_value* buffer, size_t bufferlength) = 0;
404 virtual void SetReturnValue(napi_value value) = 0;
405
This()406 napi_value This() { return _this; }
407
ArgsLength()408 size_t ArgsLength() { return _args_length; }
409
Data()410 void* Data() { return _data; }
411
412 protected:
413 const napi_value _this;
414 const size_t _args_length;
415 void* _data;
416 };
417
418 class CallbackWrapperBase : public CallbackWrapper {
419 public:
CallbackWrapperBase(const v8::FunctionCallbackInfo<v8::Value> & cbinfo,const size_t args_length)420 inline CallbackWrapperBase(const v8::FunctionCallbackInfo<v8::Value>& cbinfo,
421 const size_t args_length)
422 : CallbackWrapper(
423 JsValueFromV8LocalValue(cbinfo.This()), args_length, nullptr),
424 _cbinfo(cbinfo) {
425 _bundle = reinterpret_cast<CallbackBundle*>(
426 cbinfo.Data().As<v8::External>()->Value());
427 _data = _bundle->cb_data;
428 }
429
430 protected:
InvokeCallback()431 inline void InvokeCallback() {
432 napi_callback_info cbinfo_wrapper = reinterpret_cast<napi_callback_info>(
433 static_cast<CallbackWrapper*>(this));
434
435 // All other pointers we need are stored in `_bundle`
436 napi_env env = _bundle->env;
437 napi_callback cb = _bundle->cb;
438
439 napi_value result = nullptr;
440 bool exceptionOccurred = false;
441 env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); },
442 [&](napi_env env, v8::Local<v8::Value> value) {
443 exceptionOccurred = true;
444 if (env->terminatedOrTerminating()) {
445 return;
446 }
447 env->isolate->ThrowException(value);
448 });
449
450 if (!exceptionOccurred && (result != nullptr)) {
451 this->SetReturnValue(result);
452 }
453 }
454
455 const v8::FunctionCallbackInfo<v8::Value>& _cbinfo;
456 CallbackBundle* _bundle;
457 };
458
459 class FunctionCallbackWrapper : public CallbackWrapperBase {
460 public:
Invoke(const v8::FunctionCallbackInfo<v8::Value> & info)461 static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
462 FunctionCallbackWrapper cbwrapper(info);
463 cbwrapper.InvokeCallback();
464 }
465
NewFunction(napi_env env,napi_callback cb,void * cb_data,v8::Local<v8::Function> * result)466 static inline napi_status NewFunction(napi_env env,
467 napi_callback cb,
468 void* cb_data,
469 v8::Local<v8::Function>* result) {
470 v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, cb, cb_data);
471 RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure);
472
473 v8::MaybeLocal<v8::Function> maybe_function =
474 v8::Function::New(env->context(), Invoke, cbdata);
475 CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure);
476
477 *result = maybe_function.ToLocalChecked();
478 return napi_clear_last_error(env);
479 }
480
NewTemplate(napi_env env,napi_callback cb,void * cb_data,v8::Local<v8::FunctionTemplate> * result,v8::Local<v8::Signature> sig=v8::Local<v8::Signature> ())481 static inline napi_status NewTemplate(
482 napi_env env,
483 napi_callback cb,
484 void* cb_data,
485 v8::Local<v8::FunctionTemplate>* result,
486 v8::Local<v8::Signature> sig = v8::Local<v8::Signature>()) {
487 v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, cb, cb_data);
488 RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure);
489
490 *result = v8::FunctionTemplate::New(env->isolate, Invoke, cbdata, sig);
491 return napi_clear_last_error(env);
492 }
493
FunctionCallbackWrapper(const v8::FunctionCallbackInfo<v8::Value> & cbinfo)494 explicit FunctionCallbackWrapper(
495 const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
496 : CallbackWrapperBase(cbinfo, cbinfo.Length()) {}
497
GetNewTarget()498 napi_value GetNewTarget() override {
499 if (_cbinfo.IsConstructCall()) {
500 return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget());
501 } else {
502 return nullptr;
503 }
504 }
505
506 /*virtual*/
Args(napi_value * buffer,size_t buffer_length)507 void Args(napi_value* buffer, size_t buffer_length) override {
508 size_t i = 0;
509 size_t min = std::min(buffer_length, _args_length);
510
511 for (; i < min; i += 1) {
512 buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]);
513 }
514
515 if (i < buffer_length) {
516 napi_value undefined =
517 v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
518 for (; i < buffer_length; i += 1) {
519 buffer[i] = undefined;
520 }
521 }
522 }
523
524 /*virtual*/
SetReturnValue(napi_value value)525 void SetReturnValue(napi_value value) override {
526 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
527 _cbinfo.GetReturnValue().Set(val);
528 }
529 };
530
Wrap(napi_env env,napi_value js_object,void * native_object,napi_finalize finalize_cb,void * finalize_hint,napi_ref * result)531 inline napi_status Wrap(napi_env env,
532 napi_value js_object,
533 void* native_object,
534 napi_finalize finalize_cb,
535 void* finalize_hint,
536 napi_ref* result) {
537 NAPI_PREAMBLE(env);
538 CHECK_ARG(env, js_object);
539
540 v8::Local<v8::Context> context = env->context();
541
542 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object);
543 RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
544 v8::Local<v8::Object> obj = value.As<v8::Object>();
545
546 // If we've already wrapped this object, we error out.
547 RETURN_STATUS_IF_FALSE(
548 env,
549 !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).FromJust(),
550 napi_invalid_arg);
551
552 v8impl::Reference* reference = nullptr;
553 if (result != nullptr) {
554 // The returned reference should be deleted via napi_delete_reference()
555 // ONLY in response to the finalize callback invocation. (If it is deleted
556 // before then, then the finalize callback will never be invoked.)
557 // Therefore a finalize callback is required when returning a reference.
558 CHECK_ARG(env, finalize_cb);
559 reference = v8impl::Reference::New(env,
560 obj,
561 0,
562 v8impl::Ownership::kUserland,
563 finalize_cb,
564 native_object,
565 finalize_hint);
566 *result = reinterpret_cast<napi_ref>(reference);
567 } else {
568 // Create a self-deleting reference.
569 reference = v8impl::Reference::New(
570 env,
571 obj,
572 0,
573 v8impl::Ownership::kRuntime,
574 finalize_cb,
575 native_object,
576 finalize_cb == nullptr ? nullptr : finalize_hint);
577 }
578
579 CHECK(obj->SetPrivate(context,
580 NAPI_PRIVATE_KEY(context, wrapper),
581 v8::External::New(env->isolate, reference))
582 .FromJust());
583
584 return GET_RETURN_STATUS(env);
585 }
586
587 // In JavaScript, weak references can be created for object types (Object,
588 // Function, and external Object) and for local symbols that are created with
589 // the `Symbol` function call. Global symbols created with the `Symbol.for`
590 // method cannot be weak references because they are never collected.
591 //
592 // Currently, V8 has no API to detect if a symbol is local or global.
593 // Until we have a V8 API for it, we consider that all symbols can be weak.
594 // This matches the current Node-API behavior.
CanBeHeldWeakly(v8::Local<v8::Value> value)595 inline bool CanBeHeldWeakly(v8::Local<v8::Value> value) {
596 return value->IsObject() || value->IsSymbol();
597 }
598
599 } // end of anonymous namespace
600
ResetFinalizer()601 void Finalizer::ResetFinalizer() {
602 finalize_callback_ = nullptr;
603 finalize_data_ = nullptr;
604 finalize_hint_ = nullptr;
605 }
606
607 // Wrapper around v8impl::Persistent that implements reference counting.
RefBase(napi_env env,uint32_t initial_refcount,Ownership ownership,napi_finalize finalize_callback,void * finalize_data,void * finalize_hint)608 RefBase::RefBase(napi_env env,
609 uint32_t initial_refcount,
610 Ownership ownership,
611 napi_finalize finalize_callback,
612 void* finalize_data,
613 void* finalize_hint)
614 : Finalizer(env, finalize_callback, finalize_data, finalize_hint),
615 refcount_(initial_refcount),
616 ownership_(ownership) {
617 Link(finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist);
618 }
619
620 // When a RefBase is being deleted, it may have been queued to call its
621 // finalizer.
~RefBase()622 RefBase::~RefBase() {
623 // Remove from the env's tracked list.
624 Unlink();
625 // Try to remove the finalizer from the scheduled second pass callback.
626 env_->DequeueFinalizer(this);
627 }
628
New(napi_env env,uint32_t initial_refcount,Ownership ownership,napi_finalize finalize_callback,void * finalize_data,void * finalize_hint)629 RefBase* RefBase::New(napi_env env,
630 uint32_t initial_refcount,
631 Ownership ownership,
632 napi_finalize finalize_callback,
633 void* finalize_data,
634 void* finalize_hint) {
635 return new RefBase(env,
636 initial_refcount,
637 ownership,
638 finalize_callback,
639 finalize_data,
640 finalize_hint);
641 }
642
Data()643 void* RefBase::Data() {
644 return finalize_data_;
645 }
646
Ref()647 uint32_t RefBase::Ref() {
648 return ++refcount_;
649 }
650
Unref()651 uint32_t RefBase::Unref() {
652 if (refcount_ == 0) {
653 return 0;
654 }
655 return --refcount_;
656 }
657
RefCount()658 uint32_t RefBase::RefCount() {
659 return refcount_;
660 }
661
Finalize()662 void RefBase::Finalize() {
663 Ownership ownership = ownership_;
664 // Swap out the field finalize_callback so that it can not be accidentally
665 // called more than once.
666 napi_finalize finalize_callback = finalize_callback_;
667 void* finalize_data = finalize_data_;
668 void* finalize_hint = finalize_hint_;
669 ResetFinalizer();
670
671 // Either the RefBase is going to be deleted in the finalize_callback or not,
672 // it should be removed from the tracked list.
673 Unlink();
674 // 1. If the finalize_callback is present, it should either delete the
675 // RefBase, or set ownership with Ownership::kRuntime.
676 // 2. If the finalizer is not present, the RefBase can be deleted after the
677 // call.
678 if (finalize_callback != nullptr) {
679 env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint);
680 // No access to `this` after finalize_callback is called.
681 }
682
683 // If the RefBase is not Ownership::kRuntime, userland code should delete it.
684 // Now delete it if it is Ownership::kRuntime.
685 if (ownership == Ownership::kRuntime) {
686 delete this;
687 }
688 }
689
690 template <typename... Args>
Reference(napi_env env,v8::Local<v8::Value> value,Args &&...args)691 Reference::Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args)
692 : RefBase(env, std::forward<Args>(args)...),
693 persistent_(env->isolate, value),
694 can_be_weak_(CanBeHeldWeakly(value)) {
695 if (RefCount() == 0) {
696 SetWeak();
697 }
698 }
699
~Reference()700 Reference::~Reference() {
701 // Reset the handle. And no weak callback will be invoked.
702 persistent_.Reset();
703 }
704
New(napi_env env,v8::Local<v8::Value> value,uint32_t initial_refcount,Ownership ownership,napi_finalize finalize_callback,void * finalize_data,void * finalize_hint)705 Reference* Reference::New(napi_env env,
706 v8::Local<v8::Value> value,
707 uint32_t initial_refcount,
708 Ownership ownership,
709 napi_finalize finalize_callback,
710 void* finalize_data,
711 void* finalize_hint) {
712 return new Reference(env,
713 value,
714 initial_refcount,
715 ownership,
716 finalize_callback,
717 finalize_data,
718 finalize_hint);
719 }
720
Ref()721 uint32_t Reference::Ref() {
722 // When the persistent_ is cleared in the WeakCallback, and a second pass
723 // callback is pending, return 0 unconditionally.
724 if (persistent_.IsEmpty()) {
725 return 0;
726 }
727 uint32_t refcount = RefBase::Ref();
728 if (refcount == 1 && can_be_weak_) {
729 persistent_.ClearWeak();
730 }
731 return refcount;
732 }
733
Unref()734 uint32_t Reference::Unref() {
735 // When the persistent_ is cleared in the WeakCallback, and a second pass
736 // callback is pending, return 0 unconditionally.
737 if (persistent_.IsEmpty()) {
738 return 0;
739 }
740 uint32_t old_refcount = RefCount();
741 uint32_t refcount = RefBase::Unref();
742 if (old_refcount == 1 && refcount == 0) {
743 SetWeak();
744 }
745 return refcount;
746 }
747
Get()748 v8::Local<v8::Value> Reference::Get() {
749 if (persistent_.IsEmpty()) {
750 return v8::Local<v8::Value>();
751 } else {
752 return v8::Local<v8::Value>::New(env_->isolate, persistent_);
753 }
754 }
755
Finalize()756 void Reference::Finalize() {
757 // Unconditionally reset the persistent handle so that no weak callback will
758 // be invoked again.
759 persistent_.Reset();
760
761 // Chain up to perform the rest of the finalization.
762 RefBase::Finalize();
763 }
764
765 // Mark the reference as weak and eligible for collection
766 // by the gc.
SetWeak()767 void Reference::SetWeak() {
768 if (can_be_weak_) {
769 persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
770 } else {
771 persistent_.Reset();
772 }
773 }
774
775 // The N-API finalizer callback may make calls into the engine. V8's heap is
776 // not in a consistent state during the weak callback, and therefore it does
777 // not support calls back into it. Enqueue the invocation of the finalizer.
WeakCallback(const v8::WeakCallbackInfo<Reference> & data)778 void Reference::WeakCallback(const v8::WeakCallbackInfo<Reference>& data) {
779 Reference* reference = data.GetParameter();
780 // The reference must be reset during the weak callback as the API protocol.
781 reference->persistent_.Reset();
782 reference->env_->EnqueueFinalizer(reference);
783 }
784
785 } // end of namespace v8impl
786
787 // Warning: Keep in-sync with napi_status enum
788 static const char* error_messages[] = {
789 nullptr,
790 "Invalid argument",
791 "An object was expected",
792 "A string was expected",
793 "A string or symbol was expected",
794 "A function was expected",
795 "A number was expected",
796 "A boolean was expected",
797 "An array was expected",
798 "Unknown failure",
799 "An exception is pending",
800 "The async work item was cancelled",
801 "napi_escape_handle already called on scope",
802 "Invalid handle scope usage",
803 "Invalid callback scope usage",
804 "Thread-safe function queue is full",
805 "Thread-safe function handle is closing",
806 "A bigint was expected",
807 "A date was expected",
808 "An arraybuffer was expected",
809 "A detachable arraybuffer was expected",
810 "Main thread would deadlock",
811 "External buffers are not allowed",
812 "Cannot run JavaScript",
813 };
814
napi_get_last_error_info(napi_env env,const napi_extended_error_info ** result)815 napi_status NAPI_CDECL napi_get_last_error_info(
816 napi_env env, const napi_extended_error_info** result) {
817 CHECK_ENV(env);
818 CHECK_ARG(env, result);
819
820 // The value of the constant below must be updated to reference the last
821 // message in the `napi_status` enum each time a new error message is added.
822 // We don't have a napi_status_last as this would result in an ABI
823 // change each time a message was added.
824 const int last_status = napi_cannot_run_js;
825
826 static_assert(NAPI_ARRAYSIZE(error_messages) == last_status + 1,
827 "Count of error messages must match count of error values");
828 CHECK_LE(env->last_error.error_code, last_status);
829 // Wait until someone requests the last error information to fetch the error
830 // message string
831 env->last_error.error_message = error_messages[env->last_error.error_code];
832
833 if (env->last_error.error_code == napi_ok) {
834 napi_clear_last_error(env);
835 }
836 *result = &(env->last_error);
837 return napi_ok;
838 }
839
napi_create_function(napi_env env,const char * utf8name,size_t length,napi_callback cb,void * callback_data,napi_value * result)840 napi_status NAPI_CDECL napi_create_function(napi_env env,
841 const char* utf8name,
842 size_t length,
843 napi_callback cb,
844 void* callback_data,
845 napi_value* result) {
846 NAPI_PREAMBLE(env);
847 CHECK_ARG(env, result);
848 CHECK_ARG(env, cb);
849
850 v8::Local<v8::Function> return_value;
851 v8::EscapableHandleScope scope(env->isolate);
852 v8::Local<v8::Function> fn;
853 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
854 env, cb, callback_data, &fn));
855 return_value = scope.Escape(fn);
856
857 if (utf8name != nullptr) {
858 v8::Local<v8::String> name_string;
859 CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
860 return_value->SetName(name_string);
861 }
862
863 *result = v8impl::JsValueFromV8LocalValue(return_value);
864
865 return GET_RETURN_STATUS(env);
866 }
867
868 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)869 napi_define_class(napi_env env,
870 const char* utf8name,
871 size_t length,
872 napi_callback constructor,
873 void* callback_data,
874 size_t property_count,
875 const napi_property_descriptor* properties,
876 napi_value* result) {
877 NAPI_PREAMBLE(env);
878 CHECK_ARG(env, result);
879 CHECK_ARG(env, constructor);
880
881 if (property_count > 0) {
882 CHECK_ARG(env, properties);
883 }
884
885 v8::Isolate* isolate = env->isolate;
886
887 v8::EscapableHandleScope scope(isolate);
888 v8::Local<v8::FunctionTemplate> tpl;
889 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
890 env, constructor, callback_data, &tpl));
891
892 v8::Local<v8::String> name_string;
893 CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
894 tpl->SetClassName(name_string);
895
896 size_t static_property_count = 0;
897 for (size_t i = 0; i < property_count; i++) {
898 const napi_property_descriptor* p = properties + i;
899
900 if ((p->attributes & napi_static) != 0) {
901 // Static properties are handled separately below.
902 static_property_count++;
903 continue;
904 }
905
906 v8::Local<v8::Name> property_name;
907 STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name));
908
909 v8::PropertyAttribute attributes =
910 v8impl::V8PropertyAttributesFromDescriptor(p);
911
912 // This code is similar to that in napi_define_properties(); the
913 // difference is it applies to a template instead of an object,
914 // and preferred PropertyAttribute for lack of PropertyDescriptor
915 // support on ObjectTemplate.
916 if (p->getter != nullptr || p->setter != nullptr) {
917 v8::Local<v8::FunctionTemplate> getter_tpl;
918 v8::Local<v8::FunctionTemplate> setter_tpl;
919 if (p->getter != nullptr) {
920 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
921 env, p->getter, p->data, &getter_tpl));
922 }
923 if (p->setter != nullptr) {
924 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
925 env, p->setter, p->data, &setter_tpl));
926 }
927
928 tpl->PrototypeTemplate()->SetAccessorProperty(property_name,
929 getter_tpl,
930 setter_tpl,
931 attributes,
932 v8::AccessControl::DEFAULT);
933 } else if (p->method != nullptr) {
934 v8::Local<v8::FunctionTemplate> t;
935 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
936 env, p->method, p->data, &t, v8::Signature::New(isolate, tpl)));
937
938 tpl->PrototypeTemplate()->Set(property_name, t, attributes);
939 } else {
940 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
941 tpl->PrototypeTemplate()->Set(property_name, value, attributes);
942 }
943 }
944
945 v8::Local<v8::Context> context = env->context();
946 *result = v8impl::JsValueFromV8LocalValue(
947 scope.Escape(tpl->GetFunction(context).ToLocalChecked()));
948
949 if (static_property_count > 0) {
950 std::vector<napi_property_descriptor> static_descriptors;
951 static_descriptors.reserve(static_property_count);
952
953 for (size_t i = 0; i < property_count; i++) {
954 const napi_property_descriptor* p = properties + i;
955 if ((p->attributes & napi_static) != 0) {
956 static_descriptors.push_back(*p);
957 }
958 }
959
960 STATUS_CALL(napi_define_properties(
961 env, *result, static_descriptors.size(), static_descriptors.data()));
962 }
963
964 return GET_RETURN_STATUS(env);
965 }
966
napi_get_property_names(napi_env env,napi_value object,napi_value * result)967 napi_status NAPI_CDECL napi_get_property_names(napi_env env,
968 napi_value object,
969 napi_value* result) {
970 return napi_get_all_property_names(
971 env,
972 object,
973 napi_key_include_prototypes,
974 static_cast<napi_key_filter>(napi_key_enumerable | napi_key_skip_symbols),
975 napi_key_numbers_to_strings,
976 result);
977 }
978
979 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)980 napi_get_all_property_names(napi_env env,
981 napi_value object,
982 napi_key_collection_mode key_mode,
983 napi_key_filter key_filter,
984 napi_key_conversion key_conversion,
985 napi_value* result) {
986 NAPI_PREAMBLE(env);
987 CHECK_ARG(env, result);
988
989 v8::Local<v8::Context> context = env->context();
990 v8::Local<v8::Object> obj;
991 CHECK_TO_OBJECT(env, context, obj, object);
992
993 v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES;
994 if (key_filter & napi_key_writable) {
995 filter = static_cast<v8::PropertyFilter>(filter |
996 v8::PropertyFilter::ONLY_WRITABLE);
997 }
998 if (key_filter & napi_key_enumerable) {
999 filter = static_cast<v8::PropertyFilter>(
1000 filter | v8::PropertyFilter::ONLY_ENUMERABLE);
1001 }
1002 if (key_filter & napi_key_configurable) {
1003 filter = static_cast<v8::PropertyFilter>(
1004 filter | v8::PropertyFilter::ONLY_CONFIGURABLE);
1005 }
1006 if (key_filter & napi_key_skip_strings) {
1007 filter = static_cast<v8::PropertyFilter>(filter |
1008 v8::PropertyFilter::SKIP_STRINGS);
1009 }
1010 if (key_filter & napi_key_skip_symbols) {
1011 filter = static_cast<v8::PropertyFilter>(filter |
1012 v8::PropertyFilter::SKIP_SYMBOLS);
1013 }
1014 v8::KeyCollectionMode collection_mode;
1015 v8::KeyConversionMode conversion_mode;
1016
1017 switch (key_mode) {
1018 case napi_key_include_prototypes:
1019 collection_mode = v8::KeyCollectionMode::kIncludePrototypes;
1020 break;
1021 case napi_key_own_only:
1022 collection_mode = v8::KeyCollectionMode::kOwnOnly;
1023 break;
1024 default:
1025 return napi_set_last_error(env, napi_invalid_arg);
1026 }
1027
1028 switch (key_conversion) {
1029 case napi_key_keep_numbers:
1030 conversion_mode = v8::KeyConversionMode::kKeepNumbers;
1031 break;
1032 case napi_key_numbers_to_strings:
1033 conversion_mode = v8::KeyConversionMode::kConvertToString;
1034 break;
1035 default:
1036 return napi_set_last_error(env, napi_invalid_arg);
1037 }
1038
1039 v8::MaybeLocal<v8::Array> maybe_all_propertynames =
1040 obj->GetPropertyNames(context,
1041 collection_mode,
1042 filter,
1043 v8::IndexFilter::kIncludeIndices,
1044 conversion_mode);
1045
1046 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(
1047 env, maybe_all_propertynames, napi_generic_failure);
1048
1049 *result =
1050 v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked());
1051 return GET_RETURN_STATUS(env);
1052 }
1053
napi_set_property(napi_env env,napi_value object,napi_value key,napi_value value)1054 napi_status NAPI_CDECL napi_set_property(napi_env env,
1055 napi_value object,
1056 napi_value key,
1057 napi_value value) {
1058 NAPI_PREAMBLE(env);
1059 CHECK_ARG(env, key);
1060 CHECK_ARG(env, value);
1061
1062 v8::Local<v8::Context> context = env->context();
1063 v8::Local<v8::Object> obj;
1064
1065 CHECK_TO_OBJECT(env, context, obj, object);
1066
1067 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
1068 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
1069
1070 v8::Maybe<bool> set_maybe = obj->Set(context, k, val);
1071
1072 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure);
1073 return GET_RETURN_STATUS(env);
1074 }
1075
napi_has_property(napi_env env,napi_value object,napi_value key,bool * result)1076 napi_status NAPI_CDECL napi_has_property(napi_env env,
1077 napi_value object,
1078 napi_value key,
1079 bool* result) {
1080 NAPI_PREAMBLE(env);
1081 CHECK_ARG(env, result);
1082 CHECK_ARG(env, key);
1083
1084 v8::Local<v8::Context> context = env->context();
1085 v8::Local<v8::Object> obj;
1086
1087 CHECK_TO_OBJECT(env, context, obj, object);
1088
1089 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
1090 v8::Maybe<bool> has_maybe = obj->Has(context, k);
1091
1092 CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
1093
1094 *result = has_maybe.FromMaybe(false);
1095 return GET_RETURN_STATUS(env);
1096 }
1097
napi_get_property(napi_env env,napi_value object,napi_value key,napi_value * result)1098 napi_status NAPI_CDECL napi_get_property(napi_env env,
1099 napi_value object,
1100 napi_value key,
1101 napi_value* result) {
1102 NAPI_PREAMBLE(env);
1103 CHECK_ARG(env, key);
1104 CHECK_ARG(env, result);
1105
1106 v8::Local<v8::Context> context = env->context();
1107 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
1108 v8::Local<v8::Object> obj;
1109
1110 CHECK_TO_OBJECT(env, context, obj, object);
1111
1112 auto get_maybe = obj->Get(context, k);
1113
1114 CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure);
1115
1116 v8::Local<v8::Value> val = get_maybe.ToLocalChecked();
1117 *result = v8impl::JsValueFromV8LocalValue(val);
1118 return GET_RETURN_STATUS(env);
1119 }
1120
napi_delete_property(napi_env env,napi_value object,napi_value key,bool * result)1121 napi_status NAPI_CDECL napi_delete_property(napi_env env,
1122 napi_value object,
1123 napi_value key,
1124 bool* result) {
1125 NAPI_PREAMBLE(env);
1126 CHECK_ARG(env, key);
1127
1128 v8::Local<v8::Context> context = env->context();
1129 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
1130 v8::Local<v8::Object> obj;
1131
1132 CHECK_TO_OBJECT(env, context, obj, object);
1133 v8::Maybe<bool> delete_maybe = obj->Delete(context, k);
1134 CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure);
1135
1136 if (result != nullptr) *result = delete_maybe.FromMaybe(false);
1137
1138 return GET_RETURN_STATUS(env);
1139 }
1140
napi_has_own_property(napi_env env,napi_value object,napi_value key,bool * result)1141 napi_status NAPI_CDECL napi_has_own_property(napi_env env,
1142 napi_value object,
1143 napi_value key,
1144 bool* result) {
1145 NAPI_PREAMBLE(env);
1146 CHECK_ARG(env, key);
1147 CHECK_ARG(env, result);
1148
1149 v8::Local<v8::Context> context = env->context();
1150 v8::Local<v8::Object> obj;
1151
1152 CHECK_TO_OBJECT(env, context, obj, object);
1153 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
1154 RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected);
1155 v8::Maybe<bool> has_maybe = obj->HasOwnProperty(context, k.As<v8::Name>());
1156 CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
1157 *result = has_maybe.FromMaybe(false);
1158
1159 return GET_RETURN_STATUS(env);
1160 }
1161
napi_set_named_property(napi_env env,napi_value object,const char * utf8name,napi_value value)1162 napi_status NAPI_CDECL napi_set_named_property(napi_env env,
1163 napi_value object,
1164 const char* utf8name,
1165 napi_value value) {
1166 NAPI_PREAMBLE(env);
1167 CHECK_ARG(env, value);
1168
1169 v8::Local<v8::Context> context = env->context();
1170 v8::Local<v8::Object> obj;
1171
1172 CHECK_TO_OBJECT(env, context, obj, object);
1173
1174 v8::Local<v8::Name> key;
1175 CHECK_NEW_FROM_UTF8(env, key, utf8name);
1176
1177 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
1178
1179 v8::Maybe<bool> set_maybe = obj->Set(context, key, val);
1180
1181 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure);
1182 return GET_RETURN_STATUS(env);
1183 }
1184
napi_has_named_property(napi_env env,napi_value object,const char * utf8name,bool * result)1185 napi_status NAPI_CDECL napi_has_named_property(napi_env env,
1186 napi_value object,
1187 const char* utf8name,
1188 bool* result) {
1189 NAPI_PREAMBLE(env);
1190 CHECK_ARG(env, result);
1191
1192 v8::Local<v8::Context> context = env->context();
1193 v8::Local<v8::Object> obj;
1194
1195 CHECK_TO_OBJECT(env, context, obj, object);
1196
1197 v8::Local<v8::Name> key;
1198 CHECK_NEW_FROM_UTF8(env, key, utf8name);
1199
1200 v8::Maybe<bool> has_maybe = obj->Has(context, key);
1201
1202 CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
1203
1204 *result = has_maybe.FromMaybe(false);
1205 return GET_RETURN_STATUS(env);
1206 }
1207
napi_get_named_property(napi_env env,napi_value object,const char * utf8name,napi_value * result)1208 napi_status NAPI_CDECL napi_get_named_property(napi_env env,
1209 napi_value object,
1210 const char* utf8name,
1211 napi_value* result) {
1212 NAPI_PREAMBLE(env);
1213 CHECK_ARG(env, result);
1214
1215 v8::Local<v8::Context> context = env->context();
1216
1217 v8::Local<v8::Name> key;
1218 CHECK_NEW_FROM_UTF8(env, key, utf8name);
1219
1220 v8::Local<v8::Object> obj;
1221
1222 CHECK_TO_OBJECT(env, context, obj, object);
1223
1224 auto get_maybe = obj->Get(context, key);
1225
1226 CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure);
1227
1228 v8::Local<v8::Value> val = get_maybe.ToLocalChecked();
1229 *result = v8impl::JsValueFromV8LocalValue(val);
1230 return GET_RETURN_STATUS(env);
1231 }
1232
napi_set_element(napi_env env,napi_value object,uint32_t index,napi_value value)1233 napi_status NAPI_CDECL napi_set_element(napi_env env,
1234 napi_value object,
1235 uint32_t index,
1236 napi_value value) {
1237 NAPI_PREAMBLE(env);
1238 CHECK_ARG(env, value);
1239
1240 v8::Local<v8::Context> context = env->context();
1241 v8::Local<v8::Object> obj;
1242
1243 CHECK_TO_OBJECT(env, context, obj, object);
1244
1245 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
1246 auto set_maybe = obj->Set(context, index, val);
1247
1248 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure);
1249
1250 return GET_RETURN_STATUS(env);
1251 }
1252
napi_has_element(napi_env env,napi_value object,uint32_t index,bool * result)1253 napi_status NAPI_CDECL napi_has_element(napi_env env,
1254 napi_value object,
1255 uint32_t index,
1256 bool* result) {
1257 NAPI_PREAMBLE(env);
1258 CHECK_ARG(env, result);
1259
1260 v8::Local<v8::Context> context = env->context();
1261 v8::Local<v8::Object> obj;
1262
1263 CHECK_TO_OBJECT(env, context, obj, object);
1264
1265 v8::Maybe<bool> has_maybe = obj->Has(context, index);
1266
1267 CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
1268
1269 *result = has_maybe.FromMaybe(false);
1270 return GET_RETURN_STATUS(env);
1271 }
1272
napi_get_element(napi_env env,napi_value object,uint32_t index,napi_value * result)1273 napi_status NAPI_CDECL napi_get_element(napi_env env,
1274 napi_value object,
1275 uint32_t index,
1276 napi_value* result) {
1277 NAPI_PREAMBLE(env);
1278 CHECK_ARG(env, result);
1279
1280 v8::Local<v8::Context> context = env->context();
1281 v8::Local<v8::Object> obj;
1282
1283 CHECK_TO_OBJECT(env, context, obj, object);
1284
1285 auto get_maybe = obj->Get(context, index);
1286
1287 CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure);
1288
1289 *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked());
1290 return GET_RETURN_STATUS(env);
1291 }
1292
napi_delete_element(napi_env env,napi_value object,uint32_t index,bool * result)1293 napi_status NAPI_CDECL napi_delete_element(napi_env env,
1294 napi_value object,
1295 uint32_t index,
1296 bool* result) {
1297 NAPI_PREAMBLE(env);
1298
1299 v8::Local<v8::Context> context = env->context();
1300 v8::Local<v8::Object> obj;
1301
1302 CHECK_TO_OBJECT(env, context, obj, object);
1303 v8::Maybe<bool> delete_maybe = obj->Delete(context, index);
1304 CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure);
1305
1306 if (result != nullptr) *result = delete_maybe.FromMaybe(false);
1307
1308 return GET_RETURN_STATUS(env);
1309 }
1310
1311 napi_status NAPI_CDECL
napi_define_properties(napi_env env,napi_value object,size_t property_count,const napi_property_descriptor * properties)1312 napi_define_properties(napi_env env,
1313 napi_value object,
1314 size_t property_count,
1315 const napi_property_descriptor* properties) {
1316 NAPI_PREAMBLE(env);
1317 if (property_count > 0) {
1318 CHECK_ARG(env, properties);
1319 }
1320
1321 v8::Local<v8::Context> context = env->context();
1322
1323 v8::Local<v8::Object> obj;
1324 CHECK_TO_OBJECT(env, context, obj, object);
1325
1326 for (size_t i = 0; i < property_count; i++) {
1327 const napi_property_descriptor* p = &properties[i];
1328
1329 v8::Local<v8::Name> property_name;
1330 STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name));
1331
1332 if (p->getter != nullptr || p->setter != nullptr) {
1333 v8::Local<v8::Function> local_getter;
1334 v8::Local<v8::Function> local_setter;
1335
1336 if (p->getter != nullptr) {
1337 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
1338 env, p->getter, p->data, &local_getter));
1339 }
1340 if (p->setter != nullptr) {
1341 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
1342 env, p->setter, p->data, &local_setter));
1343 }
1344
1345 v8::PropertyDescriptor descriptor(local_getter, local_setter);
1346 descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1347 descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1348
1349 auto define_maybe =
1350 obj->DefineProperty(context, property_name, descriptor);
1351
1352 if (!define_maybe.FromMaybe(false)) {
1353 return napi_set_last_error(env, napi_invalid_arg);
1354 }
1355 } else if (p->method != nullptr) {
1356 v8::Local<v8::Function> method;
1357 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
1358 env, p->method, p->data, &method));
1359 v8::PropertyDescriptor descriptor(method,
1360 (p->attributes & napi_writable) != 0);
1361 descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1362 descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1363
1364 auto define_maybe =
1365 obj->DefineProperty(context, property_name, descriptor);
1366
1367 if (!define_maybe.FromMaybe(false)) {
1368 return napi_set_last_error(env, napi_generic_failure);
1369 }
1370 } else {
1371 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
1372 bool defined_successfully = false;
1373
1374 if ((p->attributes & napi_enumerable) &&
1375 (p->attributes & napi_writable) &&
1376 (p->attributes & napi_configurable)) {
1377 // Use a fast path for this type of data property.
1378 auto define_maybe =
1379 obj->CreateDataProperty(context, property_name, value);
1380 defined_successfully = define_maybe.FromMaybe(false);
1381 } else {
1382 v8::PropertyDescriptor descriptor(value,
1383 (p->attributes & napi_writable) != 0);
1384 descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1385 descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1386
1387 auto define_maybe =
1388 obj->DefineProperty(context, property_name, descriptor);
1389 defined_successfully = define_maybe.FromMaybe(false);
1390 }
1391
1392 if (!defined_successfully) {
1393 return napi_set_last_error(env, napi_invalid_arg);
1394 }
1395 }
1396 }
1397
1398 return GET_RETURN_STATUS(env);
1399 }
1400
napi_object_freeze(napi_env env,napi_value object)1401 napi_status NAPI_CDECL napi_object_freeze(napi_env env, napi_value object) {
1402 NAPI_PREAMBLE(env);
1403
1404 v8::Local<v8::Context> context = env->context();
1405 v8::Local<v8::Object> obj;
1406
1407 CHECK_TO_OBJECT(env, context, obj, object);
1408
1409 v8::Maybe<bool> set_frozen =
1410 obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
1411
1412 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
1413 env, set_frozen.FromMaybe(false), napi_generic_failure);
1414
1415 return GET_RETURN_STATUS(env);
1416 }
1417
napi_object_seal(napi_env env,napi_value object)1418 napi_status NAPI_CDECL napi_object_seal(napi_env env, napi_value object) {
1419 NAPI_PREAMBLE(env);
1420
1421 v8::Local<v8::Context> context = env->context();
1422 v8::Local<v8::Object> obj;
1423
1424 CHECK_TO_OBJECT(env, context, obj, object);
1425
1426 v8::Maybe<bool> set_sealed =
1427 obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed);
1428
1429 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
1430 env, set_sealed.FromMaybe(false), napi_generic_failure);
1431
1432 return GET_RETURN_STATUS(env);
1433 }
1434
napi_is_array(napi_env env,napi_value value,bool * result)1435 napi_status NAPI_CDECL napi_is_array(napi_env env,
1436 napi_value value,
1437 bool* result) {
1438 CHECK_ENV(env);
1439 CHECK_ARG(env, value);
1440 CHECK_ARG(env, result);
1441
1442 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
1443
1444 *result = val->IsArray();
1445 return napi_clear_last_error(env);
1446 }
1447
napi_get_array_length(napi_env env,napi_value value,uint32_t * result)1448 napi_status NAPI_CDECL napi_get_array_length(napi_env env,
1449 napi_value value,
1450 uint32_t* result) {
1451 NAPI_PREAMBLE(env);
1452 CHECK_ARG(env, value);
1453 CHECK_ARG(env, result);
1454
1455 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
1456 RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected);
1457
1458 v8::Local<v8::Array> arr = val.As<v8::Array>();
1459 *result = arr->Length();
1460
1461 return GET_RETURN_STATUS(env);
1462 }
1463
napi_strict_equals(napi_env env,napi_value lhs,napi_value rhs,bool * result)1464 napi_status NAPI_CDECL napi_strict_equals(napi_env env,
1465 napi_value lhs,
1466 napi_value rhs,
1467 bool* result) {
1468 NAPI_PREAMBLE(env);
1469 CHECK_ARG(env, lhs);
1470 CHECK_ARG(env, rhs);
1471 CHECK_ARG(env, result);
1472
1473 v8::Local<v8::Value> a = v8impl::V8LocalValueFromJsValue(lhs);
1474 v8::Local<v8::Value> b = v8impl::V8LocalValueFromJsValue(rhs);
1475
1476 *result = a->StrictEquals(b);
1477 return GET_RETURN_STATUS(env);
1478 }
1479
napi_get_prototype(napi_env env,napi_value object,napi_value * result)1480 napi_status NAPI_CDECL napi_get_prototype(napi_env env,
1481 napi_value object,
1482 napi_value* result) {
1483 NAPI_PREAMBLE(env);
1484 CHECK_ARG(env, result);
1485
1486 v8::Local<v8::Context> context = env->context();
1487
1488 v8::Local<v8::Object> obj;
1489 CHECK_TO_OBJECT(env, context, obj, object);
1490
1491 v8::Local<v8::Value> val = obj->GetPrototype();
1492 *result = v8impl::JsValueFromV8LocalValue(val);
1493 return GET_RETURN_STATUS(env);
1494 }
1495
napi_create_object(napi_env env,napi_value * result)1496 napi_status NAPI_CDECL napi_create_object(napi_env env, napi_value* result) {
1497 CHECK_ENV(env);
1498 CHECK_ARG(env, result);
1499
1500 *result = v8impl::JsValueFromV8LocalValue(v8::Object::New(env->isolate));
1501
1502 return napi_clear_last_error(env);
1503 }
1504
napi_create_array(napi_env env,napi_value * result)1505 napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) {
1506 CHECK_ENV(env);
1507 CHECK_ARG(env, result);
1508
1509 *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate));
1510
1511 return napi_clear_last_error(env);
1512 }
1513
napi_create_array_with_length(napi_env env,size_t length,napi_value * result)1514 napi_status NAPI_CDECL napi_create_array_with_length(napi_env env,
1515 size_t length,
1516 napi_value* result) {
1517 CHECK_ENV(env);
1518 CHECK_ARG(env, result);
1519
1520 *result =
1521 v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate, length));
1522
1523 return napi_clear_last_error(env);
1524 }
1525
napi_create_string_latin1(napi_env env,const char * str,size_t length,napi_value * result)1526 napi_status NAPI_CDECL napi_create_string_latin1(napi_env env,
1527 const char* str,
1528 size_t length,
1529 napi_value* result) {
1530 return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
1531 return v8::String::NewFromOneByte(isolate,
1532 reinterpret_cast<const uint8_t*>(str),
1533 v8::NewStringType::kNormal,
1534 length);
1535 });
1536 }
1537
napi_create_string_utf8(napi_env env,const char * str,size_t length,napi_value * result)1538 napi_status NAPI_CDECL napi_create_string_utf8(napi_env env,
1539 const char* str,
1540 size_t length,
1541 napi_value* result) {
1542 return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
1543 return v8::String::NewFromUtf8(
1544 isolate, str, v8::NewStringType::kNormal, static_cast<int>(length));
1545 });
1546 }
1547
napi_create_string_utf16(napi_env env,const char16_t * str,size_t length,napi_value * result)1548 napi_status NAPI_CDECL napi_create_string_utf16(napi_env env,
1549 const char16_t* str,
1550 size_t length,
1551 napi_value* result) {
1552 return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
1553 return v8::String::NewFromTwoByte(isolate,
1554 reinterpret_cast<const uint16_t*>(str),
1555 v8::NewStringType::kNormal,
1556 length);
1557 });
1558 }
1559
1560 napi_status NAPI_CDECL
node_api_create_external_string_latin1(napi_env env,char * str,size_t length,napi_finalize finalize_callback,void * finalize_hint,napi_value * result,bool * copied)1561 node_api_create_external_string_latin1(napi_env env,
1562 char* str,
1563 size_t length,
1564 napi_finalize finalize_callback,
1565 void* finalize_hint,
1566 napi_value* result,
1567 bool* copied) {
1568 return v8impl::NewExternalString(
1569 env,
1570 str,
1571 length,
1572 finalize_callback,
1573 finalize_hint,
1574 result,
1575 copied,
1576 napi_create_string_latin1,
1577 [&](v8::Isolate* isolate) {
1578 if (length == NAPI_AUTO_LENGTH) {
1579 length = (std::string_view(str)).length();
1580 }
1581 auto resource = new v8impl::ExternalOneByteStringResource(
1582 env, str, length, finalize_callback, finalize_hint);
1583 return v8::String::NewExternalOneByte(isolate, resource);
1584 });
1585 }
1586
1587 napi_status NAPI_CDECL
node_api_create_external_string_utf16(napi_env env,char16_t * str,size_t length,napi_finalize finalize_callback,void * finalize_hint,napi_value * result,bool * copied)1588 node_api_create_external_string_utf16(napi_env env,
1589 char16_t* str,
1590 size_t length,
1591 napi_finalize finalize_callback,
1592 void* finalize_hint,
1593 napi_value* result,
1594 bool* copied) {
1595 return v8impl::NewExternalString(
1596 env,
1597 str,
1598 length,
1599 finalize_callback,
1600 finalize_hint,
1601 result,
1602 copied,
1603 napi_create_string_utf16,
1604 [&](v8::Isolate* isolate) {
1605 if (length == NAPI_AUTO_LENGTH) {
1606 length = (std::u16string_view(str)).length();
1607 }
1608 auto resource = new v8impl::ExternalStringResource(
1609 env, str, length, finalize_callback, finalize_hint);
1610 return v8::String::NewExternalTwoByte(isolate, resource);
1611 });
1612 }
1613
napi_create_double(napi_env env,double value,napi_value * result)1614 napi_status NAPI_CDECL napi_create_double(napi_env env,
1615 double value,
1616 napi_value* result) {
1617 CHECK_ENV(env);
1618 CHECK_ARG(env, result);
1619
1620 *result =
1621 v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, value));
1622
1623 return napi_clear_last_error(env);
1624 }
1625
napi_create_int32(napi_env env,int32_t value,napi_value * result)1626 napi_status NAPI_CDECL napi_create_int32(napi_env env,
1627 int32_t value,
1628 napi_value* result) {
1629 CHECK_ENV(env);
1630 CHECK_ARG(env, result);
1631
1632 *result =
1633 v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value));
1634
1635 return napi_clear_last_error(env);
1636 }
1637
napi_create_uint32(napi_env env,uint32_t value,napi_value * result)1638 napi_status NAPI_CDECL napi_create_uint32(napi_env env,
1639 uint32_t value,
1640 napi_value* result) {
1641 CHECK_ENV(env);
1642 CHECK_ARG(env, result);
1643
1644 *result = v8impl::JsValueFromV8LocalValue(
1645 v8::Integer::NewFromUnsigned(env->isolate, value));
1646
1647 return napi_clear_last_error(env);
1648 }
1649
napi_create_int64(napi_env env,int64_t value,napi_value * result)1650 napi_status NAPI_CDECL napi_create_int64(napi_env env,
1651 int64_t value,
1652 napi_value* result) {
1653 CHECK_ENV(env);
1654 CHECK_ARG(env, result);
1655
1656 *result = v8impl::JsValueFromV8LocalValue(
1657 v8::Number::New(env->isolate, static_cast<double>(value)));
1658
1659 return napi_clear_last_error(env);
1660 }
1661
napi_create_bigint_int64(napi_env env,int64_t value,napi_value * result)1662 napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env,
1663 int64_t value,
1664 napi_value* result) {
1665 CHECK_ENV(env);
1666 CHECK_ARG(env, result);
1667
1668 *result =
1669 v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value));
1670
1671 return napi_clear_last_error(env);
1672 }
1673
napi_create_bigint_uint64(napi_env env,uint64_t value,napi_value * result)1674 napi_status NAPI_CDECL napi_create_bigint_uint64(napi_env env,
1675 uint64_t value,
1676 napi_value* result) {
1677 CHECK_ENV(env);
1678 CHECK_ARG(env, result);
1679
1680 *result = v8impl::JsValueFromV8LocalValue(
1681 v8::BigInt::NewFromUnsigned(env->isolate, value));
1682
1683 return napi_clear_last_error(env);
1684 }
1685
napi_create_bigint_words(napi_env env,int sign_bit,size_t word_count,const uint64_t * words,napi_value * result)1686 napi_status NAPI_CDECL napi_create_bigint_words(napi_env env,
1687 int sign_bit,
1688 size_t word_count,
1689 const uint64_t* words,
1690 napi_value* result) {
1691 NAPI_PREAMBLE(env);
1692 CHECK_ARG(env, words);
1693 CHECK_ARG(env, result);
1694
1695 v8::Local<v8::Context> context = env->context();
1696
1697 RETURN_STATUS_IF_FALSE(env, word_count <= INT_MAX, napi_invalid_arg);
1698
1699 v8::MaybeLocal<v8::BigInt> b =
1700 v8::BigInt::NewFromWords(context, sign_bit, word_count, words);
1701
1702 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, b, napi_generic_failure);
1703
1704 *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked());
1705 return GET_RETURN_STATUS(env);
1706 }
1707
napi_get_boolean(napi_env env,bool value,napi_value * result)1708 napi_status NAPI_CDECL napi_get_boolean(napi_env env,
1709 bool value,
1710 napi_value* result) {
1711 CHECK_ENV(env);
1712 CHECK_ARG(env, result);
1713
1714 v8::Isolate* isolate = env->isolate;
1715
1716 if (value) {
1717 *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate));
1718 } else {
1719 *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate));
1720 }
1721
1722 return napi_clear_last_error(env);
1723 }
1724
napi_create_symbol(napi_env env,napi_value description,napi_value * result)1725 napi_status NAPI_CDECL napi_create_symbol(napi_env env,
1726 napi_value description,
1727 napi_value* result) {
1728 CHECK_ENV(env);
1729 CHECK_ARG(env, result);
1730
1731 v8::Isolate* isolate = env->isolate;
1732
1733 if (description == nullptr) {
1734 *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate));
1735 } else {
1736 v8::Local<v8::Value> desc = v8impl::V8LocalValueFromJsValue(description);
1737 RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected);
1738
1739 *result = v8impl::JsValueFromV8LocalValue(
1740 v8::Symbol::New(isolate, desc.As<v8::String>()));
1741 }
1742
1743 return napi_clear_last_error(env);
1744 }
1745
node_api_symbol_for(napi_env env,const char * utf8description,size_t length,napi_value * result)1746 napi_status NAPI_CDECL node_api_symbol_for(napi_env env,
1747 const char* utf8description,
1748 size_t length,
1749 napi_value* result) {
1750 CHECK_ENV(env);
1751 CHECK_ARG(env, result);
1752
1753 napi_value js_description_string;
1754 STATUS_CALL(napi_create_string_utf8(
1755 env, utf8description, length, &js_description_string));
1756 v8::Local<v8::String> description_string =
1757 v8impl::V8LocalValueFromJsValue(js_description_string).As<v8::String>();
1758
1759 *result = v8impl::JsValueFromV8LocalValue(
1760 v8::Symbol::For(env->isolate, description_string));
1761
1762 return napi_clear_last_error(env);
1763 }
1764
set_error_code(napi_env env,v8::Local<v8::Value> error,napi_value code,const char * code_cstring)1765 static inline napi_status set_error_code(napi_env env,
1766 v8::Local<v8::Value> error,
1767 napi_value code,
1768 const char* code_cstring) {
1769 if ((code != nullptr) || (code_cstring != nullptr)) {
1770 v8::Local<v8::Context> context = env->context();
1771 v8::Local<v8::Object> err_object = error.As<v8::Object>();
1772
1773 v8::Local<v8::Value> code_value = v8impl::V8LocalValueFromJsValue(code);
1774 if (code != nullptr) {
1775 code_value = v8impl::V8LocalValueFromJsValue(code);
1776 RETURN_STATUS_IF_FALSE(env, code_value->IsString(), napi_string_expected);
1777 } else {
1778 CHECK_NEW_FROM_UTF8(env, code_value, code_cstring);
1779 }
1780
1781 v8::Local<v8::Name> code_key;
1782 CHECK_NEW_FROM_UTF8(env, code_key, "code");
1783
1784 v8::Maybe<bool> set_maybe = err_object->Set(context, code_key, code_value);
1785 RETURN_STATUS_IF_FALSE(
1786 env, set_maybe.FromMaybe(false), napi_generic_failure);
1787 }
1788 return napi_ok;
1789 }
1790
napi_create_error(napi_env env,napi_value code,napi_value msg,napi_value * result)1791 napi_status NAPI_CDECL napi_create_error(napi_env env,
1792 napi_value code,
1793 napi_value msg,
1794 napi_value* result) {
1795 CHECK_ENV(env);
1796 CHECK_ARG(env, msg);
1797 CHECK_ARG(env, result);
1798
1799 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
1800 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
1801
1802 v8::Local<v8::Value> error_obj =
1803 v8::Exception::Error(message_value.As<v8::String>());
1804 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
1805
1806 *result = v8impl::JsValueFromV8LocalValue(error_obj);
1807
1808 return napi_clear_last_error(env);
1809 }
1810
napi_create_type_error(napi_env env,napi_value code,napi_value msg,napi_value * result)1811 napi_status NAPI_CDECL napi_create_type_error(napi_env env,
1812 napi_value code,
1813 napi_value msg,
1814 napi_value* result) {
1815 CHECK_ENV(env);
1816 CHECK_ARG(env, msg);
1817 CHECK_ARG(env, result);
1818
1819 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
1820 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
1821
1822 v8::Local<v8::Value> error_obj =
1823 v8::Exception::TypeError(message_value.As<v8::String>());
1824 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
1825
1826 *result = v8impl::JsValueFromV8LocalValue(error_obj);
1827
1828 return napi_clear_last_error(env);
1829 }
1830
napi_create_range_error(napi_env env,napi_value code,napi_value msg,napi_value * result)1831 napi_status NAPI_CDECL napi_create_range_error(napi_env env,
1832 napi_value code,
1833 napi_value msg,
1834 napi_value* result) {
1835 CHECK_ENV(env);
1836 CHECK_ARG(env, msg);
1837 CHECK_ARG(env, result);
1838
1839 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
1840 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
1841
1842 v8::Local<v8::Value> error_obj =
1843 v8::Exception::RangeError(message_value.As<v8::String>());
1844 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
1845
1846 *result = v8impl::JsValueFromV8LocalValue(error_obj);
1847
1848 return napi_clear_last_error(env);
1849 }
1850
node_api_create_syntax_error(napi_env env,napi_value code,napi_value msg,napi_value * result)1851 napi_status NAPI_CDECL node_api_create_syntax_error(napi_env env,
1852 napi_value code,
1853 napi_value msg,
1854 napi_value* result) {
1855 CHECK_ENV(env);
1856 CHECK_ARG(env, msg);
1857 CHECK_ARG(env, result);
1858
1859 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
1860 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected);
1861
1862 v8::Local<v8::Value> error_obj =
1863 v8::Exception::SyntaxError(message_value.As<v8::String>());
1864 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
1865
1866 *result = v8impl::JsValueFromV8LocalValue(error_obj);
1867
1868 return napi_clear_last_error(env);
1869 }
1870
napi_typeof(napi_env env,napi_value value,napi_valuetype * result)1871 napi_status NAPI_CDECL napi_typeof(napi_env env,
1872 napi_value value,
1873 napi_valuetype* result) {
1874 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
1875 // JS exceptions.
1876 CHECK_ENV(env);
1877 CHECK_ARG(env, value);
1878 CHECK_ARG(env, result);
1879
1880 v8::Local<v8::Value> v = v8impl::V8LocalValueFromJsValue(value);
1881
1882 if (v->IsNumber()) {
1883 *result = napi_number;
1884 } else if (v->IsBigInt()) {
1885 *result = napi_bigint;
1886 } else if (v->IsString()) {
1887 *result = napi_string;
1888 } else if (v->IsFunction()) {
1889 // This test has to come before IsObject because IsFunction
1890 // implies IsObject
1891 *result = napi_function;
1892 } else if (v->IsExternal()) {
1893 // This test has to come before IsObject because IsExternal
1894 // implies IsObject
1895 *result = napi_external;
1896 } else if (v->IsObject()) {
1897 *result = napi_object;
1898 } else if (v->IsBoolean()) {
1899 *result = napi_boolean;
1900 } else if (v->IsUndefined()) {
1901 *result = napi_undefined;
1902 } else if (v->IsSymbol()) {
1903 *result = napi_symbol;
1904 } else if (v->IsNull()) {
1905 *result = napi_null;
1906 } else {
1907 // Should not get here unless V8 has added some new kind of value.
1908 return napi_set_last_error(env, napi_invalid_arg);
1909 }
1910
1911 return napi_clear_last_error(env);
1912 }
1913
napi_get_undefined(napi_env env,napi_value * result)1914 napi_status NAPI_CDECL napi_get_undefined(napi_env env, napi_value* result) {
1915 CHECK_ENV(env);
1916 CHECK_ARG(env, result);
1917
1918 *result = v8impl::JsValueFromV8LocalValue(v8::Undefined(env->isolate));
1919
1920 return napi_clear_last_error(env);
1921 }
1922
napi_get_null(napi_env env,napi_value * result)1923 napi_status NAPI_CDECL napi_get_null(napi_env env, napi_value* result) {
1924 CHECK_ENV(env);
1925 CHECK_ARG(env, result);
1926
1927 *result = v8impl::JsValueFromV8LocalValue(v8::Null(env->isolate));
1928
1929 return napi_clear_last_error(env);
1930 }
1931
1932 // Gets all callback info in a single call. (Ugly, but faster.)
napi_get_cb_info(napi_env env,napi_callback_info cbinfo,size_t * argc,napi_value * argv,napi_value * this_arg,void ** data)1933 napi_status NAPI_CDECL napi_get_cb_info(
1934 napi_env env, // [in] NAPI environment handle
1935 napi_callback_info cbinfo, // [in] Opaque callback-info handle
1936 size_t* argc, // [in-out] Specifies the size of the provided argv array
1937 // and receives the actual count of args.
1938 napi_value* argv, // [out] Array of values
1939 napi_value* this_arg, // [out] Receives the JS 'this' arg for the call
1940 void** data) { // [out] Receives the data pointer for the callback.
1941 CHECK_ENV(env);
1942 CHECK_ARG(env, cbinfo);
1943
1944 v8impl::CallbackWrapper* info =
1945 reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
1946
1947 if (argv != nullptr) {
1948 CHECK_ARG(env, argc);
1949 info->Args(argv, *argc);
1950 }
1951 if (argc != nullptr) {
1952 *argc = info->ArgsLength();
1953 }
1954 if (this_arg != nullptr) {
1955 *this_arg = info->This();
1956 }
1957 if (data != nullptr) {
1958 *data = info->Data();
1959 }
1960
1961 return napi_clear_last_error(env);
1962 }
1963
napi_get_new_target(napi_env env,napi_callback_info cbinfo,napi_value * result)1964 napi_status NAPI_CDECL napi_get_new_target(napi_env env,
1965 napi_callback_info cbinfo,
1966 napi_value* result) {
1967 CHECK_ENV(env);
1968 CHECK_ARG(env, cbinfo);
1969 CHECK_ARG(env, result);
1970
1971 v8impl::CallbackWrapper* info =
1972 reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
1973
1974 *result = info->GetNewTarget();
1975 return napi_clear_last_error(env);
1976 }
1977
napi_call_function(napi_env env,napi_value recv,napi_value func,size_t argc,const napi_value * argv,napi_value * result)1978 napi_status NAPI_CDECL napi_call_function(napi_env env,
1979 napi_value recv,
1980 napi_value func,
1981 size_t argc,
1982 const napi_value* argv,
1983 napi_value* result) {
1984 NAPI_PREAMBLE(env);
1985 CHECK_ARG(env, recv);
1986 if (argc > 0) {
1987 CHECK_ARG(env, argv);
1988 }
1989
1990 v8::Local<v8::Context> context = env->context();
1991
1992 v8::Local<v8::Value> v8recv = v8impl::V8LocalValueFromJsValue(recv);
1993
1994 v8::Local<v8::Function> v8func;
1995 CHECK_TO_FUNCTION(env, v8func, func);
1996
1997 auto maybe = v8func->Call(
1998 context,
1999 v8recv,
2000 argc,
2001 reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
2002
2003 if (try_catch.HasCaught()) {
2004 return napi_set_last_error(env, napi_pending_exception);
2005 } else {
2006 if (result != nullptr) {
2007 CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
2008 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
2009 }
2010 return napi_clear_last_error(env);
2011 }
2012 }
2013
napi_get_global(napi_env env,napi_value * result)2014 napi_status NAPI_CDECL napi_get_global(napi_env env, napi_value* result) {
2015 CHECK_ENV(env);
2016 CHECK_ARG(env, result);
2017
2018 *result = v8impl::JsValueFromV8LocalValue(env->context()->Global());
2019
2020 return napi_clear_last_error(env);
2021 }
2022
napi_throw(napi_env env,napi_value error)2023 napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error) {
2024 NAPI_PREAMBLE(env);
2025 CHECK_ARG(env, error);
2026
2027 v8::Isolate* isolate = env->isolate;
2028
2029 isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error));
2030 // any VM calls after this point and before returning
2031 // to the javascript invoker will fail
2032 return napi_clear_last_error(env);
2033 }
2034
napi_throw_error(napi_env env,const char * code,const char * msg)2035 napi_status NAPI_CDECL napi_throw_error(napi_env env,
2036 const char* code,
2037 const char* msg) {
2038 NAPI_PREAMBLE(env);
2039
2040 v8::Isolate* isolate = env->isolate;
2041 v8::Local<v8::String> str;
2042 CHECK_NEW_FROM_UTF8(env, str, msg);
2043
2044 v8::Local<v8::Value> error_obj = v8::Exception::Error(str);
2045 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
2046
2047 isolate->ThrowException(error_obj);
2048 // any VM calls after this point and before returning
2049 // to the javascript invoker will fail
2050 return napi_clear_last_error(env);
2051 }
2052
napi_throw_type_error(napi_env env,const char * code,const char * msg)2053 napi_status NAPI_CDECL napi_throw_type_error(napi_env env,
2054 const char* code,
2055 const char* msg) {
2056 NAPI_PREAMBLE(env);
2057
2058 v8::Isolate* isolate = env->isolate;
2059 v8::Local<v8::String> str;
2060 CHECK_NEW_FROM_UTF8(env, str, msg);
2061
2062 v8::Local<v8::Value> error_obj = v8::Exception::TypeError(str);
2063 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
2064
2065 isolate->ThrowException(error_obj);
2066 // any VM calls after this point and before returning
2067 // to the javascript invoker will fail
2068 return napi_clear_last_error(env);
2069 }
2070
napi_throw_range_error(napi_env env,const char * code,const char * msg)2071 napi_status NAPI_CDECL napi_throw_range_error(napi_env env,
2072 const char* code,
2073 const char* msg) {
2074 NAPI_PREAMBLE(env);
2075
2076 v8::Isolate* isolate = env->isolate;
2077 v8::Local<v8::String> str;
2078 CHECK_NEW_FROM_UTF8(env, str, msg);
2079
2080 v8::Local<v8::Value> error_obj = v8::Exception::RangeError(str);
2081 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
2082
2083 isolate->ThrowException(error_obj);
2084 // any VM calls after this point and before returning
2085 // to the javascript invoker will fail
2086 return napi_clear_last_error(env);
2087 }
2088
node_api_throw_syntax_error(napi_env env,const char * code,const char * msg)2089 napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env,
2090 const char* code,
2091 const char* msg) {
2092 NAPI_PREAMBLE(env);
2093
2094 v8::Isolate* isolate = env->isolate;
2095 v8::Local<v8::String> str;
2096 CHECK_NEW_FROM_UTF8(env, str, msg);
2097
2098 v8::Local<v8::Value> error_obj = v8::Exception::SyntaxError(str);
2099 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
2100
2101 isolate->ThrowException(error_obj);
2102 // any VM calls after this point and before returning
2103 // to the javascript invoker will fail
2104 return napi_clear_last_error(env);
2105 }
2106
napi_is_error(napi_env env,napi_value value,bool * result)2107 napi_status NAPI_CDECL napi_is_error(napi_env env,
2108 napi_value value,
2109 bool* result) {
2110 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot
2111 // throw JS exceptions.
2112 CHECK_ENV(env);
2113 CHECK_ARG(env, value);
2114 CHECK_ARG(env, result);
2115
2116 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2117 *result = val->IsNativeError();
2118
2119 return napi_clear_last_error(env);
2120 }
2121
napi_get_value_double(napi_env env,napi_value value,double * result)2122 napi_status NAPI_CDECL napi_get_value_double(napi_env env,
2123 napi_value value,
2124 double* result) {
2125 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2126 // JS exceptions.
2127 CHECK_ENV(env);
2128 CHECK_ARG(env, value);
2129 CHECK_ARG(env, result);
2130
2131 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2132 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
2133
2134 *result = val.As<v8::Number>()->Value();
2135
2136 return napi_clear_last_error(env);
2137 }
2138
napi_get_value_int32(napi_env env,napi_value value,int32_t * result)2139 napi_status NAPI_CDECL napi_get_value_int32(napi_env env,
2140 napi_value value,
2141 int32_t* result) {
2142 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2143 // JS exceptions.
2144 CHECK_ENV(env);
2145 CHECK_ARG(env, value);
2146 CHECK_ARG(env, result);
2147
2148 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2149
2150 if (val->IsInt32()) {
2151 *result = val.As<v8::Int32>()->Value();
2152 } else {
2153 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
2154
2155 // Empty context: https://github.com/nodejs/node/issues/14379
2156 v8::Local<v8::Context> context;
2157 *result = val->Int32Value(context).FromJust();
2158 }
2159
2160 return napi_clear_last_error(env);
2161 }
2162
napi_get_value_uint32(napi_env env,napi_value value,uint32_t * result)2163 napi_status NAPI_CDECL napi_get_value_uint32(napi_env env,
2164 napi_value value,
2165 uint32_t* result) {
2166 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2167 // JS exceptions.
2168 CHECK_ENV(env);
2169 CHECK_ARG(env, value);
2170 CHECK_ARG(env, result);
2171
2172 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2173
2174 if (val->IsUint32()) {
2175 *result = val.As<v8::Uint32>()->Value();
2176 } else {
2177 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
2178
2179 // Empty context: https://github.com/nodejs/node/issues/14379
2180 v8::Local<v8::Context> context;
2181 *result = val->Uint32Value(context).FromJust();
2182 }
2183
2184 return napi_clear_last_error(env);
2185 }
2186
napi_get_value_int64(napi_env env,napi_value value,int64_t * result)2187 napi_status NAPI_CDECL napi_get_value_int64(napi_env env,
2188 napi_value value,
2189 int64_t* result) {
2190 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2191 // JS exceptions.
2192 CHECK_ENV(env);
2193 CHECK_ARG(env, value);
2194 CHECK_ARG(env, result);
2195
2196 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2197
2198 // This is still a fast path very likely to be taken.
2199 if (val->IsInt32()) {
2200 *result = val.As<v8::Int32>()->Value();
2201 return napi_clear_last_error(env);
2202 }
2203
2204 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
2205
2206 // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN,
2207 // inconsistent with v8::Value::Int32Value() which converts those values to 0.
2208 // Special-case all non-finite values to match that behavior.
2209 double doubleValue = val.As<v8::Number>()->Value();
2210 if (std::isfinite(doubleValue)) {
2211 // Empty context: https://github.com/nodejs/node/issues/14379
2212 v8::Local<v8::Context> context;
2213 *result = val->IntegerValue(context).FromJust();
2214 } else {
2215 *result = 0;
2216 }
2217
2218 return napi_clear_last_error(env);
2219 }
2220
napi_get_value_bigint_int64(napi_env env,napi_value value,int64_t * result,bool * lossless)2221 napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env,
2222 napi_value value,
2223 int64_t* result,
2224 bool* lossless) {
2225 CHECK_ENV(env);
2226 CHECK_ARG(env, value);
2227 CHECK_ARG(env, result);
2228 CHECK_ARG(env, lossless);
2229
2230 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2231
2232 RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected);
2233
2234 *result = val.As<v8::BigInt>()->Int64Value(lossless);
2235
2236 return napi_clear_last_error(env);
2237 }
2238
napi_get_value_bigint_uint64(napi_env env,napi_value value,uint64_t * result,bool * lossless)2239 napi_status NAPI_CDECL napi_get_value_bigint_uint64(napi_env env,
2240 napi_value value,
2241 uint64_t* result,
2242 bool* lossless) {
2243 CHECK_ENV(env);
2244 CHECK_ARG(env, value);
2245 CHECK_ARG(env, result);
2246 CHECK_ARG(env, lossless);
2247
2248 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2249
2250 RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected);
2251
2252 *result = val.As<v8::BigInt>()->Uint64Value(lossless);
2253
2254 return napi_clear_last_error(env);
2255 }
2256
napi_get_value_bigint_words(napi_env env,napi_value value,int * sign_bit,size_t * word_count,uint64_t * words)2257 napi_status NAPI_CDECL napi_get_value_bigint_words(napi_env env,
2258 napi_value value,
2259 int* sign_bit,
2260 size_t* word_count,
2261 uint64_t* words) {
2262 CHECK_ENV(env);
2263 CHECK_ARG(env, value);
2264 CHECK_ARG(env, word_count);
2265
2266 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2267
2268 RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected);
2269
2270 v8::Local<v8::BigInt> big = val.As<v8::BigInt>();
2271
2272 int word_count_int = *word_count;
2273
2274 if (sign_bit == nullptr && words == nullptr) {
2275 word_count_int = big->WordCount();
2276 } else {
2277 CHECK_ARG(env, sign_bit);
2278 CHECK_ARG(env, words);
2279 big->ToWordsArray(sign_bit, &word_count_int, words);
2280 }
2281
2282 *word_count = word_count_int;
2283
2284 return napi_clear_last_error(env);
2285 }
2286
napi_get_value_bool(napi_env env,napi_value value,bool * result)2287 napi_status NAPI_CDECL napi_get_value_bool(napi_env env,
2288 napi_value value,
2289 bool* result) {
2290 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2291 // JS exceptions.
2292 CHECK_ENV(env);
2293 CHECK_ARG(env, value);
2294 CHECK_ARG(env, result);
2295
2296 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2297 RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), napi_boolean_expected);
2298
2299 *result = val.As<v8::Boolean>()->Value();
2300
2301 return napi_clear_last_error(env);
2302 }
2303
2304 // Copies a JavaScript string into a LATIN-1 string buffer. The result is the
2305 // number of bytes (excluding the null terminator) copied into buf.
2306 // A sufficient buffer size should be greater than the length of string,
2307 // reserving space for null terminator.
2308 // If bufsize is insufficient, the string will be truncated and null terminated.
2309 // If buf is NULL, this method returns the length of the string (in bytes)
2310 // via the result parameter.
2311 // The result argument is optional unless buf is NULL.
napi_get_value_string_latin1(napi_env env,napi_value value,char * buf,size_t bufsize,size_t * result)2312 napi_status NAPI_CDECL napi_get_value_string_latin1(
2313 napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) {
2314 CHECK_ENV(env);
2315 CHECK_ARG(env, value);
2316
2317 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2318 RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected);
2319
2320 if (!buf) {
2321 CHECK_ARG(env, result);
2322 *result = val.As<v8::String>()->Length();
2323 } else if (bufsize != 0) {
2324 int copied =
2325 val.As<v8::String>()->WriteOneByte(env->isolate,
2326 reinterpret_cast<uint8_t*>(buf),
2327 0,
2328 bufsize - 1,
2329 v8::String::NO_NULL_TERMINATION);
2330
2331 buf[copied] = '\0';
2332 if (result != nullptr) {
2333 *result = copied;
2334 }
2335 } else if (result != nullptr) {
2336 *result = 0;
2337 }
2338
2339 return napi_clear_last_error(env);
2340 }
2341
2342 // Copies a JavaScript string into a UTF-8 string buffer. The result is the
2343 // number of bytes (excluding the null terminator) copied into buf.
2344 // A sufficient buffer size should be greater than the length of string,
2345 // reserving space for null terminator.
2346 // If bufsize is insufficient, the string will be truncated and null terminated.
2347 // If buf is NULL, this method returns the length of the string (in bytes)
2348 // via the result parameter.
2349 // The result argument is optional unless buf is NULL.
napi_get_value_string_utf8(napi_env env,napi_value value,char * buf,size_t bufsize,size_t * result)2350 napi_status NAPI_CDECL napi_get_value_string_utf8(
2351 napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result) {
2352 CHECK_ENV(env);
2353 CHECK_ARG(env, value);
2354
2355 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2356 RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected);
2357
2358 if (!buf) {
2359 CHECK_ARG(env, result);
2360 *result = val.As<v8::String>()->Utf8Length(env->isolate);
2361 } else if (bufsize != 0) {
2362 int copied = val.As<v8::String>()->WriteUtf8(
2363 env->isolate,
2364 buf,
2365 bufsize - 1,
2366 nullptr,
2367 v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION);
2368
2369 buf[copied] = '\0';
2370 if (result != nullptr) {
2371 *result = copied;
2372 }
2373 } else if (result != nullptr) {
2374 *result = 0;
2375 }
2376
2377 return napi_clear_last_error(env);
2378 }
2379
2380 // Copies a JavaScript string into a UTF-16 string buffer. The result is the
2381 // number of 2-byte code units (excluding the null terminator) copied into buf.
2382 // A sufficient buffer size should be greater than the length of string,
2383 // reserving space for null terminator.
2384 // If bufsize is insufficient, the string will be truncated and null terminated.
2385 // If buf is NULL, this method returns the length of the string (in 2-byte
2386 // code units) via the result parameter.
2387 // The result argument is optional unless buf is NULL.
napi_get_value_string_utf16(napi_env env,napi_value value,char16_t * buf,size_t bufsize,size_t * result)2388 napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env,
2389 napi_value value,
2390 char16_t* buf,
2391 size_t bufsize,
2392 size_t* result) {
2393 CHECK_ENV(env);
2394 CHECK_ARG(env, value);
2395
2396 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2397 RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected);
2398
2399 if (!buf) {
2400 CHECK_ARG(env, result);
2401 // V8 assumes UTF-16 length is the same as the number of characters.
2402 *result = val.As<v8::String>()->Length();
2403 } else if (bufsize != 0) {
2404 int copied = val.As<v8::String>()->Write(env->isolate,
2405 reinterpret_cast<uint16_t*>(buf),
2406 0,
2407 bufsize - 1,
2408 v8::String::NO_NULL_TERMINATION);
2409
2410 buf[copied] = '\0';
2411 if (result != nullptr) {
2412 *result = copied;
2413 }
2414 } else if (result != nullptr) {
2415 *result = 0;
2416 }
2417
2418 return napi_clear_last_error(env);
2419 }
2420
napi_coerce_to_bool(napi_env env,napi_value value,napi_value * result)2421 napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env,
2422 napi_value value,
2423 napi_value* result) {
2424 NAPI_PREAMBLE(env);
2425 CHECK_ARG(env, value);
2426 CHECK_ARG(env, result);
2427
2428 v8::Isolate* isolate = env->isolate;
2429 v8::Local<v8::Boolean> b =
2430 v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate);
2431 *result = v8impl::JsValueFromV8LocalValue(b);
2432 return GET_RETURN_STATUS(env);
2433 }
2434
2435 #define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \
2436 napi_status NAPI_CDECL napi_coerce_to_##LowerCaseName( \
2437 napi_env env, napi_value value, napi_value* result) { \
2438 NAPI_PREAMBLE(env); \
2439 CHECK_ARG(env, value); \
2440 CHECK_ARG(env, result); \
2441 \
2442 v8::Local<v8::Context> context = env->context(); \
2443 v8::Local<v8::MixedCaseName> str; \
2444 \
2445 CHECK_TO_##UpperCaseName(env, context, str, value); \
2446 \
2447 *result = v8impl::JsValueFromV8LocalValue(str); \
2448 return GET_RETURN_STATUS(env); \
2449 }
2450
GEN_COERCE_FUNCTION(NUMBER,Number,number)2451 GEN_COERCE_FUNCTION(NUMBER, Number, number)
2452 GEN_COERCE_FUNCTION(OBJECT, Object, object)
2453 GEN_COERCE_FUNCTION(STRING, String, string)
2454
2455 #undef GEN_COERCE_FUNCTION
2456
2457 napi_status NAPI_CDECL napi_wrap(napi_env env,
2458 napi_value js_object,
2459 void* native_object,
2460 napi_finalize finalize_cb,
2461 void* finalize_hint,
2462 napi_ref* result) {
2463 return v8impl::Wrap(
2464 env, js_object, native_object, finalize_cb, finalize_hint, result);
2465 }
2466
napi_unwrap(napi_env env,napi_value obj,void ** result)2467 napi_status NAPI_CDECL napi_unwrap(napi_env env,
2468 napi_value obj,
2469 void** result) {
2470 return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap);
2471 }
2472
napi_remove_wrap(napi_env env,napi_value obj,void ** result)2473 napi_status NAPI_CDECL napi_remove_wrap(napi_env env,
2474 napi_value obj,
2475 void** result) {
2476 return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap);
2477 }
2478
napi_create_external(napi_env env,void * data,napi_finalize finalize_cb,void * finalize_hint,napi_value * result)2479 napi_status NAPI_CDECL napi_create_external(napi_env env,
2480 void* data,
2481 napi_finalize finalize_cb,
2482 void* finalize_hint,
2483 napi_value* result) {
2484 NAPI_PREAMBLE(env);
2485 CHECK_ARG(env, result);
2486
2487 v8::Isolate* isolate = env->isolate;
2488
2489 v8::Local<v8::Value> external_value = v8::External::New(isolate, data);
2490
2491 if (finalize_cb) {
2492 // The Reference object will delete itself after invoking the finalizer
2493 // callback.
2494 v8impl::Reference::New(env,
2495 external_value,
2496 0,
2497 v8impl::Ownership::kRuntime,
2498 finalize_cb,
2499 data,
2500 finalize_hint);
2501 }
2502
2503 *result = v8impl::JsValueFromV8LocalValue(external_value);
2504
2505 return napi_clear_last_error(env);
2506 }
2507
napi_type_tag_object(napi_env env,napi_value object,const napi_type_tag * type_tag)2508 napi_status NAPI_CDECL napi_type_tag_object(napi_env env,
2509 napi_value object,
2510 const napi_type_tag* type_tag) {
2511 NAPI_PREAMBLE(env);
2512 v8::Local<v8::Context> context = env->context();
2513 v8::Local<v8::Object> obj;
2514 CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object);
2515 CHECK_ARG_WITH_PREAMBLE(env, type_tag);
2516
2517 auto key = NAPI_PRIVATE_KEY(context, type_tag);
2518 auto maybe_has = obj->HasPrivate(context, key);
2519 CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_has, napi_generic_failure);
2520 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
2521 env, !maybe_has.FromJust(), napi_invalid_arg);
2522
2523 auto tag = v8::BigInt::NewFromWords(
2524 context, 0, 2, reinterpret_cast<const uint64_t*>(type_tag));
2525 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, tag, napi_generic_failure);
2526
2527 auto maybe_set = obj->SetPrivate(context, key, tag.ToLocalChecked());
2528 CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_set, napi_generic_failure);
2529 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
2530 env, maybe_set.FromJust(), napi_generic_failure);
2531
2532 return GET_RETURN_STATUS(env);
2533 }
2534
napi_check_object_type_tag(napi_env env,napi_value object,const napi_type_tag * type_tag,bool * result)2535 napi_status NAPI_CDECL napi_check_object_type_tag(napi_env env,
2536 napi_value object,
2537 const napi_type_tag* type_tag,
2538 bool* result) {
2539 NAPI_PREAMBLE(env);
2540 v8::Local<v8::Context> context = env->context();
2541 v8::Local<v8::Object> obj;
2542 CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object);
2543 CHECK_ARG_WITH_PREAMBLE(env, type_tag);
2544 CHECK_ARG_WITH_PREAMBLE(env, result);
2545
2546 auto maybe_value =
2547 obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, type_tag));
2548 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe_value, napi_generic_failure);
2549 v8::Local<v8::Value> val = maybe_value.ToLocalChecked();
2550
2551 // We consider the type check to have failed unless we reach the line below
2552 // where we set whether the type check succeeded or not based on the
2553 // comparison of the two type tags.
2554 *result = false;
2555 if (val->IsBigInt()) {
2556 int sign;
2557 int size = 2;
2558 napi_type_tag tag;
2559 val.As<v8::BigInt>()->ToWordsArray(
2560 &sign, &size, reinterpret_cast<uint64_t*>(&tag));
2561 if (sign == 0) {
2562 if (size == 2) {
2563 *result =
2564 (tag.lower == type_tag->lower && tag.upper == type_tag->upper);
2565 } else if (size == 1) {
2566 *result = (tag.lower == type_tag->lower && 0 == type_tag->upper);
2567 } else if (size == 0) {
2568 *result = (0 == type_tag->lower && 0 == type_tag->upper);
2569 }
2570 }
2571 }
2572
2573 return GET_RETURN_STATUS(env);
2574 }
2575
napi_get_value_external(napi_env env,napi_value value,void ** result)2576 napi_status NAPI_CDECL napi_get_value_external(napi_env env,
2577 napi_value value,
2578 void** result) {
2579 CHECK_ENV(env);
2580 CHECK_ARG(env, value);
2581 CHECK_ARG(env, result);
2582
2583 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2584 RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg);
2585
2586 v8::Local<v8::External> external_value = val.As<v8::External>();
2587 *result = external_value->Value();
2588
2589 return napi_clear_last_error(env);
2590 }
2591
2592 // Set initial_refcount to 0 for a weak reference, >0 for a strong reference.
napi_create_reference(napi_env env,napi_value value,uint32_t initial_refcount,napi_ref * result)2593 napi_status NAPI_CDECL napi_create_reference(napi_env env,
2594 napi_value value,
2595 uint32_t initial_refcount,
2596 napi_ref* result) {
2597 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2598 // JS exceptions.
2599 CHECK_ENV(env);
2600 CHECK_ARG(env, value);
2601 CHECK_ARG(env, result);
2602
2603 v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value);
2604 if (env->module_api_version != NAPI_VERSION_EXPERIMENTAL) {
2605 if (!(v8_value->IsObject() || v8_value->IsFunction() ||
2606 v8_value->IsSymbol())) {
2607 return napi_set_last_error(env, napi_invalid_arg);
2608 }
2609 }
2610
2611 v8impl::Reference* reference = v8impl::Reference::New(
2612 env, v8_value, initial_refcount, v8impl::Ownership::kUserland);
2613
2614 *result = reinterpret_cast<napi_ref>(reference);
2615 return napi_clear_last_error(env);
2616 }
2617
2618 // Deletes a reference. The referenced value is released, and may be GC'd unless
2619 // there are other references to it.
napi_delete_reference(napi_env env,napi_ref ref)2620 napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
2621 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2622 // JS exceptions.
2623 CHECK_ENV(env);
2624 CHECK_ARG(env, ref);
2625
2626 delete reinterpret_cast<v8impl::Reference*>(ref);
2627
2628 return napi_clear_last_error(env);
2629 }
2630
2631 // Increments the reference count, optionally returning the resulting count.
2632 // After this call the reference will be a strong reference because its
2633 // refcount is >0, and the referenced object is effectively "pinned".
2634 // Calling this when the refcount is 0 and the object is unavailable
2635 // results in an error.
napi_reference_ref(napi_env env,napi_ref ref,uint32_t * result)2636 napi_status NAPI_CDECL napi_reference_ref(napi_env env,
2637 napi_ref ref,
2638 uint32_t* result) {
2639 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2640 // JS exceptions.
2641 CHECK_ENV(env);
2642 CHECK_ARG(env, ref);
2643
2644 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
2645 uint32_t count = reference->Ref();
2646
2647 if (result != nullptr) {
2648 *result = count;
2649 }
2650
2651 return napi_clear_last_error(env);
2652 }
2653
2654 // Decrements the reference count, optionally returning the resulting count. If
2655 // the result is 0 the reference is now weak and the object may be GC'd at any
2656 // time if there are no other references. Calling this when the refcount is
2657 // already 0 results in an error.
napi_reference_unref(napi_env env,napi_ref ref,uint32_t * result)2658 napi_status NAPI_CDECL napi_reference_unref(napi_env env,
2659 napi_ref ref,
2660 uint32_t* result) {
2661 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2662 // JS exceptions.
2663 CHECK_ENV(env);
2664 CHECK_ARG(env, ref);
2665
2666 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
2667
2668 if (reference->RefCount() == 0) {
2669 return napi_set_last_error(env, napi_generic_failure);
2670 }
2671
2672 uint32_t count = reference->Unref();
2673
2674 if (result != nullptr) {
2675 *result = count;
2676 }
2677
2678 return napi_clear_last_error(env);
2679 }
2680
2681 // Attempts to get a referenced value. If the reference is weak, the value might
2682 // no longer be available, in that case the call is still successful but the
2683 // result is NULL.
napi_get_reference_value(napi_env env,napi_ref ref,napi_value * result)2684 napi_status NAPI_CDECL napi_get_reference_value(napi_env env,
2685 napi_ref ref,
2686 napi_value* result) {
2687 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2688 // JS exceptions.
2689 CHECK_ENV(env);
2690 CHECK_ARG(env, ref);
2691 CHECK_ARG(env, result);
2692
2693 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
2694 *result = v8impl::JsValueFromV8LocalValue(reference->Get());
2695
2696 return napi_clear_last_error(env);
2697 }
2698
napi_open_handle_scope(napi_env env,napi_handle_scope * result)2699 napi_status NAPI_CDECL napi_open_handle_scope(napi_env env,
2700 napi_handle_scope* result) {
2701 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2702 // JS exceptions.
2703 CHECK_ENV(env);
2704 CHECK_ARG(env, result);
2705
2706 *result = v8impl::JsHandleScopeFromV8HandleScope(
2707 new v8impl::HandleScopeWrapper(env->isolate));
2708 env->open_handle_scopes++;
2709 return napi_clear_last_error(env);
2710 }
2711
napi_close_handle_scope(napi_env env,napi_handle_scope scope)2712 napi_status NAPI_CDECL napi_close_handle_scope(napi_env env,
2713 napi_handle_scope scope) {
2714 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2715 // JS exceptions.
2716 CHECK_ENV(env);
2717 CHECK_ARG(env, scope);
2718 if (env->open_handle_scopes == 0) {
2719 return napi_handle_scope_mismatch;
2720 }
2721
2722 env->open_handle_scopes--;
2723 delete v8impl::V8HandleScopeFromJsHandleScope(scope);
2724 return napi_clear_last_error(env);
2725 }
2726
napi_open_escapable_handle_scope(napi_env env,napi_escapable_handle_scope * result)2727 napi_status NAPI_CDECL napi_open_escapable_handle_scope(
2728 napi_env env, napi_escapable_handle_scope* result) {
2729 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2730 // JS exceptions.
2731 CHECK_ENV(env);
2732 CHECK_ARG(env, result);
2733
2734 *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(
2735 new v8impl::EscapableHandleScopeWrapper(env->isolate));
2736 env->open_handle_scopes++;
2737 return napi_clear_last_error(env);
2738 }
2739
napi_close_escapable_handle_scope(napi_env env,napi_escapable_handle_scope scope)2740 napi_status NAPI_CDECL napi_close_escapable_handle_scope(
2741 napi_env env, napi_escapable_handle_scope scope) {
2742 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2743 // JS exceptions.
2744 CHECK_ENV(env);
2745 CHECK_ARG(env, scope);
2746 if (env->open_handle_scopes == 0) {
2747 return napi_handle_scope_mismatch;
2748 }
2749
2750 delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
2751 env->open_handle_scopes--;
2752 return napi_clear_last_error(env);
2753 }
2754
napi_escape_handle(napi_env env,napi_escapable_handle_scope scope,napi_value escapee,napi_value * result)2755 napi_status NAPI_CDECL napi_escape_handle(napi_env env,
2756 napi_escapable_handle_scope scope,
2757 napi_value escapee,
2758 napi_value* result) {
2759 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2760 // JS exceptions.
2761 CHECK_ENV(env);
2762 CHECK_ARG(env, scope);
2763 CHECK_ARG(env, escapee);
2764 CHECK_ARG(env, result);
2765
2766 v8impl::EscapableHandleScopeWrapper* s =
2767 v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
2768 if (!s->escape_called()) {
2769 *result = v8impl::JsValueFromV8LocalValue(
2770 s->Escape(v8impl::V8LocalValueFromJsValue(escapee)));
2771 return napi_clear_last_error(env);
2772 }
2773 return napi_set_last_error(env, napi_escape_called_twice);
2774 }
2775
napi_new_instance(napi_env env,napi_value constructor,size_t argc,const napi_value * argv,napi_value * result)2776 napi_status NAPI_CDECL napi_new_instance(napi_env env,
2777 napi_value constructor,
2778 size_t argc,
2779 const napi_value* argv,
2780 napi_value* result) {
2781 NAPI_PREAMBLE(env);
2782 CHECK_ARG(env, constructor);
2783 if (argc > 0) {
2784 CHECK_ARG(env, argv);
2785 }
2786 CHECK_ARG(env, result);
2787
2788 v8::Local<v8::Context> context = env->context();
2789
2790 v8::Local<v8::Function> ctor;
2791 CHECK_TO_FUNCTION(env, ctor, constructor);
2792
2793 auto maybe = ctor->NewInstance(
2794 context,
2795 argc,
2796 reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
2797
2798 CHECK_MAYBE_EMPTY(env, maybe, napi_pending_exception);
2799
2800 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
2801 return GET_RETURN_STATUS(env);
2802 }
2803
napi_instanceof(napi_env env,napi_value object,napi_value constructor,bool * result)2804 napi_status NAPI_CDECL napi_instanceof(napi_env env,
2805 napi_value object,
2806 napi_value constructor,
2807 bool* result) {
2808 NAPI_PREAMBLE(env);
2809 CHECK_ARG(env, object);
2810 CHECK_ARG(env, result);
2811
2812 *result = false;
2813
2814 v8::Local<v8::Object> ctor;
2815 v8::Local<v8::Context> context = env->context();
2816
2817 CHECK_TO_OBJECT(env, context, ctor, constructor);
2818
2819 if (!ctor->IsFunction()) {
2820 napi_throw_type_error(
2821 env, "ERR_NAPI_CONS_FUNCTION", "Constructor must be a function");
2822
2823 return napi_set_last_error(env, napi_function_expected);
2824 }
2825
2826 napi_status status = napi_generic_failure;
2827
2828 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(object);
2829 auto maybe_result = val->InstanceOf(context, ctor);
2830 CHECK_MAYBE_NOTHING(env, maybe_result, status);
2831 *result = maybe_result.FromJust();
2832 return GET_RETURN_STATUS(env);
2833 }
2834
2835 // Methods to support catching exceptions
napi_is_exception_pending(napi_env env,bool * result)2836 napi_status NAPI_CDECL napi_is_exception_pending(napi_env env, bool* result) {
2837 // NAPI_PREAMBLE is not used here: this function must execute when there is a
2838 // pending exception.
2839 CHECK_ENV(env);
2840 CHECK_ARG(env, result);
2841
2842 *result = !env->last_exception.IsEmpty();
2843 return napi_clear_last_error(env);
2844 }
2845
napi_get_and_clear_last_exception(napi_env env,napi_value * result)2846 napi_status NAPI_CDECL napi_get_and_clear_last_exception(napi_env env,
2847 napi_value* result) {
2848 // NAPI_PREAMBLE is not used here: this function must execute when there is a
2849 // pending exception.
2850 CHECK_ENV(env);
2851 CHECK_ARG(env, result);
2852
2853 if (env->last_exception.IsEmpty()) {
2854 return napi_get_undefined(env, result);
2855 } else {
2856 *result = v8impl::JsValueFromV8LocalValue(
2857 v8::Local<v8::Value>::New(env->isolate, env->last_exception));
2858 env->last_exception.Reset();
2859 }
2860
2861 return napi_clear_last_error(env);
2862 }
2863
napi_is_arraybuffer(napi_env env,napi_value value,bool * result)2864 napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env,
2865 napi_value value,
2866 bool* result) {
2867 CHECK_ENV(env);
2868 CHECK_ARG(env, value);
2869 CHECK_ARG(env, result);
2870
2871 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2872 *result = val->IsArrayBuffer();
2873
2874 return napi_clear_last_error(env);
2875 }
2876
napi_create_arraybuffer(napi_env env,size_t byte_length,void ** data,napi_value * result)2877 napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env,
2878 size_t byte_length,
2879 void** data,
2880 napi_value* result) {
2881 NAPI_PREAMBLE(env);
2882 CHECK_ARG(env, result);
2883
2884 v8::Isolate* isolate = env->isolate;
2885 v8::Local<v8::ArrayBuffer> buffer =
2886 v8::ArrayBuffer::New(isolate, byte_length);
2887
2888 // Optionally return a pointer to the buffer's data, to avoid another call to
2889 // retrieve it.
2890 if (data != nullptr) {
2891 *data = buffer->Data();
2892 }
2893
2894 *result = v8impl::JsValueFromV8LocalValue(buffer);
2895 return GET_RETURN_STATUS(env);
2896 }
2897
2898 napi_status NAPI_CDECL
napi_create_external_arraybuffer(napi_env env,void * external_data,size_t byte_length,napi_finalize finalize_cb,void * finalize_hint,napi_value * result)2899 napi_create_external_arraybuffer(napi_env env,
2900 void* external_data,
2901 size_t byte_length,
2902 napi_finalize finalize_cb,
2903 void* finalize_hint,
2904 napi_value* result) {
2905 // The API contract here is that the cleanup function runs on the JS thread,
2906 // and is able to use napi_env. Implementing that properly is hard, so use the
2907 // `Buffer` variant for easier implementation.
2908 napi_value buffer;
2909 STATUS_CALL(napi_create_external_buffer(
2910 env, byte_length, external_data, finalize_cb, finalize_hint, &buffer));
2911 return napi_get_typedarray_info(
2912 env, buffer, nullptr, nullptr, nullptr, result, nullptr);
2913 }
2914
napi_get_arraybuffer_info(napi_env env,napi_value arraybuffer,void ** data,size_t * byte_length)2915 napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env,
2916 napi_value arraybuffer,
2917 void** data,
2918 size_t* byte_length) {
2919 CHECK_ENV(env);
2920 CHECK_ARG(env, arraybuffer);
2921
2922 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
2923 RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
2924
2925 v8::Local<v8::ArrayBuffer> ab = value.As<v8::ArrayBuffer>();
2926
2927 if (data != nullptr) {
2928 *data = ab->Data();
2929 }
2930
2931 if (byte_length != nullptr) {
2932 *byte_length = ab->ByteLength();
2933 }
2934
2935 return napi_clear_last_error(env);
2936 }
2937
napi_is_typedarray(napi_env env,napi_value value,bool * result)2938 napi_status NAPI_CDECL napi_is_typedarray(napi_env env,
2939 napi_value value,
2940 bool* result) {
2941 CHECK_ENV(env);
2942 CHECK_ARG(env, value);
2943 CHECK_ARG(env, result);
2944
2945 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2946 *result = val->IsTypedArray();
2947
2948 return napi_clear_last_error(env);
2949 }
2950
napi_create_typedarray(napi_env env,napi_typedarray_type type,size_t length,napi_value arraybuffer,size_t byte_offset,napi_value * result)2951 napi_status NAPI_CDECL napi_create_typedarray(napi_env env,
2952 napi_typedarray_type type,
2953 size_t length,
2954 napi_value arraybuffer,
2955 size_t byte_offset,
2956 napi_value* result) {
2957 NAPI_PREAMBLE(env);
2958 CHECK_ARG(env, arraybuffer);
2959 CHECK_ARG(env, result);
2960
2961 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
2962 RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
2963
2964 v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
2965 v8::Local<v8::TypedArray> typedArray;
2966
2967 switch (type) {
2968 case napi_int8_array:
2969 CREATE_TYPED_ARRAY(
2970 env, Int8Array, 1, buffer, byte_offset, length, typedArray);
2971 break;
2972 case napi_uint8_array:
2973 CREATE_TYPED_ARRAY(
2974 env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
2975 break;
2976 case napi_uint8_clamped_array:
2977 CREATE_TYPED_ARRAY(
2978 env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
2979 break;
2980 case napi_int16_array:
2981 CREATE_TYPED_ARRAY(
2982 env, Int16Array, 2, buffer, byte_offset, length, typedArray);
2983 break;
2984 case napi_uint16_array:
2985 CREATE_TYPED_ARRAY(
2986 env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
2987 break;
2988 case napi_int32_array:
2989 CREATE_TYPED_ARRAY(
2990 env, Int32Array, 4, buffer, byte_offset, length, typedArray);
2991 break;
2992 case napi_uint32_array:
2993 CREATE_TYPED_ARRAY(
2994 env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
2995 break;
2996 case napi_float32_array:
2997 CREATE_TYPED_ARRAY(
2998 env, Float32Array, 4, buffer, byte_offset, length, typedArray);
2999 break;
3000 case napi_float64_array:
3001 CREATE_TYPED_ARRAY(
3002 env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3003 break;
3004 case napi_bigint64_array:
3005 CREATE_TYPED_ARRAY(
3006 env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3007 break;
3008 case napi_biguint64_array:
3009 CREATE_TYPED_ARRAY(
3010 env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3011 break;
3012 default:
3013 return napi_set_last_error(env, napi_invalid_arg);
3014 }
3015
3016 *result = v8impl::JsValueFromV8LocalValue(typedArray);
3017 return GET_RETURN_STATUS(env);
3018 }
3019
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)3020 napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env,
3021 napi_value typedarray,
3022 napi_typedarray_type* type,
3023 size_t* length,
3024 void** data,
3025 napi_value* arraybuffer,
3026 size_t* byte_offset) {
3027 CHECK_ENV(env);
3028 CHECK_ARG(env, typedarray);
3029
3030 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(typedarray);
3031 RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), napi_invalid_arg);
3032
3033 v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
3034
3035 if (type != nullptr) {
3036 if (value->IsInt8Array()) {
3037 *type = napi_int8_array;
3038 } else if (value->IsUint8Array()) {
3039 *type = napi_uint8_array;
3040 } else if (value->IsUint8ClampedArray()) {
3041 *type = napi_uint8_clamped_array;
3042 } else if (value->IsInt16Array()) {
3043 *type = napi_int16_array;
3044 } else if (value->IsUint16Array()) {
3045 *type = napi_uint16_array;
3046 } else if (value->IsInt32Array()) {
3047 *type = napi_int32_array;
3048 } else if (value->IsUint32Array()) {
3049 *type = napi_uint32_array;
3050 } else if (value->IsFloat32Array()) {
3051 *type = napi_float32_array;
3052 } else if (value->IsFloat64Array()) {
3053 *type = napi_float64_array;
3054 } else if (value->IsBigInt64Array()) {
3055 *type = napi_bigint64_array;
3056 } else if (value->IsBigUint64Array()) {
3057 *type = napi_biguint64_array;
3058 }
3059 }
3060
3061 if (length != nullptr) {
3062 *length = array->Length();
3063 }
3064
3065 v8::Local<v8::ArrayBuffer> buffer;
3066 if (data != nullptr || arraybuffer != nullptr) {
3067 // Calling Buffer() may have the side effect of allocating the buffer,
3068 // so only do this when it’s needed.
3069 buffer = array->Buffer();
3070 }
3071
3072 if (data != nullptr) {
3073 *data = static_cast<uint8_t*>(buffer->Data()) + array->ByteOffset();
3074 }
3075
3076 if (arraybuffer != nullptr) {
3077 *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer);
3078 }
3079
3080 if (byte_offset != nullptr) {
3081 *byte_offset = array->ByteOffset();
3082 }
3083
3084 return napi_clear_last_error(env);
3085 }
3086
napi_create_dataview(napi_env env,size_t byte_length,napi_value arraybuffer,size_t byte_offset,napi_value * result)3087 napi_status NAPI_CDECL napi_create_dataview(napi_env env,
3088 size_t byte_length,
3089 napi_value arraybuffer,
3090 size_t byte_offset,
3091 napi_value* result) {
3092 NAPI_PREAMBLE(env);
3093 CHECK_ARG(env, arraybuffer);
3094 CHECK_ARG(env, result);
3095
3096 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
3097 RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
3098
3099 v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
3100 if (byte_length + byte_offset > buffer->ByteLength()) {
3101 napi_throw_range_error(env,
3102 "ERR_NAPI_INVALID_DATAVIEW_ARGS",
3103 "byte_offset + byte_length should be less than or "
3104 "equal to the size in bytes of the array passed in");
3105 return napi_set_last_error(env, napi_pending_exception);
3106 }
3107 v8::Local<v8::DataView> DataView =
3108 v8::DataView::New(buffer, byte_offset, byte_length);
3109
3110 *result = v8impl::JsValueFromV8LocalValue(DataView);
3111 return GET_RETURN_STATUS(env);
3112 }
3113
napi_is_dataview(napi_env env,napi_value value,bool * result)3114 napi_status NAPI_CDECL napi_is_dataview(napi_env env,
3115 napi_value value,
3116 bool* result) {
3117 CHECK_ENV(env);
3118 CHECK_ARG(env, value);
3119 CHECK_ARG(env, result);
3120
3121 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3122 *result = val->IsDataView();
3123
3124 return napi_clear_last_error(env);
3125 }
3126
napi_get_dataview_info(napi_env env,napi_value dataview,size_t * byte_length,void ** data,napi_value * arraybuffer,size_t * byte_offset)3127 napi_status NAPI_CDECL napi_get_dataview_info(napi_env env,
3128 napi_value dataview,
3129 size_t* byte_length,
3130 void** data,
3131 napi_value* arraybuffer,
3132 size_t* byte_offset) {
3133 CHECK_ENV(env);
3134 CHECK_ARG(env, dataview);
3135
3136 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(dataview);
3137 RETURN_STATUS_IF_FALSE(env, value->IsDataView(), napi_invalid_arg);
3138
3139 v8::Local<v8::DataView> array = value.As<v8::DataView>();
3140
3141 if (byte_length != nullptr) {
3142 *byte_length = array->ByteLength();
3143 }
3144
3145 v8::Local<v8::ArrayBuffer> buffer;
3146 if (data != nullptr || arraybuffer != nullptr) {
3147 // Calling Buffer() may have the side effect of allocating the buffer,
3148 // so only do this when it’s needed.
3149 buffer = array->Buffer();
3150 }
3151
3152 if (data != nullptr) {
3153 *data = static_cast<uint8_t*>(buffer->Data()) + array->ByteOffset();
3154 }
3155
3156 if (arraybuffer != nullptr) {
3157 *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer);
3158 }
3159
3160 if (byte_offset != nullptr) {
3161 *byte_offset = array->ByteOffset();
3162 }
3163
3164 return napi_clear_last_error(env);
3165 }
3166
napi_get_version(napi_env env,uint32_t * result)3167 napi_status NAPI_CDECL napi_get_version(napi_env env, uint32_t* result) {
3168 CHECK_ENV(env);
3169 CHECK_ARG(env, result);
3170 *result = NAPI_VERSION;
3171 return napi_clear_last_error(env);
3172 }
3173
napi_create_promise(napi_env env,napi_deferred * deferred,napi_value * promise)3174 napi_status NAPI_CDECL napi_create_promise(napi_env env,
3175 napi_deferred* deferred,
3176 napi_value* promise) {
3177 NAPI_PREAMBLE(env);
3178 CHECK_ARG(env, deferred);
3179 CHECK_ARG(env, promise);
3180
3181 auto maybe = v8::Promise::Resolver::New(env->context());
3182 CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
3183
3184 auto v8_resolver = maybe.ToLocalChecked();
3185 auto v8_deferred = new v8impl::Persistent<v8::Value>();
3186 v8_deferred->Reset(env->isolate, v8_resolver);
3187
3188 *deferred = v8impl::JsDeferredFromNodePersistent(v8_deferred);
3189 *promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise());
3190 return GET_RETURN_STATUS(env);
3191 }
3192
napi_resolve_deferred(napi_env env,napi_deferred deferred,napi_value resolution)3193 napi_status NAPI_CDECL napi_resolve_deferred(napi_env env,
3194 napi_deferred deferred,
3195 napi_value resolution) {
3196 return v8impl::ConcludeDeferred(env, deferred, resolution, true);
3197 }
3198
napi_reject_deferred(napi_env env,napi_deferred deferred,napi_value resolution)3199 napi_status NAPI_CDECL napi_reject_deferred(napi_env env,
3200 napi_deferred deferred,
3201 napi_value resolution) {
3202 return v8impl::ConcludeDeferred(env, deferred, resolution, false);
3203 }
3204
napi_is_promise(napi_env env,napi_value value,bool * is_promise)3205 napi_status NAPI_CDECL napi_is_promise(napi_env env,
3206 napi_value value,
3207 bool* is_promise) {
3208 CHECK_ENV(env);
3209 CHECK_ARG(env, value);
3210 CHECK_ARG(env, is_promise);
3211
3212 *is_promise = v8impl::V8LocalValueFromJsValue(value)->IsPromise();
3213
3214 return napi_clear_last_error(env);
3215 }
3216
napi_create_date(napi_env env,double time,napi_value * result)3217 napi_status NAPI_CDECL napi_create_date(napi_env env,
3218 double time,
3219 napi_value* result) {
3220 NAPI_PREAMBLE(env);
3221 CHECK_ARG(env, result);
3222
3223 v8::MaybeLocal<v8::Value> maybe_date = v8::Date::New(env->context(), time);
3224 CHECK_MAYBE_EMPTY(env, maybe_date, napi_generic_failure);
3225
3226 *result = v8impl::JsValueFromV8LocalValue(maybe_date.ToLocalChecked());
3227
3228 return GET_RETURN_STATUS(env);
3229 }
3230
napi_is_date(napi_env env,napi_value value,bool * is_date)3231 napi_status NAPI_CDECL napi_is_date(napi_env env,
3232 napi_value value,
3233 bool* is_date) {
3234 CHECK_ENV(env);
3235 CHECK_ARG(env, value);
3236 CHECK_ARG(env, is_date);
3237
3238 *is_date = v8impl::V8LocalValueFromJsValue(value)->IsDate();
3239
3240 return napi_clear_last_error(env);
3241 }
3242
napi_get_date_value(napi_env env,napi_value value,double * result)3243 napi_status NAPI_CDECL napi_get_date_value(napi_env env,
3244 napi_value value,
3245 double* result) {
3246 NAPI_PREAMBLE(env);
3247 CHECK_ARG(env, value);
3248 CHECK_ARG(env, result);
3249
3250 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3251 RETURN_STATUS_IF_FALSE(env, val->IsDate(), napi_date_expected);
3252
3253 v8::Local<v8::Date> date = val.As<v8::Date>();
3254 *result = date->ValueOf();
3255
3256 return GET_RETURN_STATUS(env);
3257 }
3258
napi_run_script(napi_env env,napi_value script,napi_value * result)3259 napi_status NAPI_CDECL napi_run_script(napi_env env,
3260 napi_value script,
3261 napi_value* result) {
3262 NAPI_PREAMBLE(env);
3263 CHECK_ARG(env, script);
3264 CHECK_ARG(env, result);
3265
3266 v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
3267
3268 if (!v8_script->IsString()) {
3269 return napi_set_last_error(env, napi_string_expected);
3270 }
3271
3272 v8::Local<v8::Context> context = env->context();
3273
3274 auto maybe_script = v8::Script::Compile(context, v8_script.As<v8::String>());
3275 CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure);
3276
3277 auto script_result = maybe_script.ToLocalChecked()->Run(context);
3278 CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure);
3279
3280 *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked());
3281 return GET_RETURN_STATUS(env);
3282 }
3283
napi_add_finalizer(napi_env env,napi_value js_object,void * finalize_data,napi_finalize finalize_cb,void * finalize_hint,napi_ref * result)3284 napi_status NAPI_CDECL napi_add_finalizer(napi_env env,
3285 napi_value js_object,
3286 void* finalize_data,
3287 napi_finalize finalize_cb,
3288 void* finalize_hint,
3289 napi_ref* result) {
3290 // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3291 // JS exceptions.
3292 CHECK_ENV(env);
3293 CHECK_ARG(env, js_object);
3294 CHECK_ARG(env, finalize_cb);
3295
3296 v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(js_object);
3297 RETURN_STATUS_IF_FALSE(env, v8_value->IsObject(), napi_invalid_arg);
3298
3299 // Create a self-deleting reference if the optional out-param result is not
3300 // set.
3301 v8impl::Ownership ownership = result == nullptr
3302 ? v8impl::Ownership::kRuntime
3303 : v8impl::Ownership::kUserland;
3304 v8impl::Reference* reference = v8impl::Reference::New(
3305 env, v8_value, 0, ownership, finalize_cb, finalize_data, finalize_hint);
3306
3307 if (result != nullptr) {
3308 *result = reinterpret_cast<napi_ref>(reference);
3309 }
3310 return napi_clear_last_error(env);
3311 }
3312
napi_adjust_external_memory(napi_env env,int64_t change_in_bytes,int64_t * adjusted_value)3313 napi_status NAPI_CDECL napi_adjust_external_memory(napi_env env,
3314 int64_t change_in_bytes,
3315 int64_t* adjusted_value) {
3316 CHECK_ENV(env);
3317 CHECK_ARG(env, adjusted_value);
3318
3319 *adjusted_value =
3320 env->isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
3321
3322 return napi_clear_last_error(env);
3323 }
3324
napi_set_instance_data(napi_env env,void * data,napi_finalize finalize_cb,void * finalize_hint)3325 napi_status NAPI_CDECL napi_set_instance_data(napi_env env,
3326 void* data,
3327 napi_finalize finalize_cb,
3328 void* finalize_hint) {
3329 CHECK_ENV(env);
3330
3331 v8impl::RefBase* old_data = static_cast<v8impl::RefBase*>(env->instance_data);
3332 if (old_data != nullptr) {
3333 // Our contract so far has been to not finalize any old data there may be.
3334 // So we simply delete it.
3335 delete old_data;
3336 }
3337
3338 env->instance_data = v8impl::RefBase::New(
3339 env, 0, v8impl::Ownership::kRuntime, finalize_cb, data, finalize_hint);
3340
3341 return napi_clear_last_error(env);
3342 }
3343
napi_get_instance_data(napi_env env,void ** data)3344 napi_status NAPI_CDECL napi_get_instance_data(napi_env env, void** data) {
3345 CHECK_ENV(env);
3346 CHECK_ARG(env, data);
3347
3348 v8impl::RefBase* idata = static_cast<v8impl::RefBase*>(env->instance_data);
3349
3350 *data = (idata == nullptr ? nullptr : idata->Data());
3351
3352 return napi_clear_last_error(env);
3353 }
3354
napi_detach_arraybuffer(napi_env env,napi_value arraybuffer)3355 napi_status NAPI_CDECL napi_detach_arraybuffer(napi_env env,
3356 napi_value arraybuffer) {
3357 CHECK_ENV(env);
3358 CHECK_ARG(env, arraybuffer);
3359
3360 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
3361 RETURN_STATUS_IF_FALSE(
3362 env, value->IsArrayBuffer(), napi_arraybuffer_expected);
3363
3364 v8::Local<v8::ArrayBuffer> it = value.As<v8::ArrayBuffer>();
3365 RETURN_STATUS_IF_FALSE(
3366 env, it->IsDetachable(), napi_detachable_arraybuffer_expected);
3367
3368 it->Detach();
3369
3370 return napi_clear_last_error(env);
3371 }
3372
napi_is_detached_arraybuffer(napi_env env,napi_value arraybuffer,bool * result)3373 napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env,
3374 napi_value arraybuffer,
3375 bool* result) {
3376 CHECK_ENV(env);
3377 CHECK_ARG(env, arraybuffer);
3378 CHECK_ARG(env, result);
3379
3380 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
3381
3382 *result =
3383 value->IsArrayBuffer() && value.As<v8::ArrayBuffer>()->WasDetached();
3384
3385 return napi_clear_last_error(env);
3386 }
3387