diff options
author | Gabriel Schulhof <gabriel.schulhof@intel.com> | 2017-06-22 14:10:05 +0300 |
---|---|---|
committer | Michael Dawson <michael_dawson@ca.ibm.com> | 2017-07-12 12:05:44 -0400 |
commit | d5b397c9b63507a5dcf6d1da47df2a090d3d73a0 (patch) | |
tree | cd63dfde838dd4b7858b1e3a0e6c10da1029f5af /src/node_api.cc | |
parent | 3eae3103349b4fd5dc9a5d7a8b50374d6bf43c16 (diff) | |
download | android-node-v8-d5b397c9b63507a5dcf6d1da47df2a090d3d73a0.tar.gz android-node-v8-d5b397c9b63507a5dcf6d1da47df2a090d3d73a0.tar.bz2 android-node-v8-d5b397c9b63507a5dcf6d1da47df2a090d3d73a0.zip |
n-api: Implement stricter wrapping
Use a stronger criterion to identify objects in the prototype chain that store
pointers to native data that were added by previous calls to `napi_wrap()`.
Whereas the old criterion for identifying `napi_wrap()`-injected prototype
chain objects was to consider an object with an internal field
count of 1 to be such an object, the new criterion is to consider an object
with an internal field count of 2 such that the second field holds a
`v8::External` which itself contains a pointer to a global static string unique
to N-API to be a `napi_wrap()`-injected prototype chain object.
This greatly reduces the possibility of returning a pointer that was not
previously added with `napi_wrap()`, and it allows us to recognize that an
object has already undergone `napi_wrap()` and we can thus prevent a chain of
wrappers only the first of which is accessible from appearing in the prototype
chain, as would be the result of multiple calls to `napi_wrap()` using the same
object.
PR-URL: https://github.com/nodejs/node/pull/13872
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Diffstat (limited to 'src/node_api.cc')
-rw-r--r-- | src/node_api.cc | 64 |
1 files changed, 50 insertions, 14 deletions
diff --git a/src/node_api.cc b/src/node_api.cc index ffccff5b8e..becafc4684 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -673,6 +673,38 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env, return cbdata; } +// 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. +bool FindWrapper(v8::Local<v8::Object> obj, + v8::Local<v8::Object>* result = nullptr) { + v8::Local<v8::Object> wrapper = obj; + + do { + v8::Local<v8::Value> proto = wrapper->GetPrototype(); + if (proto.IsEmpty() || !proto->IsObject()) { + return false; + } + wrapper = proto.As<v8::Object>(); + if (wrapper->InternalFieldCount() == 2) { + 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; +} + } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters @@ -2046,11 +2078,22 @@ napi_status napi_wrap(napi_env env, RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); v8::Local<v8::Object> obj = value.As<v8::Object>(); - // Create a wrapper object with an internal field to hold the wrapped pointer. + // 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, 1); - v8::Local<v8::Object> wrapper = - wrapper_template->NewInstance(context).ToLocalChecked(); + ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, 2); + + auto maybe_object = wrapper_template->NewInstance(context); + CHECK_MAYBE_EMPTY(env, maybe_object, napi_generic_failure); + + v8::Local<v8::Object> wrapper = maybe_object.ToLocalChecked(); + wrapper->SetInternalField(1, v8::External::New(isolate, + reinterpret_cast<void*>(const_cast<char*>(v8impl::napi_wrap_name)))); + + // Store the pointer as an external in the wrapper. wrapper->SetInternalField(0, v8::External::New(isolate, native_object)); // Insert the wrapper into the object's prototype chain. @@ -2087,16 +2130,9 @@ napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) { RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); v8::Local<v8::Object> obj = value.As<v8::Object>(); - // Search the object's prototype chain for the wrapper with an internal field. - // Usually the wrapper would be the first in the chain, but it is OK for - // other objects to be inserted in the prototype chain. - v8::Local<v8::Object> wrapper = obj; - do { - v8::Local<v8::Value> proto = wrapper->GetPrototype(); - RETURN_STATUS_IF_FALSE( - env, !proto.IsEmpty() && proto->IsObject(), napi_invalid_arg); - wrapper = proto.As<v8::Object>(); - } while (wrapper->InternalFieldCount() != 1); + v8::Local<v8::Object> wrapper; + RETURN_STATUS_IF_FALSE( + env, v8impl::FindWrapper(obj, &wrapper), napi_invalid_arg); v8::Local<v8::Value> unwrappedValue = wrapper->GetInternalField(0); RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg); |