Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update files from node #79

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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