|
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, |
|