1717#include < vector>
1818#include " node_api.h"
1919#include " node_internals.h"
20+ #include " env.h"
2021
2122static
2223napi_status napi_set_last_error (napi_env env, napi_status error_code,
@@ -46,6 +47,9 @@ struct napi_env__ {
4647 uv_loop_t * loop = nullptr ;
4748};
4849
50+ #define NAPI_PRIVATE_KEY (context, suffix ) \
51+ (node::Environment::GetCurrent((context))->napi_ ## suffix())
52+
4953#define ENV_OBJECT_TEMPLATE (env, prefix, destination, field_count ) \
5054 do { \
5155 if ((env)->prefix ## _template.IsEmpty ()) { \
@@ -373,6 +377,10 @@ class Reference : private Finalizer {
373377 }
374378
375379 public:
380+ void * Data () {
381+ return _finalize_data;
382+ }
383+
376384 static Reference* New (napi_env env,
377385 v8::Local<v8::Value> value,
378386 uint32_t initial_refcount,
@@ -732,45 +740,6 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
732740 return cbdata;
733741}
734742
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-
774743static void DeleteEnv (napi_env env, void * data, void * hint) {
775744 delete env;
776745}
@@ -787,11 +756,8 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
787756 // because we need to stop hard if either of them is empty.
788757 //
789758 // 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 ();
795761
796762 if (value->IsExternal ()) {
797763 result = static_cast <napi_env>(value.As <v8::External>()->Value ());
@@ -801,7 +767,8 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
801767
802768 // We must also stop hard if the result of assigning the env to the global
803769 // 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 ());
805772
806773 // Create a self-destructing reference to external that will get rid of the
807774 // napi_env when external goes out of scope.
@@ -811,28 +778,46 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
811778 return result;
812779}
813780
781+ enum UnwrapAction {
782+ KeepWrap,
783+ RemoveWrap
784+ };
785+
814786static
815787napi_status Unwrap (napi_env env,
816788 napi_value js_object,
817789 void ** result,
818- v8::Local<v8::Object>* wrapper,
819- v8::Local<v8::Object>* parent = nullptr ) {
790+ UnwrapAction action) {
791+ NAPI_PREAMBLE (env);
820792 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 ();
822799
823800 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue (js_object);
824801 RETURN_STATUS_IF_FALSE (env, value->IsObject (), napi_invalid_arg);
825802 v8::Local<v8::Object> obj = value.As <v8::Object>();
826803
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 ());
829809
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+ }
832813
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+ }
834819
835- return napi_ok ;
820+ return GET_RETURN_STATUS (env) ;
836821}
837822
838823static
@@ -2391,26 +2376,9 @@ napi_status napi_wrap(napi_env env,
23912376 v8::Local<v8::Object> obj = value.As <v8::Object>();
23922377
23932378 // 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);
24142382
24152383 v8impl::Reference* reference = nullptr ;
24162384 if (result != nullptr ) {
@@ -2422,52 +2390,24 @@ napi_status napi_wrap(napi_env env,
24222390 reference = v8impl::Reference::New (
24232391 env, obj, 0 , false , finalize_cb, native_object, finalize_hint);
24242392 *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);
24292397 }
24302398
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 ());
24342401
24352402 return GET_RETURN_STATUS (env);
24362403}
24372404
24382405napi_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);
24442407}
24452408
24462409napi_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);
24712411}
24722412
24732413napi_status napi_create_external (napi_env env,
0 commit comments