summaryrefslogtreecommitdiff
path: root/src/node_api.cc
diff options
context:
space:
mode:
authorGabriel Schulhof <gabriel.schulhof@intel.com>2018-01-22 23:13:02 -0500
committerGabriel Schulhof <gabriel.schulhof@intel.com>2018-02-02 20:20:20 -0500
commit1286923cc05e3e7a726fc682be54212ccfc66812 (patch)
treeeffca948f249ea01059307db3a8c879a8c51da76 /src/node_api.cc
parentf0b2dd31cebceca42b0e41ba2ecf843b3578bf87 (diff)
downloadandroid-node-v8-1286923cc05e3e7a726fc682be54212ccfc66812.tar.gz
android-node-v8-1286923cc05e3e7a726fc682be54212ccfc66812.tar.bz2
android-node-v8-1286923cc05e3e7a726fc682be54212ccfc66812.zip
n-api: implement wrapping using private properties
PR-URL: https://github.com/nodejs/node/pull/18311 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Fixes: https://github.com/nodejs/node/issues/14367
Diffstat (limited to 'src/node_api.cc')
-rw-r--r--src/node_api.cc160
1 files changed, 50 insertions, 110 deletions
diff --git a/src/node_api.cc b/src/node_api.cc
index 27ab6707de..78841d1488 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -17,6 +17,7 @@
#include <vector>
#include "node_api.h"
#include "node_internals.h"
+#include "env.h"
static
napi_status napi_set_last_error(napi_env env, napi_status error_code,
@@ -46,6 +47,9 @@ struct napi_env__ {
uv_loop_t* loop = nullptr;
};
+#define NAPI_PRIVATE_KEY(context, suffix) \
+ (node::Environment::GetCurrent((context))->napi_ ## suffix())
+
#define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \
do { \
if ((env)->prefix ## _template.IsEmpty()) { \
@@ -373,6 +377,10 @@ class Reference : private Finalizer {
}
public:
+ void* Data() {
+ return _finalize_data;
+ }
+
static Reference* New(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
@@ -732,45 +740,6 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
return cbdata;
}
-int kWrapperFields = 3;
-
-// Pointer used to identify items wrapped by N-API. Used by FindWrapper and
-// napi_wrap().
-const char napi_wrap_name[] = "N-API Wrapper";
-
-// Search the object's prototype chain for the wrapper object. Usually the
-// wrapper would be the first in the chain, but it is OK for other objects to
-// be inserted in the prototype chain.
-static
-bool FindWrapper(v8::Local<v8::Object> obj,
- v8::Local<v8::Object>* result = nullptr,
- v8::Local<v8::Object>* parent = nullptr) {
- v8::Local<v8::Object> wrapper = obj;
-
- do {
- v8::Local<v8::Value> proto = wrapper->GetPrototype();
- if (proto.IsEmpty() || !proto->IsObject()) {
- return false;
- }
- if (parent != nullptr) {
- *parent = wrapper;
- }
- wrapper = proto.As<v8::Object>();
- if (wrapper->InternalFieldCount() == kWrapperFields) {
- v8::Local<v8::Value> external = wrapper->GetInternalField(1);
- if (external->IsExternal() &&
- external.As<v8::External>()->Value() == v8impl::napi_wrap_name) {
- break;
- }
- }
- } while (true);
-
- if (result != nullptr) {
- *result = wrapper;
- }
- return true;
-}
-
static void DeleteEnv(napi_env env, void* data, void* hint) {
delete env;
}
@@ -787,11 +756,8 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
// because we need to stop hard if either of them is empty.
//
// Re https://github.com/nodejs/node/pull/14217#discussion_r128775149
- auto key = v8::Private::ForApi(isolate,
- v8::String::NewFromOneByte(isolate,
- reinterpret_cast<const uint8_t*>("N-API Environment"),
- v8::NewStringType::kInternalized).ToLocalChecked());
- auto value = global->GetPrivate(context, key).ToLocalChecked();
+ auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env))
+ .ToLocalChecked();
if (value->IsExternal()) {
result = static_cast<napi_env>(value.As<v8::External>()->Value());
@@ -801,7 +767,8 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
// We must also stop hard if the result of assigning the env to the global
// is either nothing or false.
- CHECK(global->SetPrivate(context, key, external).FromJust());
+ CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external)
+ .FromJust());
// Create a self-destructing reference to external that will get rid of the
// napi_env when external goes out of scope.
@@ -811,28 +778,46 @@ napi_env GetEnv(v8::Local<v8::Context> context) {
return result;
}
+enum UnwrapAction {
+ KeepWrap,
+ RemoveWrap
+};
+
static
napi_status Unwrap(napi_env env,
napi_value js_object,
void** result,
- v8::Local<v8::Object>* wrapper,
- v8::Local<v8::Object>* parent = nullptr) {
+ UnwrapAction action) {
+ NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
- CHECK_ARG(env, result);
+ if (action == KeepWrap) {
+ CHECK_ARG(env, result);
+ }
+
+ v8::Isolate* isolate = env->isolate;
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object);
RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg);
v8::Local<v8::Object> obj = value.As<v8::Object>();
- RETURN_STATUS_IF_FALSE(
- env, v8impl::FindWrapper(obj, wrapper, parent), napi_invalid_arg);
+ auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
+ .ToLocalChecked();
+ RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg);
+ Reference* reference =
+ static_cast<v8impl::Reference*>(val.As<v8::External>()->Value());
- v8::Local<v8::Value> unwrappedValue = (*wrapper)->GetInternalField(0);
- RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg);
+ if (result) {
+ *result = reference->Data();
+ }
- *result = unwrappedValue.As<v8::External>()->Value();
+ if (action == RemoveWrap) {
+ CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
+ .FromJust());
+ Reference::Delete(reference);
+ }
- return napi_ok;
+ return GET_RETURN_STATUS(env);
}
static
@@ -2391,26 +2376,9 @@ napi_status napi_wrap(napi_env env,
v8::Local<v8::Object> obj = value.As<v8::Object>();
// If we've already wrapped this object, we error out.
- RETURN_STATUS_IF_FALSE(env, !v8impl::FindWrapper(obj), napi_invalid_arg);
-
- // Create a wrapper object with an internal field to hold the wrapped pointer
- // and a second internal field to identify the owner as N-API.
- v8::Local<v8::ObjectTemplate> wrapper_template;
- ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, v8impl::kWrapperFields);
-
- auto maybe_object = wrapper_template->NewInstance(context);
- CHECK_MAYBE_EMPTY(env, maybe_object, napi_generic_failure);
- v8::Local<v8::Object> wrapper = maybe_object.ToLocalChecked();
-
- // Store the pointer as an external in the wrapper.
- wrapper->SetInternalField(0, v8::External::New(isolate, native_object));
- wrapper->SetInternalField(1, v8::External::New(isolate,
- reinterpret_cast<void*>(const_cast<char*>(v8impl::napi_wrap_name))));
-
- // Insert the wrapper into the object's prototype chain.
- v8::Local<v8::Value> proto = obj->GetPrototype();
- CHECK(wrapper->SetPrototype(context, proto).FromJust());
- CHECK(obj->SetPrototype(context, wrapper).FromJust());
+ RETURN_STATUS_IF_FALSE(env,
+ !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).FromJust(),
+ napi_invalid_arg);
v8impl::Reference* reference = nullptr;
if (result != nullptr) {
@@ -2422,52 +2390,24 @@ napi_status napi_wrap(napi_env env,
reference = v8impl::Reference::New(
env, obj, 0, false, finalize_cb, native_object, finalize_hint);
*result = reinterpret_cast<napi_ref>(reference);
- } else if (finalize_cb != nullptr) {
- // Create a self-deleting reference just for the finalize callback.
- reference = v8impl::Reference::New(
- env, obj, 0, true, finalize_cb, native_object, finalize_hint);
+ } else {
+ // Create a self-deleting reference.
+ reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb,
+ native_object, finalize_cb == nullptr ? nullptr : finalize_hint);
}
- if (reference != nullptr) {
- wrapper->SetInternalField(2, v8::External::New(isolate, reference));
- }
+ CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper),
+ v8::External::New(isolate, reference)).FromJust());
return GET_RETURN_STATUS(env);
}
napi_status napi_unwrap(napi_env env, napi_value obj, void** result) {
- // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
- // JS exceptions.
- CHECK_ENV(env);
- v8::Local<v8::Object> wrapper;
- return napi_set_last_error(env, v8impl::Unwrap(env, obj, result, &wrapper));
+ return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap);
}
napi_status napi_remove_wrap(napi_env env, napi_value obj, void** result) {
- NAPI_PREAMBLE(env);
- v8::Local<v8::Object> wrapper;
- v8::Local<v8::Object> parent;
- napi_status status = v8impl::Unwrap(env, obj, result, &wrapper, &parent);
- if (status != napi_ok) {
- return napi_set_last_error(env, status);
- }
-
- v8::Local<v8::Value> external = wrapper->GetInternalField(2);
- if (external->IsExternal()) {
- v8impl::Reference::Delete(
- static_cast<v8impl::Reference*>(external.As<v8::External>()->Value()));
- }
-
- if (!parent.IsEmpty()) {
- v8::Maybe<bool> maybe = parent->SetPrototype(
- env->isolate->GetCurrentContext(), wrapper->GetPrototype());
- CHECK_MAYBE_NOTHING(env, maybe, napi_generic_failure);
- if (!maybe.FromMaybe(false)) {
- return napi_set_last_error(env, napi_generic_failure);
- }
- }
-
- return GET_RETURN_STATUS(env);
+ return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap);
}
napi_status napi_create_external(napi_env env,