Skip to content

Commit

Permalink
Update files from node
Browse files Browse the repository at this point in the history
* Added `node_internals.cc/h` to shim missing internal functions
* Added `util.h` and `util-inl.h` for incoming changes to error helpers

PR-URL: #79
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Jason Ginchereau <[email protected]>
  • Loading branch information
kfarnung authored and mhdawson committed Jul 14, 2017
1 parent 10ef293 commit 92d14c0
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 29 deletions.
209 changes: 182 additions & 27 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#include <vector>
#include "uv.h"
#include "node_api.h"
#include "node_internals.h"

#define NAPI_VERSION 1

static
napi_status napi_set_last_error(napi_env env, napi_status error_code,
Expand All @@ -31,14 +34,33 @@ struct napi_env__ {
~napi_env__() {
last_exception.Reset();
has_instance.Reset();
wrap_template.Reset();
function_data_template.Reset();
accessor_data_template.Reset();
}
v8::Isolate* isolate;
v8::Persistent<v8::Value> last_exception;
v8::Persistent<v8::Value> has_instance;
v8::Persistent<v8::ObjectTemplate> wrap_template;
v8::Persistent<v8::ObjectTemplate> function_data_template;
v8::Persistent<v8::ObjectTemplate> accessor_data_template;
bool has_instance_available;
napi_extended_error_info last_error;
};

#define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \
do { \
if ((env)->prefix ## _template.IsEmpty()) { \
(destination) = v8::ObjectTemplate::New(isolate); \
(destination)->SetInternalFieldCount((field_count)); \
(env)->prefix ## _template.Reset(isolate, (destination)); \
} else { \
(destination) = v8::Local<v8::ObjectTemplate>::New( \
isolate, env->prefix ## _template); \
} \
} while (0)


#define RETURN_STATUS_IF_FALSE(env, condition, status) \
do { \
if (!(condition)) { \
Expand Down Expand Up @@ -154,14 +176,20 @@ class HandleScopeWrapper {
// across different versions.
class EscapableHandleScopeWrapper {
public:
explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {}
explicit EscapableHandleScopeWrapper(v8::Isolate* isolate)
: scope(isolate), escape_called_(false) {}
bool escape_called() const {
return escape_called_;
}
template <typename T>
v8::Local<T> Escape(v8::Local<T> handle) {
escape_called_ = true;
return scope.Escape(handle);
}

private:
v8::EscapableHandleScope scope;
bool escape_called_;
};

napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) {
Expand Down Expand Up @@ -594,8 +622,8 @@ v8::Local<v8::Object> CreateFunctionCallbackData(napi_env env,
v8::Isolate* isolate = env->isolate;
v8::Local<v8::Context> context = isolate->GetCurrentContext();

v8::Local<v8::ObjectTemplate> otpl = v8::ObjectTemplate::New(isolate);
otpl->SetInternalFieldCount(v8impl::kFunctionFieldCount);
v8::Local<v8::ObjectTemplate> otpl;
ENV_OBJECT_TEMPLATE(env, function_data, otpl, v8impl::kFunctionFieldCount);
v8::Local<v8::Object> cbdata = otpl->NewInstance(context).ToLocalChecked();

cbdata->SetInternalField(
Expand All @@ -620,8 +648,8 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
v8::Isolate* isolate = env->isolate;
v8::Local<v8::Context> context = isolate->GetCurrentContext();

v8::Local<v8::ObjectTemplate> otpl = v8::ObjectTemplate::New(isolate);
otpl->SetInternalFieldCount(v8impl::kAccessorFieldCount);
v8::Local<v8::ObjectTemplate> otpl;
ENV_OBJECT_TEMPLATE(env, accessor_data, otpl, v8impl::kAccessorFieldCount);
v8::Local<v8::Object> cbdata = otpl->NewInstance(context).ToLocalChecked();

cbdata->SetInternalField(
Expand All @@ -646,6 +674,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
Expand Down Expand Up @@ -716,7 +776,8 @@ const char* error_messages[] = {nullptr,
"An array was expected",
"Unknown failure",
"An exception is pending",
"The async work item was cancelled"};
"The async work item was cancelled",
"napi_escape_handle already called on scope"};

static napi_status napi_clear_last_error(napi_env env) {
CHECK_ENV(env);
Expand Down Expand Up @@ -744,10 +805,14 @@ napi_status napi_get_last_error_info(napi_env env,
CHECK_ENV(env);
CHECK_ARG(env, result);

// you must update this assert to reference the last message
// in the napi_status enum each time a new error message is added.
// We don't have a napi_status_last as this would result in an ABI
// change each time a message was added.
static_assert(
(sizeof (error_messages) / sizeof (*error_messages)) == napi_status_last,
node::arraysize(error_messages) == napi_escape_called_twice + 1,
"Count of error messages must match count of error values");
assert(env->last_error.error_code < napi_status_last);
assert(env->last_error.error_code <= napi_escape_called_twice);

// Wait until someone requests the last error information to fetch the error
// message string
Expand Down Expand Up @@ -817,9 +882,6 @@ napi_status napi_define_class(napi_env env,
v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(
isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);

// we need an internal field to stash the wrapped object
tpl->InstanceTemplate()->SetInternalFieldCount(1);

v8::Local<v8::String> name_string;
CHECK_NEW_FROM_UTF8(env, name_string, utf8name);
tpl->SetClassName(name_string);
Expand Down Expand Up @@ -991,6 +1053,49 @@ napi_status napi_get_property(napi_env env,
return GET_RETURN_STATUS(env);
}

napi_status napi_delete_property(napi_env env,
napi_value object,
napi_value key,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, key);

v8::Isolate* isolate = env->isolate;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
v8::Local<v8::Object> obj;

CHECK_TO_OBJECT(env, context, obj, object);
v8::Maybe<bool> delete_maybe = obj->Delete(context, k);
CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure);

if (result != NULL)
*result = delete_maybe.FromMaybe(false);

return GET_RETURN_STATUS(env);
}

NAPI_EXTERN napi_status napi_has_own_property(napi_env env,
napi_value object,
napi_value key,
bool* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, key);

v8::Isolate* isolate = env->isolate;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> obj;

CHECK_TO_OBJECT(env, context, obj, object);
v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
RETURN_STATUS_IF_FALSE(env, k->IsName(), napi_name_expected);
v8::Maybe<bool> has_maybe = obj->HasOwnProperty(context, k.As<v8::Name>());
CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure);
*result = has_maybe.FromMaybe(false);

return GET_RETURN_STATUS(env);
}

napi_status napi_set_named_property(napi_env env,
napi_value object,
const char* utf8name,
Expand Down Expand Up @@ -1128,6 +1233,26 @@ napi_status napi_get_element(napi_env env,
return GET_RETURN_STATUS(env);
}

napi_status napi_delete_element(napi_env env,
napi_value object,
uint32_t index,
bool* result) {
NAPI_PREAMBLE(env);

v8::Isolate* isolate = env->isolate;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> obj;

CHECK_TO_OBJECT(env, context, obj, object);
v8::Maybe<bool> delete_maybe = obj->Delete(context, index);
CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure);

if (result != NULL)
*result = delete_maybe.FromMaybe(false);

return GET_RETURN_STATUS(env);
}

napi_status napi_define_properties(napi_env env,
napi_value object,
size_t property_count,
Expand Down Expand Up @@ -1948,14 +2073,34 @@ napi_status napi_wrap(napi_env env,
CHECK_ARG(env, js_object);

v8::Isolate* isolate = env->isolate;
v8::Local<v8::Object> obj =
v8impl::V8LocalValueFromJsValue(js_object).As<v8::Object>();
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>();

// Only objects that were created from a NAPI constructor's prototype
// via napi_define_class() can be (un)wrapped.
RETURN_STATUS_IF_FALSE(env, obj->InternalFieldCount() > 0, napi_invalid_arg);
// If we've already wrapped this object, we error out.
RETURN_STATUS_IF_FALSE(env, !v8impl::FindWrapper(obj), napi_invalid_arg);

obj->SetInternalField(0, v8::External::New(isolate, native_object));
// 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, 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.
v8::Local<v8::Value> proto = obj->GetPrototype();
CHECK(wrapper->SetPrototype(context, proto).FromJust());
CHECK(obj->SetPrototype(context, wrapper).FromJust());

if (result != nullptr) {
// The returned reference should be deleted via napi_delete_reference()
Expand Down Expand Up @@ -1986,11 +2131,11 @@ 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>();

// Only objects that were created from a NAPI constructor's prototype
// via napi_define_class() can be (un)wrapped.
RETURN_STATUS_IF_FALSE(env, obj->InternalFieldCount() > 0, napi_invalid_arg);
v8::Local<v8::Object> wrapper;
RETURN_STATUS_IF_FALSE(
env, v8impl::FindWrapper(obj, &wrapper), napi_invalid_arg);

v8::Local<v8::Value> unwrappedValue = obj->GetInternalField(0);
v8::Local<v8::Value> unwrappedValue = wrapper->GetInternalField(0);
RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg);

*result = unwrappedValue.As<v8::External>()->Value();
Expand Down Expand Up @@ -2195,9 +2340,12 @@ napi_status napi_escape_handle(napi_env env,

v8impl::EscapableHandleScopeWrapper* s =
v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
*result = v8impl::JsValueFromV8LocalValue(
s->Escape(v8impl::V8LocalValueFromJsValue(escapee)));
return napi_clear_last_error(env);
if (!s->escape_called()) {
*result = v8impl::JsValueFromV8LocalValue(
s->Escape(v8impl::V8LocalValueFromJsValue(escapee)));
return napi_clear_last_error(env);
}
return napi_set_last_error(env, napi_escape_called_twice);
}

napi_status napi_new_instance(napi_env env,
Expand Down Expand Up @@ -2250,7 +2398,7 @@ napi_status napi_instanceof(napi_env env,
}

if (env->has_instance_available) {
napi_value value, js_result, has_instance = nullptr;
napi_value value, js_result = nullptr, has_instance = nullptr;
napi_status status = napi_generic_failure;
napi_valuetype value_type;

Expand Down Expand Up @@ -2530,7 +2678,7 @@ napi_status napi_create_arraybuffer(napi_env env,
v8::ArrayBuffer::New(isolate, byte_length);

// Optionally return a pointer to the buffer's data, to avoid another call to
// retreive it.
// retrieve it.
if (data != nullptr) {
*data = buffer->GetContents().Data();
}
Expand Down Expand Up @@ -2713,6 +2861,13 @@ napi_status napi_get_typedarray_info(napi_env env,
return napi_clear_last_error(env);
}

napi_status napi_get_version(napi_env env, uint32_t* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = NAPI_VERSION;
return napi_clear_last_error(env);
}

namespace uvimpl {

static napi_status ConvertUVErrorCode(int code) {
Expand Down Expand Up @@ -2781,7 +2936,7 @@ class Work {
// report it as a fatal exception. (There is no JavaScript on the
// callstack that can possibly handle it.)
if (!env->last_exception.IsEmpty()) {
v8::TryCatch try_catch;
v8::TryCatch try_catch(env->isolate);
env->isolate->ThrowException(
v8::Local<v8::Value>::New(env->isolate, env->last_exception));
node::FatalException(env->isolate, try_catch);
Expand Down
1 change: 1 addition & 0 deletions src/node_api.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
'type': 'static_library',
'sources': [
'node_api.cc',
'node_internals.cc',
],
'defines': [
'EXTERNAL_NAPI',
Expand Down
Loading

0 comments on commit 92d14c0

Please sign in to comment.