17
17
#include < vector>
18
18
#include " node_api.h"
19
19
#include " node_internals.h"
20
+ #include " env.h"
20
21
21
22
static
22
23
napi_status napi_set_last_error (napi_env env, napi_status error_code,
@@ -46,6 +47,9 @@ struct napi_env__ {
46
47
uv_loop_t * loop = nullptr ;
47
48
};
48
49
50
+ #define NAPI_PRIVATE_KEY (context, suffix ) \
51
+ (node::Environment::GetCurrent((context))->napi_ ## suffix())
52
+
49
53
#define ENV_OBJECT_TEMPLATE (env, prefix, destination, field_count ) \
50
54
do { \
51
55
if ((env)->prefix ## _template.IsEmpty ()) { \
@@ -373,6 +377,10 @@ class Reference : private Finalizer {
373
377
}
374
378
375
379
public:
380
+ void * Data () {
381
+ return _finalize_data;
382
+ }
383
+
376
384
static Reference* New (napi_env env,
377
385
v8::Local<v8::Value> value,
378
386
uint32_t initial_refcount,
@@ -732,45 +740,6 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
732
740
return cbdata;
733
741
}
734
742
735
- int kWrapperFields = 3 ;
736
-
737
- // Pointer used to identify items wrapped by N-API. Used by FindWrapper and
738
- // napi_wrap().
739
- const char napi_wrap_name[] = " N-API Wrapper" ;
740
-
741
- // Search the object's prototype chain for the wrapper object. Usually the
742
- // wrapper would be the first in the chain, but it is OK for other objects to
743
- // be inserted in the prototype chain.
744
- static
745
- bool FindWrapper (v8::Local<v8::Object> obj,
746
- v8::Local<v8::Object>* result = nullptr ,
747
- v8::Local<v8::Object>* parent = nullptr ) {
748
- v8::Local<v8::Object> wrapper = obj;
749
-
750
- do {
751
- v8::Local<v8::Value> proto = wrapper->GetPrototype ();
752
- if (proto.IsEmpty () || !proto->IsObject ()) {
753
- return false ;
754
- }
755
- if (parent != nullptr ) {
756
- *parent = wrapper;
757
- }
758
- wrapper = proto.As <v8::Object>();
759
- if (wrapper->InternalFieldCount () == kWrapperFields ) {
760
- v8::Local<v8::Value> external = wrapper->GetInternalField (1 );
761
- if (external->IsExternal () &&
762
- external.As <v8::External>()->Value () == v8impl::napi_wrap_name) {
763
- break ;
764
- }
765
- }
766
- } while (true );
767
-
768
- if (result != nullptr ) {
769
- *result = wrapper;
770
- }
771
- return true ;
772
- }
773
-
774
743
static void DeleteEnv (napi_env env, void * data, void * hint) {
775
744
delete env;
776
745
}
@@ -787,11 +756,8 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
787
756
// because we need to stop hard if either of them is empty.
788
757
//
789
758
// Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
790
- auto key = v8::Private::ForApi (isolate,
791
- v8::String::NewFromOneByte (isolate,
792
- reinterpret_cast <const uint8_t *>(" N-API Environment" ),
793
- v8::NewStringType::kInternalized ).ToLocalChecked ());
794
- auto value = global->GetPrivate (context, key).ToLocalChecked ();
759
+ auto value = global->GetPrivate (context, NAPI_PRIVATE_KEY (context, env))
760
+ .ToLocalChecked ();
795
761
796
762
if (value->IsExternal ()) {
797
763
result = static_cast <napi_env>(value.As <v8::External>()->Value ());
@@ -801,7 +767,8 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
801
767
802
768
// We must also stop hard if the result of assigning the env to the global
803
769
// is either nothing or false.
804
- CHECK (global->SetPrivate (context, key, external).FromJust ());
770
+ CHECK (global->SetPrivate (context, NAPI_PRIVATE_KEY (context, env), external)
771
+ .FromJust ());
805
772
806
773
// Create a self-destructing reference to external that will get rid of the
807
774
// napi_env when external goes out of scope.
@@ -811,28 +778,46 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
811
778
return result;
812
779
}
813
780
781
+ enum UnwrapAction {
782
+ KeepWrap,
783
+ RemoveWrap
784
+ };
785
+
814
786
static
815
787
napi_status Unwrap (napi_env env,
816
788
napi_value js_object,
817
789
void ** result,
818
- v8::Local<v8::Object>* wrapper,
819
- v8::Local<v8::Object>* parent = nullptr ) {
790
+ UnwrapAction action) {
791
+ NAPI_PREAMBLE (env);
820
792
CHECK_ARG (env, js_object);
821
- CHECK_ARG (env, result);
793
+ if (action == KeepWrap) {
794
+ CHECK_ARG (env, result);
795
+ }
796
+
797
+ v8::Isolate* isolate = env->isolate ;
798
+ v8::Local<v8::Context> context = isolate->GetCurrentContext ();
822
799
823
800
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue (js_object);
824
801
RETURN_STATUS_IF_FALSE (env, value->IsObject (), napi_invalid_arg);
825
802
v8::Local<v8::Object> obj = value.As <v8::Object>();
826
803
827
- RETURN_STATUS_IF_FALSE (
828
- env, v8impl::FindWrapper (obj, wrapper, parent), napi_invalid_arg);
804
+ auto val = obj->GetPrivate (context, NAPI_PRIVATE_KEY (context, wrapper))
805
+ .ToLocalChecked ();
806
+ RETURN_STATUS_IF_FALSE (env, val->IsExternal (), napi_invalid_arg);
807
+ Reference* reference =
808
+ static_cast <v8impl::Reference*>(val.As <v8::External>()->Value ());
829
809
830
- v8::Local<v8::Value> unwrappedValue = (*wrapper)->GetInternalField (0 );
831
- RETURN_STATUS_IF_FALSE (env, unwrappedValue->IsExternal (), napi_invalid_arg);
810
+ if (result) {
811
+ *result = reference->Data ();
812
+ }
832
813
833
- *result = unwrappedValue.As <v8::External>()->Value ();
814
+ if (action == RemoveWrap) {
815
+ CHECK (obj->DeletePrivate (context, NAPI_PRIVATE_KEY (context, wrapper))
816
+ .FromJust ());
817
+ Reference::Delete (reference);
818
+ }
834
819
835
- return napi_ok ;
820
+ return GET_RETURN_STATUS (env) ;
836
821
}
837
822
838
823
static
@@ -2391,26 +2376,9 @@ napi_status napi_wrap(napi_env env,
2391
2376
v8::Local<v8::Object> obj = value.As <v8::Object>();
2392
2377
2393
2378
// If we've already wrapped this object, we error out.
2394
- RETURN_STATUS_IF_FALSE (env, !v8impl::FindWrapper (obj), napi_invalid_arg);
2395
-
2396
- // Create a wrapper object with an internal field to hold the wrapped pointer
2397
- // and a second internal field to identify the owner as N-API.
2398
- v8::Local<v8::ObjectTemplate> wrapper_template;
2399
- ENV_OBJECT_TEMPLATE (env, wrap, wrapper_template, v8impl::kWrapperFields );
2400
-
2401
- auto maybe_object = wrapper_template->NewInstance (context);
2402
- CHECK_MAYBE_EMPTY (env, maybe_object, napi_generic_failure);
2403
- v8::Local<v8::Object> wrapper = maybe_object.ToLocalChecked ();
2404
-
2405
- // Store the pointer as an external in the wrapper.
2406
- wrapper->SetInternalField (0 , v8::External::New (isolate, native_object));
2407
- wrapper->SetInternalField (1 , v8::External::New (isolate,
2408
- reinterpret_cast <void *>(const_cast <char *>(v8impl::napi_wrap_name))));
2409
-
2410
- // Insert the wrapper into the object's prototype chain.
2411
- v8::Local<v8::Value> proto = obj->GetPrototype ();
2412
- CHECK (wrapper->SetPrototype (context, proto).FromJust ());
2413
- CHECK (obj->SetPrototype (context, wrapper).FromJust ());
2379
+ RETURN_STATUS_IF_FALSE (env,
2380
+ !obj->HasPrivate (context, NAPI_PRIVATE_KEY (context, wrapper)).FromJust (),
2381
+ napi_invalid_arg);
2414
2382
2415
2383
v8impl::Reference* reference = nullptr ;
2416
2384
if (result != nullptr ) {
@@ -2422,52 +2390,24 @@ napi_status napi_wrap(napi_env env,
2422
2390
reference = v8impl::Reference::New (
2423
2391
env, obj, 0 , false , finalize_cb, native_object, finalize_hint);
2424
2392
*result = reinterpret_cast <napi_ref>(reference);
2425
- } else if (finalize_cb != nullptr ) {
2426
- // Create a self-deleting reference just for the finalize callback .
2427
- reference = v8impl::Reference::New (
2428
- env, obj, 0 , true , finalize_cb, native_object, finalize_hint);
2393
+ } else {
2394
+ // Create a self-deleting reference.
2395
+ reference = v8impl::Reference::New (env, obj, 0 , true , finalize_cb,
2396
+ native_object, finalize_cb == nullptr ? nullptr : finalize_hint);
2429
2397
}
2430
2398
2431
- if (reference != nullptr ) {
2432
- wrapper->SetInternalField (2 , v8::External::New (isolate, reference));
2433
- }
2399
+ CHECK (obj->SetPrivate (context, NAPI_PRIVATE_KEY (context, wrapper),
2400
+ v8::External::New (isolate, reference)).FromJust ());
2434
2401
2435
2402
return GET_RETURN_STATUS (env);
2436
2403
}
2437
2404
2438
2405
napi_status napi_unwrap (napi_env env, napi_value obj, void ** result) {
2439
- // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
2440
- // JS exceptions.
2441
- CHECK_ENV (env);
2442
- v8::Local<v8::Object> wrapper;
2443
- return napi_set_last_error (env, v8impl::Unwrap (env, obj, result, &wrapper));
2406
+ return v8impl::Unwrap (env, obj, result, v8impl::KeepWrap);
2444
2407
}
2445
2408
2446
2409
napi_status napi_remove_wrap (napi_env env, napi_value obj, void ** result) {
2447
- NAPI_PREAMBLE (env);
2448
- v8::Local<v8::Object> wrapper;
2449
- v8::Local<v8::Object> parent;
2450
- napi_status status = v8impl::Unwrap (env, obj, result, &wrapper, &parent);
2451
- if (status != napi_ok) {
2452
- return napi_set_last_error (env, status);
2453
- }
2454
-
2455
- v8::Local<v8::Value> external = wrapper->GetInternalField (2 );
2456
- if (external->IsExternal ()) {
2457
- v8impl::Reference::Delete (
2458
- static_cast <v8impl::Reference*>(external.As <v8::External>()->Value ()));
2459
- }
2460
-
2461
- if (!parent.IsEmpty ()) {
2462
- v8::Maybe<bool > maybe = parent->SetPrototype (
2463
- env->isolate ->GetCurrentContext (), wrapper->GetPrototype ());
2464
- CHECK_MAYBE_NOTHING (env, maybe, napi_generic_failure);
2465
- if (!maybe.FromMaybe (false )) {
2466
- return napi_set_last_error (env, napi_generic_failure);
2467
- }
2468
- }
2469
-
2470
- return GET_RETURN_STATUS (env);
2410
+ return v8impl::Unwrap (env, obj, result, v8impl::RemoveWrap);
2471
2411
}
2472
2412
2473
2413
napi_status napi_create_external (napi_env env,
0 commit comments