From a25b6d15f639f709d6cac344dbd8ed9d9711e1e0 Mon Sep 17 00:00:00 2001 From: Taylor Woll Date: Tue, 24 Jan 2017 14:42:43 -0800 Subject: [PATCH 1/4] Initial commit --- src/node_jsvmapi.cc | 550 ++++++++++++++++++++++++++++++++++---------- src/node_jsvmapi.h | 109 +++++---- 2 files changed, 491 insertions(+), 168 deletions(-) diff --git a/src/node_jsvmapi.cc b/src/node_jsvmapi.cc index 53327a64c0..ab2eb95223 100644 --- a/src/node_jsvmapi.cc +++ b/src/node_jsvmapi.cc @@ -431,10 +431,10 @@ namespace v8impl { // callback wrapper, used to retrieve the native getter/setter callback // function and data pointer. v8::Local CreateAccessorCallbackData( - napi_env e, - napi_callback getter, - napi_callback setter, - void* data) { + napi_env e, + napi_callback getter, + napi_callback setter, + void* data) { v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); @@ -465,22 +465,57 @@ namespace v8impl { } // end of namespace v8impl -// We define a macro here because, with full error handling, we may want to -// perform additional actions at the top of our public APIs, such as bail, -// returning an error code at the very top of each public API in the case that -// there's already an exception pending in v8impl::TryCatch::lastException + // We define a macro here because, with full error handling, we may want to + // perform additional actions at the top of our public APIs, such as bail, + // returning an error code at the very top of each public API in the case that + // there's already an exception pending in v8impl::TryCatch::lastException #define NAPI_PREAMBLE(e) \ v8impl::TryCatch tryCatch(v8impl::V8IsolateFromJsEnv((e))) -napi_env napi_get_current_env() { - return v8impl::JsEnvFromV8Isolate(v8::Isolate::GetCurrent()); + // Static last error returned from napi_get_last_error_info +napi_extended_error_info static_last_error; + +// Warning: Keep in-sync with napi_errorvalue enum +const char* error_messages[] = { + NULL, + "Invalid pointer passed to NAPI", + "An object was expected", + "Unknown failure" +}; + +const napi_extended_error_info* napi_get_last_error_info() { + // Wait until someone requests the last error information to fetch the error message string + static_last_error.error_message = error_messages[static_last_error.error_code]; + + return &static_last_error; +} + +napi_errorvalue napi_set_last_error_code(napi_errorvalue error_code) { + static_last_error.error_code = error_code; + + return error_code; +} + +napi_errorvalue napi_get_current_env(napi_env* e) { + if (!e) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *e = v8impl::JsEnvFromV8Isolate(v8::Isolate::GetCurrent()); + return napi_ok; } -napi_value napi_create_function( +napi_errorvalue napi_create_function( napi_env e, napi_callback cb, - void* data) { + void* data, + napi_value* result) { NAPI_PREAMBLE(e); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); v8::Local retval; @@ -493,7 +528,9 @@ napi_value napi_create_function( isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); retval = scope.Escape(tpl->GetFunction()); - return v8impl::JsValueFromV8LocalValue(retval); + *result = v8impl::JsValueFromV8LocalValue(retval); + + return napi_ok; } napi_value napi_create_constructor( @@ -601,63 +638,132 @@ napi_value napi_get_propertynames(napi_env e, napi_value o) { return v8impl::JsValueFromV8LocalValue(array); } -void napi_set_property(napi_env e, napi_value o, - napi_propertyname k, napi_value v) { +napi_errorvalue napi_set_property(napi_env e, + napi_value o, + napi_propertyname k, + napi_value v) { NAPI_PREAMBLE(e); v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); v8::Local val = v8impl::V8LocalValueFromJsValue(v); - obj->Set(key, val); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); - // This implementation is missing a lot of details, notably error - // handling on invalid inputs and regarding what happens in the - // Set operation (error thrown, key is invalid, the bool return - // value of Set) + v8::Maybe maybe = obj->Set(context, key, val); + + if (!maybe.FromMaybe(false)) { + return napi_set_last_error_code(napi_generic_failure); + } + + return napi_ok; } -bool napi_has_property(napi_env e, napi_value o, napi_propertyname k) { +napi_errorvalue napi_has_property(napi_env e, napi_value o, napi_propertyname k, bool* has) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); + + if (!has) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); + v8::Maybe has_maybe = obj_maybe.ToLocalChecked()->Has(context, key); - return obj->Has(key); + if (has_maybe.IsNothing()) { + return napi_set_last_error_code(napi_generic_failure); + } + + *has = has_maybe.FromMaybe(false); + return napi_ok; } -napi_value napi_get_property(napi_env e, napi_value o, napi_propertyname k) { +napi_errorvalue napi_get_property(napi_env e, + napi_value o, + napi_propertyname k, + napi_value* result) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); - v8::Local val = obj->Get(key); - // This implementation is missing a lot of details, notably error - // handling on invalid inputs and regarding what happens in the - // Set operation (error thrown, key is invalid, the bool return - // value of Set) - return v8impl::JsValueFromV8LocalValue(val); + auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + auto get_maybe = obj_maybe.ToLocalChecked()->Get(context, key); + + if (get_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_generic_failure); + } + + v8::Local val = get_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return napi_ok; } -void napi_set_element(napi_env e, napi_value o, uint32_t i, napi_value v) { +napi_errorvalue napi_set_element(napi_env e, napi_value o, uint32_t i, napi_value v) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); v8::Local val = v8impl::V8LocalValueFromJsValue(v); - obj->Set(i, val); + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } - // This implementation is missing a lot of details, notably error - // handling on invalid inputs and regarding what happens in the - // Set operation (error thrown, key is invalid, the bool return - // value of Set) + auto set_maybe = obj_maybe.ToLocalChecked()->Set(context, i, val); + + if (!set_maybe.FromMaybe(false)) { + return napi_set_last_error_code(napi_generic_failure); + } + + return napi_ok; } -bool napi_has_element(napi_env e, napi_value o, uint32_t i) { +napi_errorvalue napi_has_element(napi_env e, napi_value o, uint32_t i, bool* has) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - return obj->Has(i); + if (!has) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + v8::Maybe has_maybe = obj_maybe.ToLocalChecked()->Has(context, i); + + if (has_maybe.IsNothing()) { + return napi_set_last_error_code(napi_generic_failure); + } + + *has = has_maybe.FromMaybe(false); + return napi_ok; } napi_value napi_get_element(napi_env e, napi_value o, uint32_t i) { NAPI_PREAMBLE(e); + v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); v8::Local val = obj->Get(i); // This implementation is missing a lot of details, notably error @@ -742,126 +848,237 @@ napi_value napi_get_prototype(napi_env e, napi_value o) { return v8impl::JsValueFromV8LocalValue(val); } -napi_value napi_create_object(napi_env e) { +napi_errorvalue napi_create_object(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Object::New(v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result =v8impl::JsValueFromV8LocalValue( + v8::Object::New(v8impl::V8IsolateFromJsEnv(e))); + + return napi_ok; } -napi_value napi_create_array(napi_env e) { +napi_errorvalue napi_create_array(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Array::New(v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::Array::New(v8impl::V8IsolateFromJsEnv(e))); + + return napi_ok; } -napi_value napi_create_array_with_length(napi_env e, int length) { +napi_errorvalue napi_create_array_with_length(napi_env e, int length, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Array::New(v8impl::V8IsolateFromJsEnv(e), length)); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::Array::New(v8impl::V8IsolateFromJsEnv(e), length)); + + return napi_ok; } -napi_value napi_create_string(napi_env e, const char* s) { +napi_errorvalue napi_create_string(napi_env e, const char* s, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s)); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s)); + + return napi_ok; } -napi_value napi_create_string_with_length(napi_env e, - const char* s, size_t length) { +napi_errorvalue napi_create_string_with_length(napi_env e, + const char* s, + size_t length, + napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s, - v8::NewStringType::kNormal, - static_cast(length)).ToLocalChecked()); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::MaybeLocal string_maybe = v8::String::NewFromUtf8( + v8impl::V8IsolateFromJsEnv(e), s, v8::NewStringType::kNormal, static_cast(length)); + + if (string_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_generic_failure); + } + + *result = v8impl::JsValueFromV8LocalValue(string_maybe.ToLocalChecked()); + + return napi_ok; } -napi_value napi_create_number(napi_env e, double v) { +napi_errorvalue napi_create_number(napi_env e, double v, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Number::New(v8impl::V8IsolateFromJsEnv(e), v)); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::Number::New(v8impl::V8IsolateFromJsEnv(e), v)); + + return napi_ok; } -napi_value napi_create_boolean(napi_env e, bool b) { +napi_errorvalue napi_create_boolean(napi_env e, bool b, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Boolean::New(v8impl::V8IsolateFromJsEnv(e), b)); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::Boolean::New(v8impl::V8IsolateFromJsEnv(e), b)); + + return napi_ok; } -napi_value napi_create_symbol(napi_env e, const char* s) { +napi_errorvalue napi_create_symbol(napi_env e, const char* s, napi_value* result) { NAPI_PREAMBLE(e); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(e); if (s == NULL) { - return v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); } else { v8::Local string = v8::String::NewFromUtf8(isolate, s); - return v8impl::JsValueFromV8LocalValue( + *result = v8impl::JsValueFromV8LocalValue( v8::Symbol::New(isolate, string)); } + + return napi_ok; } -napi_value napi_create_error(napi_env e, napi_value msg) { +napi_errorvalue napi_create_error(napi_env e, napi_value msg, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( v8::Exception::Error( v8impl::V8LocalValueFromJsValue(msg).As())); + + return napi_ok; } -napi_value napi_create_type_error(napi_env e, napi_value msg) { +napi_errorvalue napi_create_type_error(napi_env e, napi_value msg, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( v8::Exception::TypeError( v8impl::V8LocalValueFromJsValue(msg).As())); + + return napi_ok; } -napi_valuetype napi_get_type_of_value(napi_env e, napi_value vv) { - v8::Local v = v8impl::V8LocalValueFromJsValue(vv); +napi_errorvalue napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* valuetype) { NAPI_PREAMBLE(e); + if (!valuetype) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Local v = v8impl::V8LocalValueFromJsValue(vv); + if (v->IsNumber()) { - return napi_number; + *valuetype = napi_number; } else if (v->IsString()) { - return napi_string; + *valuetype = napi_string; } else if (v->IsFunction()) { // This test has to come before IsObject because IsFunction // implies IsObject - return napi_function; + *valuetype = napi_function; } else if (v->IsObject()) { - return napi_object; + *valuetype = napi_object; } else if (v->IsBoolean()) { - return napi_boolean; + *valuetype = napi_boolean; } else if (v->IsUndefined()) { - return napi_undefined; + *valuetype = napi_undefined; } else if (v->IsSymbol()) { - return napi_symbol; + *valuetype = napi_symbol; } else if (v->IsNull()) { - return napi_null; + *valuetype = napi_null; } else { - return napi_object; // Is this correct? + *valuetype = napi_object; // Is this correct? } + + return napi_ok; } -napi_value napi_get_undefined_(napi_env e) { +napi_errorvalue napi_get_undefined(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Undefined(v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::Undefined(v8impl::V8IsolateFromJsEnv(e))); + + return napi_ok; } -napi_value napi_get_null(napi_env e) { +napi_errorvalue napi_get_null(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Null(v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::Null(v8impl::V8IsolateFromJsEnv(e))); + + return napi_ok; } -napi_value napi_get_false(napi_env e) { +napi_errorvalue napi_get_false(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::False(v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::False(v8impl::V8IsolateFromJsEnv(e))); + + return napi_ok; } -napi_value napi_get_true(napi_env e) { +napi_errorvalue napi_get_true(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::True(v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue( + v8::True(v8impl::V8IsolateFromJsEnv(e))); + + return napi_ok; } int napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo) { @@ -926,14 +1143,21 @@ napi_value napi_call_function(napi_env e, napi_value scope, return v8impl::JsValueFromV8LocalValue(result); } -napi_value napi_get_global_scope(napi_env e) { +napi_errorvalue napi_get_global_scope(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); // TODO(ianhall): what if we need the global object from a different // context in the same isolate? // Should napi_env be the current context rather than the current isolate? v8::Local context = isolate->GetCurrentContext(); - return v8impl::JsValueFromV8LocalValue(context->Global()); + *result = v8impl::JsValueFromV8LocalValue(context->Global()); + + return napi_ok; } void napi_throw(napi_env e, napi_value error) { @@ -966,26 +1190,55 @@ void napi_throw_type_error(napi_env e, const char* msg) { // to the javascript invoker will fail } -double napi_get_number_from_value(napi_env e, napi_value v) { +napi_errorvalue napi_get_number_from_value(napi_env e, napi_value v, double* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->NumberValue(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v)->NumberValue(); + + return napi_ok; } -int napi_get_string_from_value(napi_env e, napi_value v, - char* buf, const int buf_size) { +napi_errorvalue napi_get_string_from_value(napi_env e, + napi_value v, + char* buf, + const int buf_size, + int* result) { NAPI_PREAMBLE(e); - if (napi_get_type_of_value(e, v) == napi_number) { + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + napi_valuetype v_type; + napi_errorvalue err = napi_get_type_of_value(e, v, &v_type); + + // Consider: Should we pre-emptively set the length to zero ? + if (err != napi_ok) { + return err; + } + + if (v_type == napi_number) { v8::String::Utf8Value str(v8impl::V8LocalValueFromJsValue(v)); int len = str.length(); if (buf_size > len) { memcpy(buf, *str, len); - return 0; + *result = 0; } else { memcpy(buf, *str, buf_size - 1); - return len - buf_size + 1; + *result = len - buf_size + 1; } } else { - int len = napi_get_string_utf8_length(e, v); + int len = 0; + err = napi_get_string_utf8_length(e, v, &len); + + if (err != napi_ok) { + return err; + } + int copied = v8impl::V8LocalValueFromJsValue(v).As() ->WriteUtf8( buf, @@ -993,49 +1246,100 @@ int napi_get_string_from_value(napi_env e, napi_value v, 0, v8::String::REPLACE_INVALID_UTF8 | v8::String::PRESERVE_ONE_BYTE_NULL); - // add one for null ending - return len - copied + 1; + // add one for null ending + *result = len - copied + 1; } + + return napi_ok; } -int32_t napi_get_value_int32(napi_env e, napi_value v) { +napi_errorvalue napi_get_value_int32(napi_env e, napi_value v, int32_t* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->Int32Value(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v)->Int32Value(); + + return napi_ok; } -uint32_t napi_get_value_uint32(napi_env e, napi_value v) { +napi_errorvalue napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->Uint32Value(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v)->Uint32Value(); + + return napi_ok; } -int64_t napi_get_value_int64(napi_env e, napi_value v) { +napi_errorvalue napi_get_value_int64(napi_env e, napi_value v, int64_t* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->IntegerValue(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v)->IntegerValue(); + + return napi_ok; } -bool napi_get_value_bool(napi_env e, napi_value v) { +napi_errorvalue napi_get_value_bool(napi_env e, napi_value v, bool* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->BooleanValue(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v)->BooleanValue(); + + return napi_ok; } -int napi_get_string_length(napi_env e, napi_value v) { +napi_errorvalue napi_get_string_length(napi_env e, napi_value v, int* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v).As()->Length(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v).As()->Length(); + + return napi_ok; } -int napi_get_string_utf8_length(napi_env e, napi_value v) { +napi_errorvalue napi_get_string_utf8_length(napi_env e, napi_value v, int* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v).As()->Utf8Length(); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v).As()->Utf8Length(); + + return napi_ok; } -int napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bufsize) { +napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bufsize, int* result) { NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v).As() + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + *result = v8impl::V8LocalValueFromJsValue(v).As() ->WriteUtf8( buf, bufsize, 0, v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8); + + return napi_ok; } napi_value napi_coerce_to_object(napi_env e, napi_value v) { @@ -1257,9 +1561,9 @@ napi_value napi_get_and_clear_last_exception(napi_env e) { // TODO: Is there a chance that an exception will be thrown in the process of // attempting to retrieve the global static exception? - napi_value returnValue; + napi_value returnValue = nullptr; if (v8impl::TryCatch::lastException().IsEmpty()) { - returnValue = napi_get_undefined_(e); + napi_get_undefined(e, &returnValue); } else { returnValue = v8impl::JsValueFromV8LocalValue( v8impl::TryCatch::lastException().Get(v8impl::V8IsolateFromJsEnv(e))); diff --git a/src/node_jsvmapi.h b/src/node_jsvmapi.h index 164afc6066..b10445257a 100644 --- a/src/node_jsvmapi.h +++ b/src/node_jsvmapi.h @@ -116,56 +116,74 @@ enum napi_valuetype { napi_function, }; +enum napi_errorvalue { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_generic_failure +}; + +struct napi_extended_error_info { + const char* error_message; + void* engine_reserved; + uint32_t engine_error_code; + napi_errorvalue error_code; + + napi_extended_error_info() : + error_message(NULL), + engine_reserved(NULL), + engine_error_code(0), + error_code(napi_ok) + { } +}; + + +NODE_EXTERN const napi_extended_error_info* napi_get_last_error_info(); + // Environment -NODE_EXTERN napi_env napi_get_current_env(); +NODE_EXTERN napi_errorvalue napi_get_current_env(napi_env* e); // Getters for defined singletons -NODE_EXTERN napi_value napi_get_undefined_(napi_env e); -NODE_EXTERN napi_value napi_get_null(napi_env e); -NODE_EXTERN napi_value napi_get_false(napi_env e); -NODE_EXTERN napi_value napi_get_true(napi_env e); -NODE_EXTERN napi_value napi_get_global_scope(napi_env e); +NODE_EXTERN napi_errorvalue napi_get_undefined(napi_env e, napi_value* result); +NODE_EXTERN napi_errorvalue napi_get_null(napi_env e, napi_value* result); +NODE_EXTERN napi_errorvalue napi_get_false(napi_env e, napi_value* result); +NODE_EXTERN napi_errorvalue napi_get_true(napi_env e, napi_value* result); +NODE_EXTERN napi_errorvalue napi_get_global_scope(napi_env e, napi_value* result); // Methods to create Primitive types/Objects -NODE_EXTERN napi_value napi_create_object(napi_env e); -NODE_EXTERN napi_value napi_create_array(napi_env e); -NODE_EXTERN napi_value napi_create_array_with_length(napi_env e, int length); -NODE_EXTERN napi_value napi_create_number(napi_env e, double val); -NODE_EXTERN napi_value napi_create_string(napi_env e, const char*); -NODE_EXTERN napi_value napi_create_string_with_length(napi_env e, const char*, - size_t length); -NODE_EXTERN napi_value napi_create_boolean(napi_env e, bool b); -NODE_EXTERN napi_value napi_create_symbol(napi_env e, const char* s = NULL); -// Consider: Would it be useful to include an optional void* for external data -// to be included on the function object? A finalizer function would also need -// to be included. Same for napi_create_object, and perhaps add a -// napi_set_external_data() and get api? Consider JsCreateExternalObject and -// v8::ObjectTemplate::SetInternalFieldCount() -NODE_EXTERN napi_value napi_create_function(napi_env e, napi_callback cb, - void* data); -NODE_EXTERN napi_value napi_create_error(napi_env e, napi_value msg); -NODE_EXTERN napi_value napi_create_type_error(napi_env e, napi_value msg); - +NODE_EXTERN napi_errorvalue napi_create_object(napi_env e, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_array(napi_env e, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_array_with_length(napi_env e, int length, + napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_number(napi_env e, double val, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_string(napi_env e, const char* s, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_string_with_length(napi_env e, const char* s, + size_t length, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_boolean(napi_env e, bool b, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_symbol(napi_env e, const char* s, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_function(napi_env e, napi_callback cb, + void* data, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_error(napi_env e, napi_value msg, napi_value* result); +NODE_EXTERN napi_errorvalue napi_create_type_error(napi_env e, napi_value msg, napi_value* result); // Methods to get the the native napi_value from Primitive type -NODE_EXTERN napi_valuetype napi_get_type_of_value(napi_env e, napi_value v); -NODE_EXTERN double napi_get_number_from_value(napi_env e, napi_value v); -NODE_EXTERN int napi_get_string_from_value(napi_env e, napi_value v, - char* buf, const int buf_size); -NODE_EXTERN int32_t napi_get_value_int32(napi_env e, napi_value v); -NODE_EXTERN uint32_t napi_get_value_uint32(napi_env e, napi_value v); -NODE_EXTERN int64_t napi_get_value_int64(napi_env e, napi_value v); -NODE_EXTERN bool napi_get_value_bool(napi_env e, napi_value v); +NODE_EXTERN napi_errorvalue napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* valuetype); +NODE_EXTERN napi_errorvalue napi_get_number_from_value(napi_env e, napi_value v, double* result); +NODE_EXTERN napi_errorvalue napi_get_string_from_value(napi_env e, napi_value v, + char* buf, const int buf_size, int* result); +NODE_EXTERN napi_errorvalue napi_get_value_int32(napi_env e, napi_value v, int32_t* result); +NODE_EXTERN napi_errorvalue napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result); +NODE_EXTERN napi_errorvalue napi_get_value_int64(napi_env e, napi_value v, int64_t* result); +NODE_EXTERN napi_errorvalue napi_get_value_bool(napi_env e, napi_value v, bool* result); -NODE_EXTERN int napi_get_string_length(napi_env e, napi_value v); +NODE_EXTERN napi_errorvalue napi_get_string_length(napi_env e, napi_value v, int* result); // Do we need utf16 as well? -NODE_EXTERN int napi_get_string_utf8_length(napi_env e, napi_value v); -NODE_EXTERN int napi_get_string_utf8(napi_env e, napi_value v, - char* buf, int bufsize); - +NODE_EXTERN napi_errorvalue napi_get_string_utf8_length(napi_env e, napi_value v, int* result); +NODE_EXTERN napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, + char* buf, int bufsize, int* result); // Methods to coerce values // These APIs may execute user script @@ -178,15 +196,16 @@ NODE_EXTERN napi_value napi_get_prototype(napi_env e, napi_value object); NODE_EXTERN napi_propertyname napi_property_name(napi_env e, const char* utf8name); NODE_EXTERN napi_value napi_get_propertynames(napi_env e, napi_value object); -NODE_EXTERN void napi_set_property(napi_env e, napi_value object, +NODE_EXTERN napi_errorvalue napi_set_property(napi_env e, napi_value object, napi_propertyname name, napi_value v); -NODE_EXTERN bool napi_has_property(napi_env e, napi_value object, - napi_propertyname name); -NODE_EXTERN napi_value napi_get_property(napi_env e, napi_value object, - napi_propertyname name); -NODE_EXTERN void napi_set_element(napi_env e, napi_value object, +NODE_EXTERN napi_errorvalue napi_has_property(napi_env e, napi_value object, + napi_propertyname name, bool* has); +NODE_EXTERN napi_errorvalue napi_get_property(napi_env e, napi_value object, + napi_propertyname name, napi_value* result); +NODE_EXTERN napi_errorvalue napi_set_element(napi_env e, napi_value object, uint32_t i, napi_value v); -NODE_EXTERN bool napi_has_element(napi_env e, napi_value object, uint32_t i); +NODE_EXTERN napi_errorvalue napi_has_element(napi_env e, napi_value object, + uint32_t i, bool* has); NODE_EXTERN napi_value napi_get_element(napi_env e, napi_value object, uint32_t i); NODE_EXTERN void napi_define_property(napi_env e, napi_value object, From 8badec8fd60f0745e75959c7fc40d50096a811be Mon Sep 17 00:00:00 2001 From: Taylor Woll Date: Tue, 24 Jan 2017 22:35:20 -0800 Subject: [PATCH 2/4] Add error handling to a few more APIs --- src/node_jsvmapi.cc | 224 +++++++++++++++++++++++++++++++++++--------- src/node_jsvmapi.h | 22 ++--- 2 files changed, 190 insertions(+), 56 deletions(-) diff --git a/src/node_jsvmapi.cc b/src/node_jsvmapi.cc index ab2eb95223..62d5bdc983 100644 --- a/src/node_jsvmapi.cc +++ b/src/node_jsvmapi.cc @@ -478,8 +478,9 @@ napi_extended_error_info static_last_error; // Warning: Keep in-sync with napi_errorvalue enum const char* error_messages[] = { NULL, - "Invalid pointer passed to NAPI", + "Invalid pointer passed as argument", "An object was expected", + "A string was expected", "Unknown failure" }; @@ -622,20 +623,54 @@ void napi_set_return_value(napi_env e, info->SetReturnValue(v); } -napi_propertyname napi_property_name(napi_env e, const char* utf8name) { +napi_errorvalue napi_property_name(napi_env e, + const char* utf8name, + napi_propertyname* result) { NAPI_PREAMBLE(e); - v8::Local namestring = - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), utf8name, - v8::NewStringType::kInternalized).ToLocalChecked(); - return reinterpret_cast( - v8impl::JsValueFromV8LocalValue(namestring)); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + auto maybe = v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), utf8name, + v8::NewStringType::kInternalized); + + // Is this an OOM? + if (maybe.IsEmpty()) + { + return napi_set_last_error_code(napi_generic_failure); + } + + *result = reinterpret_cast( + v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked())); + + return napi_ok; } -napi_value napi_get_propertynames(napi_env e, napi_value o) { +napi_errorvalue napi_get_propertynames(napi_env e, napi_value o, napi_value* result) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local array = obj->GetPropertyNames(); - return v8impl::JsValueFromV8LocalValue(array); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + auto maybe_propertynames = obj_maybe.ToLocalChecked()->GetPropertyNames(context); + + if (maybe_propertynames.IsEmpty()) + { + return napi_set_last_error_code(napi_generic_failure); + } + + *result = v8impl::JsValueFromV8LocalValue(maybe_propertynames.ToLocalChecked()); + return napi_ok; } napi_errorvalue napi_set_property(napi_env e, @@ -643,16 +678,21 @@ napi_errorvalue napi_set_property(napi_env e, napi_propertyname k, napi_value v) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); - v8::Local val = v8impl::V8LocalValueFromJsValue(v); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); + v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); + v8::Local val = v8impl::V8LocalValueFromJsValue(v); - v8::Maybe maybe = obj->Set(context, key, val); + v8::Maybe set_maybe = obj_maybe.ToLocalChecked()->Set(context, key, val); - if (!maybe.FromMaybe(false)) { + if (!set_maybe.FromMaybe(false)) { return napi_set_last_error_code(napi_generic_failure); } @@ -761,26 +801,57 @@ napi_errorvalue napi_has_element(napi_env e, napi_value o, uint32_t i, bool* has return napi_ok; } -napi_value napi_get_element(napi_env e, napi_value o, uint32_t i) { +napi_errorvalue napi_get_element(napi_env e, + napi_value o, + uint32_t i, + napi_value* result) { NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local val = obj->Get(i); - // This implementation is missing a lot of details, notably error - // handling on invalid inputs and regarding what happens in the - // Set operation (error thrown, key is invalid, the bool return - // value of Set) - return v8impl::JsValueFromV8LocalValue(val); + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + auto get_maybe = obj_maybe.ToLocalChecked()->Get(context, i); + + if (get_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_generic_failure); + } + + *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); + return napi_ok; } -void napi_define_property(napi_env e, napi_value o, +napi_errorvalue napi_define_property(napi_env e, napi_value o, napi_property_descriptor* p) { + NAPI_PREAMBLE(e); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local name = - v8::String::NewFromUtf8(isolate, p->utf8name, - v8::NewStringType::kInternalized).ToLocalChecked(); + auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + auto obj = obj_maybe.ToLocalChecked(); + + auto name_maybe = v8::String::NewFromUtf8(isolate, p->utf8name, + v8::NewStringType::kInternalized); + + if (name_maybe.IsEmpty()) + { + return napi_set_last_error_code(napi_generic_failure); + } + + v8::Local name = name_maybe.ToLocalChecked(); v8::PropertyAttribute attributes = static_cast(p->attributes); @@ -792,17 +863,24 @@ void napi_define_property(napi_env e, napi_value o, v8::Local t = v8::FunctionTemplate::New( isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); - obj->DefineOwnProperty( + auto define_maybe = obj->DefineOwnProperty( context, name, t->GetFunction(), attributes); + + // IsNothing seems like a serious failure, + // should we return a different error code if the define failed? + if (define_maybe.IsNothing() || !define_maybe.FromMaybe(false)) + { + return napi_set_last_error_code(napi_generic_failure); + } } else if (p->getter || p->setter) { v8::Local cbdata = v8impl::CreateAccessorCallbackData( e, p->getter, p->setter, p->data); - obj->SetAccessor( + auto set_maybe = obj->SetAccessor( context, name, v8impl::GetterCallbackWrapper::Invoke, @@ -810,15 +888,32 @@ void napi_define_property(napi_env e, napi_value o, cbdata, v8::AccessControl::DEFAULT, attributes); + + // IsNothing seems like a serious failure, + // should we return a different error code if the set failed? + if (set_maybe.IsNothing() || !set_maybe.FromMaybe(false)) + { + return napi_set_last_error_code(napi_generic_failure); + } } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - obj->DefineOwnProperty( + + auto define_maybe = obj->DefineOwnProperty( context, name, value, attributes); + + // IsNothing seems like a serious failure, + // should we return a different error code if the define failed? + if (define_maybe.IsNothing() || !define_maybe.FromMaybe(false)) + { + return napi_set_last_error_code(napi_generic_failure); + } } + + return napi_ok; } bool napi_is_array(napi_env e, napi_value v) { @@ -841,11 +936,24 @@ bool napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs) { return a->StrictEquals(b); } -napi_value napi_get_prototype(napi_env e, napi_value o) { - NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local val = obj->GetPrototype(); - return v8impl::JsValueFromV8LocalValue(val); +napi_errorvalue napi_get_prototype(napi_env e, napi_value o, napi_value* result) { + NAPI_PREAMBLE(e); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + v8::Local val = obj_maybe.ToLocalChecked()->GetPrototype(); + *result = v8impl::JsValueFromV8LocalValue(val); + return napi_ok; } napi_errorvalue napi_create_object(napi_env e, napi_value* result) { @@ -1342,18 +1450,44 @@ napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bu return napi_ok; } -napi_value napi_coerce_to_object(napi_env e, napi_value v) { +napi_errorvalue napi_coerce_to_object(napi_env e, napi_value v, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8impl::V8LocalValueFromJsValue(v)->ToObject( - v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(v)->ToObject(context); + + if (obj_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_object_expected); + } + + auto val = obj_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return napi_ok; } -napi_value napi_coerce_to_string(napi_env e, napi_value v) { +napi_errorvalue napi_coerce_to_string(napi_env e, napi_value v, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8impl::V8LocalValueFromJsValue(v)->ToString( - v8impl::V8IsolateFromJsEnv(e))); + + if (!result) { + return napi_set_last_error_code(napi_invalid_arg); + } + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + auto string_maybe = v8impl::V8LocalValueFromJsValue(v)->ToString(context); + + if (string_maybe.IsEmpty()) { + return napi_set_last_error_code(napi_string_expected); + } + + auto val = string_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return napi_ok; } void napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, diff --git a/src/node_jsvmapi.h b/src/node_jsvmapi.h index b10445257a..bfb1d21341 100644 --- a/src/node_jsvmapi.h +++ b/src/node_jsvmapi.h @@ -120,6 +120,7 @@ enum napi_errorvalue { napi_ok, napi_invalid_arg, napi_object_expected, + napi_string_expected, napi_generic_failure }; @@ -143,7 +144,6 @@ NODE_EXTERN const napi_extended_error_info* napi_get_last_error_info(); // Environment NODE_EXTERN napi_errorvalue napi_get_current_env(napi_env* e); - // Getters for defined singletons NODE_EXTERN napi_errorvalue napi_get_undefined(napi_env e, napi_value* result); NODE_EXTERN napi_errorvalue napi_get_null(napi_env e, napi_value* result); @@ -151,7 +151,6 @@ NODE_EXTERN napi_errorvalue napi_get_false(napi_env e, napi_value* result); NODE_EXTERN napi_errorvalue napi_get_true(napi_env e, napi_value* result); NODE_EXTERN napi_errorvalue napi_get_global_scope(napi_env e, napi_value* result); - // Methods to create Primitive types/Objects NODE_EXTERN napi_errorvalue napi_create_object(napi_env e, napi_value* result); NODE_EXTERN napi_errorvalue napi_create_array(napi_env e, napi_value* result); @@ -187,15 +186,16 @@ NODE_EXTERN napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, // Methods to coerce values // These APIs may execute user script -NODE_EXTERN napi_value napi_coerce_to_object(napi_env e, napi_value v); -NODE_EXTERN napi_value napi_coerce_to_string(napi_env e, napi_value v); +NODE_EXTERN napi_errorvalue napi_coerce_to_object(napi_env e, napi_value v, napi_value* result); +NODE_EXTERN napi_errorvalue napi_coerce_to_string(napi_env e, napi_value v, napi_value* result); // Methods to work with Objects -NODE_EXTERN napi_value napi_get_prototype(napi_env e, napi_value object); -NODE_EXTERN napi_propertyname napi_property_name(napi_env e, - const char* utf8name); -NODE_EXTERN napi_value napi_get_propertynames(napi_env e, napi_value object); +NODE_EXTERN napi_errorvalue napi_get_prototype(napi_env e, napi_value object, napi_value* result); +NODE_EXTERN napi_errorvalue napi_property_name(napi_env e, const char* utf8name, + napi_propertyname* result); +NODE_EXTERN napi_errorvalue napi_get_propertynames(napi_env e, napi_value object, + napi_value* result); NODE_EXTERN napi_errorvalue napi_set_property(napi_env e, napi_value object, napi_propertyname name, napi_value v); NODE_EXTERN napi_errorvalue napi_has_property(napi_env e, napi_value object, @@ -206,9 +206,9 @@ NODE_EXTERN napi_errorvalue napi_set_element(napi_env e, napi_value object, uint32_t i, napi_value v); NODE_EXTERN napi_errorvalue napi_has_element(napi_env e, napi_value object, uint32_t i, bool* has); -NODE_EXTERN napi_value napi_get_element(napi_env e, - napi_value object, uint32_t i); -NODE_EXTERN void napi_define_property(napi_env e, napi_value object, +NODE_EXTERN napi_errorvalue napi_get_element(napi_env e, napi_value object, + uint32_t i, napi_value* result); +NODE_EXTERN napi_errorvalue napi_define_property(napi_env e, napi_value object, napi_property_descriptor* property); From b47414db7aafd24b8f6aaebda2b44e26c143b998 Mon Sep 17 00:00:00 2001 From: Taylor Woll Date: Fri, 27 Jan 2017 03:22:48 -0800 Subject: [PATCH 3/4] Add some convenience macros for common patterns Also add instrumentation for more of the API. Remaining API which do not return napi_status may not need to. Not sure we can run into any error here besides OOM, out of stack, etc which we aren't explicitly handling anyway. --- src/node_jsvmapi.cc | 866 ++++++++++++++++++++++---------------------- src/node_jsvmapi.h | 200 +++++----- 2 files changed, 538 insertions(+), 528 deletions(-) diff --git a/src/node_jsvmapi.cc b/src/node_jsvmapi.cc index 62d5bdc983..f171a1585a 100644 --- a/src/node_jsvmapi.cc +++ b/src/node_jsvmapi.cc @@ -465,57 +465,108 @@ namespace v8impl { } // end of namespace v8impl - // We define a macro here because, with full error handling, we may want to - // perform additional actions at the top of our public APIs, such as bail, - // returning an error code at the very top of each public API in the case that - // there's already an exception pending in v8impl::TryCatch::lastException -#define NAPI_PREAMBLE(e) \ - v8impl::TryCatch tryCatch(v8impl::V8IsolateFromJsEnv((e))) +#define RETURN_STATUS_IF_FAIL(condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error_code((status)); \ + } \ + } while(0) + +#define CHECK_ARG(arg) \ + RETURN_STATUS_IF_FAIL((arg), napi_invalid_arg) + +#define CHECK_MAYBE_EMPTY(maybe, status) \ + RETURN_STATUS_IF_FAIL(!((maybe).IsEmpty()), (status)) + +#define CHECK_MAYBE_NOTHING(maybe, status) \ + RETURN_STATUS_IF_FAIL(!((maybe).IsNothing()), (status)) + +#define NAPI_PREAMBLE(e) \ + do { \ + RETURN_STATUS_IF_FAIL(v8impl::TryCatch::lastException().IsEmpty(), \ + napi_pending_exception); \ + napi_clear_last_error_code(); \ + v8impl::TryCatch tryCatch(v8impl::V8IsolateFromJsEnv((e))); \ + } while(0) + +#define CHECK_TO_OBJECT(result, o) \ + do { \ + auto obj_maybe = \ + v8impl::V8LocalValueFromJsValue((o))->ToObject(context); \ + CHECK_MAYBE_EMPTY(obj_maybe, napi_object_expected); \ + result = obj_maybe.ToLocalChecked(); \ + } while(0) + +#define CHECK_NEW_FROM_UTF8_LEN(result, str, len) \ + do { \ + auto name_maybe = v8::String::NewFromUtf8(isolate, (str), \ + v8::NewStringType::kInternalized, len); \ + CHECK_MAYBE_EMPTY(name_maybe, napi_generic_failure); \ + result = name_maybe.ToLocalChecked(); \ + } while(0) + +#define CHECK_NEW_FROM_UTF8(result, str) \ + CHECK_NEW_FROM_UTF8_LEN((result), (str), -1) // Static last error returned from napi_get_last_error_info napi_extended_error_info static_last_error; -// Warning: Keep in-sync with napi_errorvalue enum +// Warning: Keep in-sync with napi_status enum const char* error_messages[] = { NULL, "Invalid pointer passed as argument", "An object was expected", "A string was expected", - "Unknown failure" + "A function was expected", + "Unknown failure", + "An exception is pending" }; +void napi_clear_last_error_code() { + // TODO: Should this also clear engine_specific fields? + static_last_error.error_code = napi_ok; +} + const napi_extended_error_info* napi_get_last_error_info() { + static_assert(sizeof(error_messages) / sizeof(*error_messages) == napi_status_last); + assert(static_last_error.error_code < napi_status_last); + // Wait until someone requests the last error information to fetch the error message string static_last_error.error_message = error_messages[static_last_error.error_code]; return &static_last_error; } -napi_errorvalue napi_set_last_error_code(napi_errorvalue error_code) { +void napi_clear_extended_error_info() { + // TODO: Should this be a callback? + static_last_error.engine_error_code = 0; + static_last_error.engine_reserved = NULL; +} + +napi_status napi_set_last_error_code(napi_status error_code, + uint32_t engine_error_code = 0, + void* engine_reserved = NULL) { static_last_error.error_code = error_code; + static_last_error.engine_error_code = engine_error_code; + static_last_error.engine_reserved = engine_reserved; return error_code; } -napi_errorvalue napi_get_current_env(napi_env* e) { - if (!e) { - return napi_set_last_error_code(napi_invalid_arg); - } +napi_status napi_get_current_env(napi_env* e) { + CHECK_ARG(e); *e = v8impl::JsEnvFromV8Isolate(v8::Isolate::GetCurrent()); return napi_ok; } -napi_errorvalue napi_create_function( +napi_status napi_create_function( napi_env e, napi_callback cb, void* data, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); @@ -534,14 +585,17 @@ napi_errorvalue napi_create_function( return napi_ok; } -napi_value napi_create_constructor( +napi_status napi_create_constructor( napi_env e, char* utf8name, napi_callback cb, void* data, int property_count, - napi_property_descriptor* properties) { + napi_property_descriptor* properties, + napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); v8::Local retval; @@ -555,16 +609,14 @@ napi_value napi_create_constructor( // we need an internal field to stash the wrapped object tpl->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local namestring = - v8::String::NewFromUtf8(isolate, utf8name, - v8::NewStringType::kInternalized).ToLocalChecked(); + v8::Local namestring; + CHECK_NEW_FROM_UTF8(namestring, utf8name); tpl->SetClassName(namestring); for (int i = 0; i < property_count; i++) { napi_property_descriptor* p = properties + i; - v8::Local propertyname = - v8::String::NewFromUtf8(isolate, p->utf8name, - v8::NewStringType::kInternalized).ToLocalChecked(); + v8::Local propertyname; + CHECK_NEW_FROM_UTF8(propertyname, p->utf8name); v8::PropertyAttribute attributes = static_cast(p->attributes); @@ -604,76 +656,67 @@ napi_value napi_create_constructor( } retval = scope.Escape(tpl->GetFunction()); - return v8impl::JsValueFromV8LocalValue(retval); + *result = v8impl::JsValueFromV8LocalValue(retval); + + return napi_ok; } -void napi_set_function_name(napi_env e, napi_value func, - napi_propertyname name) { +napi_status napi_set_function_name(napi_env e, napi_value func, + napi_propertyname name) { NAPI_PREAMBLE(e); + v8::Local v8func = v8impl::V8LocalFunctionFromJsValue(func); v8func->SetName( v8impl::V8LocalValueFromJsPropertyName(name).As()); + + return napi_ok; } -void napi_set_return_value(napi_env e, - napi_callback_info cbinfo, napi_value v) { +napi_status napi_set_return_value(napi_env e, + napi_callback_info cbinfo, napi_value v) { NAPI_PREAMBLE(e); + v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); + reinterpret_cast(cbinfo); + info->SetReturnValue(v); + return napi_ok; } -napi_errorvalue napi_property_name(napi_env e, - const char* utf8name, - napi_propertyname* result) { +napi_status napi_property_name(napi_env e, + const char* utf8name, + napi_propertyname* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } - - auto maybe = v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), utf8name, - v8::NewStringType::kInternalized); - - // Is this an OOM? - if (maybe.IsEmpty()) - { - return napi_set_last_error_code(napi_generic_failure); - } + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local name; + CHECK_NEW_FROM_UTF8(name, utf8name); *result = reinterpret_cast( - v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked())); + v8impl::JsValueFromV8LocalValue(name)); return napi_ok; } -napi_errorvalue napi_get_propertynames(napi_env e, napi_value o, napi_value* result) { +napi_status napi_get_propertynames(napi_env e, napi_value o, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + v8::Local obj; + CHECK_TO_OBJECT(obj, o); - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + auto maybe_propertynames = obj->GetPropertyNames(context); - auto maybe_propertynames = obj_maybe.ToLocalChecked()->GetPropertyNames(context); - - if (maybe_propertynames.IsEmpty()) - { - return napi_set_last_error_code(napi_generic_failure); - } + CHECK_MAYBE_EMPTY(maybe_propertynames, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(maybe_propertynames.ToLocalChecked()); return napi_ok; } -napi_errorvalue napi_set_property(napi_env e, +napi_status napi_set_property(napi_env e, napi_value o, napi_propertyname k, napi_value v) { @@ -681,177 +724,129 @@ napi_errorvalue napi_set_property(napi_env e, v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + v8::Local obj; - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + CHECK_TO_OBJECT(obj, o); v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); v8::Local val = v8impl::V8LocalValueFromJsValue(v); - v8::Maybe set_maybe = obj_maybe.ToLocalChecked()->Set(context, key, val); + v8::Maybe set_maybe = obj->Set(context, key, val); - if (!set_maybe.FromMaybe(false)) { - return napi_set_last_error_code(napi_generic_failure); - } + RETURN_STATUS_IF_FAIL(set_maybe.FromMaybe(false), napi_generic_failure); return napi_ok; } -napi_errorvalue napi_has_property(napi_env e, napi_value o, napi_propertyname k, bool* has) { +napi_status napi_has_property(napi_env e, napi_value o, napi_propertyname k, bool* result) { NAPI_PREAMBLE(e); - - if (!has) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + v8::Local obj; + + CHECK_TO_OBJECT(obj, o); - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } - v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); - v8::Maybe has_maybe = obj_maybe.ToLocalChecked()->Has(context, key); + v8::Maybe has_maybe = obj->Has(context, key); - if (has_maybe.IsNothing()) { - return napi_set_last_error_code(napi_generic_failure); - } + CHECK_MAYBE_NOTHING(has_maybe, napi_generic_failure); - *has = has_maybe.FromMaybe(false); + *result = has_maybe.FromMaybe(false); return napi_ok; } -napi_errorvalue napi_get_property(napi_env e, +napi_status napi_get_property(napi_env e, napi_value o, napi_propertyname k, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); - auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + v8::Local obj; - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + CHECK_TO_OBJECT(obj, o); - auto get_maybe = obj_maybe.ToLocalChecked()->Get(context, key); + auto get_maybe = obj->Get(context, key); - if (get_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_generic_failure); - } + CHECK_MAYBE_EMPTY(get_maybe, napi_generic_failure); v8::Local val = get_maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(val); return napi_ok; } -napi_errorvalue napi_set_element(napi_env e, napi_value o, uint32_t i, napi_value v) { +napi_status napi_set_element(napi_env e, napi_value o, uint32_t i, napi_value v) { NAPI_PREAMBLE(e); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); - v8::Local val = v8impl::V8LocalValueFromJsValue(v); + v8::Local obj; - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + CHECK_TO_OBJECT(obj, o); - auto set_maybe = obj_maybe.ToLocalChecked()->Set(context, i, val); + v8::Local val = v8impl::V8LocalValueFromJsValue(v); + auto set_maybe = obj->Set(context, i, val); - if (!set_maybe.FromMaybe(false)) { - return napi_set_last_error_code(napi_generic_failure); - } + RETURN_STATUS_IF_FAIL(set_maybe.FromMaybe(false), napi_generic_failure); return napi_ok; } -napi_errorvalue napi_has_element(napi_env e, napi_value o, uint32_t i, bool* has) { +napi_status napi_has_element(napi_env e, napi_value o, uint32_t i, bool* result) { NAPI_PREAMBLE(e); - - if (!has) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + v8::Local obj; - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + CHECK_TO_OBJECT(obj, o); - v8::Maybe has_maybe = obj_maybe.ToLocalChecked()->Has(context, i); + v8::Maybe has_maybe = obj->Has(context, i); - if (has_maybe.IsNothing()) { - return napi_set_last_error_code(napi_generic_failure); - } + CHECK_MAYBE_NOTHING(has_maybe, napi_generic_failure); - *has = has_maybe.FromMaybe(false); + *result = has_maybe.FromMaybe(false); return napi_ok; } -napi_errorvalue napi_get_element(napi_env e, - napi_value o, - uint32_t i, - napi_value* result) { +napi_status napi_get_element(napi_env e, + napi_value o, + uint32_t i, + napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); + v8::Local obj; - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + CHECK_TO_OBJECT(obj, o); - auto get_maybe = obj_maybe.ToLocalChecked()->Get(context, i); + auto get_maybe = obj->Get(context, i); - if (get_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_generic_failure); - } + CHECK_MAYBE_EMPTY(get_maybe, napi_generic_failure); *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); return napi_ok; } -napi_errorvalue napi_define_property(napi_env e, napi_value o, - napi_property_descriptor* p) { +napi_status napi_define_property(napi_env e, napi_value o, + napi_property_descriptor* p) { NAPI_PREAMBLE(e); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - auto obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); - - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } - - auto obj = obj_maybe.ToLocalChecked(); - - auto name_maybe = v8::String::NewFromUtf8(isolate, p->utf8name, - v8::NewStringType::kInternalized); - - if (name_maybe.IsEmpty()) - { - return napi_set_last_error_code(napi_generic_failure); - } + v8::Local obj; + CHECK_TO_OBJECT(obj, o); - v8::Local name = name_maybe.ToLocalChecked(); + v8::Local name; + CHECK_NEW_FROM_UTF8(name, p->utf8name); v8::PropertyAttribute attributes = static_cast(p->attributes); @@ -916,52 +911,57 @@ napi_errorvalue napi_define_property(napi_env e, napi_value o, return napi_ok; } -bool napi_is_array(napi_env e, napi_value v) { +napi_status napi_is_array(napi_env e, napi_value v, bool* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Local val = v8impl::V8LocalValueFromJsValue(v); - return val->IsArray(); + + *result = val->IsArray(); + return napi_ok; } -uint32_t napi_get_array_length(napi_env e, napi_value v) { +napi_status napi_get_array_length(napi_env e, napi_value v, uint32_t* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + + // TODO: Should this also check to see if v is an array before blindly casting it? v8::Local arr = v8impl::V8LocalValueFromJsValue(v).As(); - return arr->Length(); + + *result = arr->Length(); + return napi_ok; } -bool napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs) { +napi_status napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs, bool* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); - return a->StrictEquals(b); + + *result = a->StrictEquals(b); + return napi_ok; } -napi_errorvalue napi_get_prototype(napi_env e, napi_value o, napi_value* result) { - NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } +napi_status napi_get_prototype(napi_env e, napi_value o, napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(o)->ToObject(context); - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } + v8::Local obj; + CHECK_TO_OBJECT(obj, o); - v8::Local val = obj_maybe.ToLocalChecked()->GetPrototype(); + v8::Local val = obj->GetPrototype(); *result = v8impl::JsValueFromV8LocalValue(val); return napi_ok; } -napi_errorvalue napi_create_object(napi_env e, napi_value* result) { +napi_status napi_create_object(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result =v8impl::JsValueFromV8LocalValue( v8::Object::New(v8impl::V8IsolateFromJsEnv(e))); @@ -969,12 +969,9 @@ napi_errorvalue napi_create_object(napi_env e, napi_value* result) { return napi_ok; } -napi_errorvalue napi_create_array(napi_env e, napi_value* result) { +napi_status napi_create_array(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Array::New(v8impl::V8IsolateFromJsEnv(e))); @@ -982,12 +979,9 @@ napi_errorvalue napi_create_array(napi_env e, napi_value* result) { return napi_ok; } -napi_errorvalue napi_create_array_with_length(napi_env e, int length, napi_value* result) { +napi_status napi_create_array_with_length(napi_env e, int length, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Array::New(v8impl::V8IsolateFromJsEnv(e), length)); @@ -995,12 +989,9 @@ napi_errorvalue napi_create_array_with_length(napi_env e, int length, napi_value return napi_ok; } -napi_errorvalue napi_create_string(napi_env e, const char* s, napi_value* result) { +napi_status napi_create_string(napi_env e, const char* s, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s)); @@ -1008,34 +999,24 @@ napi_errorvalue napi_create_string(napi_env e, const char* s, napi_value* result return napi_ok; } -napi_errorvalue napi_create_string_with_length(napi_env e, - const char* s, - size_t length, - napi_value* result) { +napi_status napi_create_string_with_length(napi_env e, + const char* s, + size_t length, + napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } - - v8::MaybeLocal string_maybe = v8::String::NewFromUtf8( - v8impl::V8IsolateFromJsEnv(e), s, v8::NewStringType::kNormal, static_cast(length)); - - if (string_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_generic_failure); - } - - *result = v8impl::JsValueFromV8LocalValue(string_maybe.ToLocalChecked()); + auto isolate = v8impl::V8IsolateFromJsEnv(e); + v8::MaybeLocal str; + CHECK_NEW_FROM_UTF8_LEN(str, s, length); + *result = v8impl::JsValueFromV8LocalValue(str); return napi_ok; } -napi_errorvalue napi_create_number(napi_env e, double v, napi_value* result) { +napi_status napi_create_number(napi_env e, double v, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Number::New(v8impl::V8IsolateFromJsEnv(e), v)); @@ -1043,12 +1024,9 @@ napi_errorvalue napi_create_number(napi_env e, double v, napi_value* result) { return napi_ok; } -napi_errorvalue napi_create_boolean(napi_env e, bool b, napi_value* result) { +napi_status napi_create_boolean(napi_env e, bool b, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Boolean::New(v8impl::V8IsolateFromJsEnv(e), b)); @@ -1056,31 +1034,26 @@ napi_errorvalue napi_create_boolean(napi_env e, bool b, napi_value* result) { return napi_ok; } -napi_errorvalue napi_create_symbol(napi_env e, const char* s, napi_value* result) { +napi_status napi_create_symbol(napi_env e, const char* s, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(e); if (s == NULL) { *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); } else { - v8::Local string = v8::String::NewFromUtf8(isolate, s); - *result = v8impl::JsValueFromV8LocalValue( - v8::Symbol::New(isolate, string)); + v8::Local string; + CHECK_NEW_FROM_UTF8(string, s); + + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate, string)); } return napi_ok; } -napi_errorvalue napi_create_error(napi_env e, napi_value msg, napi_value* result) { +napi_status napi_create_error(napi_env e, napi_value msg, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Exception::Error( @@ -1089,12 +1062,9 @@ napi_errorvalue napi_create_error(napi_env e, napi_value msg, napi_value* result return napi_ok; } -napi_errorvalue napi_create_type_error(napi_env e, napi_value msg, napi_value* result) { +napi_status napi_create_type_error(napi_env e, napi_value msg, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Exception::TypeError( @@ -1103,46 +1073,40 @@ napi_errorvalue napi_create_type_error(napi_env e, napi_value msg, napi_value* r return napi_ok; } -napi_errorvalue napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* valuetype) { +napi_status napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* result) { NAPI_PREAMBLE(e); - - if (!valuetype) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Local v = v8impl::V8LocalValueFromJsValue(vv); if (v->IsNumber()) { - *valuetype = napi_number; + *result = napi_number; } else if (v->IsString()) { - *valuetype = napi_string; + *result = napi_string; } else if (v->IsFunction()) { // This test has to come before IsObject because IsFunction // implies IsObject - *valuetype = napi_function; + *result = napi_function; } else if (v->IsObject()) { - *valuetype = napi_object; + *result = napi_object; } else if (v->IsBoolean()) { - *valuetype = napi_boolean; + *result = napi_boolean; } else if (v->IsUndefined()) { - *valuetype = napi_undefined; + *result = napi_undefined; } else if (v->IsSymbol()) { - *valuetype = napi_symbol; + *result = napi_symbol; } else if (v->IsNull()) { - *valuetype = napi_null; + *result = napi_null; } else { - *valuetype = napi_object; // Is this correct? + *result = napi_object; // Is this correct? } return napi_ok; } -napi_errorvalue napi_get_undefined(napi_env e, napi_value* result) { +napi_status napi_get_undefined(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Undefined(v8impl::V8IsolateFromJsEnv(e))); @@ -1150,12 +1114,9 @@ napi_errorvalue napi_get_undefined(napi_env e, napi_value* result) { return napi_ok; } -napi_errorvalue napi_get_null(napi_env e, napi_value* result) { +napi_status napi_get_null(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::Null(v8impl::V8IsolateFromJsEnv(e))); @@ -1163,12 +1124,9 @@ napi_errorvalue napi_get_null(napi_env e, napi_value* result) { return napi_ok; } -napi_errorvalue napi_get_false(napi_env e, napi_value* result) { +napi_status napi_get_false(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::False(v8impl::V8IsolateFromJsEnv(e))); @@ -1176,12 +1134,9 @@ napi_errorvalue napi_get_false(napi_env e, napi_value* result) { return napi_ok; } -napi_errorvalue napi_get_true(napi_env e, napi_value* result) { +napi_status napi_get_true(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::JsValueFromV8LocalValue( v8::True(v8impl::V8IsolateFromJsEnv(e))); @@ -1190,14 +1145,13 @@ napi_errorvalue napi_get_true(napi_env e, napi_value* result) { } int napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo) { - NAPI_PREAMBLE(e); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); + return info->ArgsLength(); } bool napi_is_construct_call(napi_env e, napi_callback_info cbinfo) { - NAPI_PREAMBLE(e); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); return info->IsConstructCall(); @@ -1207,14 +1161,12 @@ bool napi_is_construct_call(napi_env e, napi_callback_info cbinfo) { // encoded arguments array? void napi_get_cb_args(napi_env e, napi_callback_info cbinfo, napi_value* buffer, size_t bufferlength) { - NAPI_PREAMBLE(e); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); info->Args(buffer, bufferlength); } napi_value napi_get_cb_this(napi_env e, napi_callback_info cbinfo) { - NAPI_PREAMBLE(e); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); return info->This(); @@ -1224,7 +1176,6 @@ napi_value napi_get_cb_this(napi_env e, napi_callback_info cbinfo) { // AFAIK Holder should be the owner of the JS function, which should be in the // prototype chain of This, so maybe it is possible to emulate. napi_value napi_get_cb_holder(napi_env e, napi_callback_info cbinfo) { - NAPI_PREAMBLE(e); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); return info->Holder(); @@ -1236,27 +1187,38 @@ void* napi_get_cb_data(napi_env e, napi_callback_info cbinfo) { return info->Data(); } -napi_value napi_call_function(napi_env e, napi_value scope, - napi_value func, int argc, napi_value* argv) { +napi_status napi_call_function(napi_env e, + napi_value scope, + napi_value func, + int argc, + napi_value* argv, + napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + std::vector> args(argc); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::Handle v8scope; + + CHECK_TO_OBJECT(v8scope, scope); - v8::Local v8func = v8impl::V8LocalFunctionFromJsValue(func); - v8::Handle v8scope = - v8impl::V8LocalValueFromJsValue(scope)->ToObject(); for (int i = 0; i < argc; i++) { args[i] = v8impl::V8LocalValueFromJsValue(argv[i]); } - v8::Handle result = v8func->Call(v8scope, argc, args.data()); - return v8impl::JsValueFromV8LocalValue(result); + + v8::Local v8func = v8impl::V8LocalFunctionFromJsValue(func); + auto maybe = v8func->Call(context, v8scope, argc, args.data()); + + CHECK_MAYBE_EMPTY(maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return napi_ok; } -napi_errorvalue napi_get_global_scope(napi_env e, napi_value* result) { +napi_status napi_get_global_scope(napi_env e, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); // TODO(ianhall): what if we need the global object from a different @@ -1269,7 +1231,6 @@ napi_errorvalue napi_get_global_scope(napi_env e, napi_value* result) { } void napi_throw(napi_env e, napi_value error) { - NAPI_PREAMBLE(e); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); isolate->ThrowException( @@ -1279,7 +1240,6 @@ void napi_throw(napi_env e, napi_value error) { } void napi_throw_error(napi_env e, const char* msg) { - NAPI_PREAMBLE(e); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); isolate->ThrowException( @@ -1289,7 +1249,6 @@ void napi_throw_error(napi_env e, const char* msg) { } void napi_throw_type_error(napi_env e, const char* msg) { - NAPI_PREAMBLE(e); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); isolate->ThrowException( @@ -1298,7 +1257,7 @@ void napi_throw_type_error(napi_env e, const char* msg) { // to the javascript invoker will fail } -napi_errorvalue napi_get_number_from_value(napi_env e, napi_value v, double* result) { +napi_status napi_get_number_from_value(napi_env e, napi_value v, double* result) { NAPI_PREAMBLE(e); if (!result) { @@ -1310,24 +1269,19 @@ napi_errorvalue napi_get_number_from_value(napi_env e, napi_value v, double* res return napi_ok; } -napi_errorvalue napi_get_string_from_value(napi_env e, +napi_status napi_get_string_from_value(napi_env e, napi_value v, char* buf, const int buf_size, int* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); napi_valuetype v_type; - napi_errorvalue err = napi_get_type_of_value(e, v, &v_type); + napi_status err = napi_get_type_of_value(e, v, &v_type); // Consider: Should we pre-emptively set the length to zero ? - if (err != napi_ok) { - return err; - } + RETURN_STATUS_IF_FAIL(err == napi_ok, err); if (v_type == napi_number) { v8::String::Utf8Value str(v8impl::V8LocalValueFromJsValue(v)); @@ -1342,10 +1296,7 @@ napi_errorvalue napi_get_string_from_value(napi_env e, } else { int len = 0; err = napi_get_string_utf8_length(e, v, &len); - - if (err != napi_ok) { - return err; - } + RETURN_STATUS_IF_FAIL(err == napi_ok, err); int copied = v8impl::V8LocalValueFromJsValue(v).As() ->WriteUtf8( @@ -1361,84 +1312,63 @@ napi_errorvalue napi_get_string_from_value(napi_env e, return napi_ok; } -napi_errorvalue napi_get_value_int32(napi_env e, napi_value v, int32_t* result) { +napi_status napi_get_value_int32(napi_env e, napi_value v, int32_t* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v)->Int32Value(); return napi_ok; } -napi_errorvalue napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result) { +napi_status napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v)->Uint32Value(); return napi_ok; } -napi_errorvalue napi_get_value_int64(napi_env e, napi_value v, int64_t* result) { +napi_status napi_get_value_int64(napi_env e, napi_value v, int64_t* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v)->IntegerValue(); return napi_ok; } -napi_errorvalue napi_get_value_bool(napi_env e, napi_value v, bool* result) { +napi_status napi_get_value_bool(napi_env e, napi_value v, bool* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v)->BooleanValue(); return napi_ok; } -napi_errorvalue napi_get_string_length(napi_env e, napi_value v, int* result) { +napi_status napi_get_string_length(napi_env e, napi_value v, int* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v).As()->Length(); return napi_ok; } -napi_errorvalue napi_get_string_utf8_length(napi_env e, napi_value v, int* result) { +napi_status napi_get_string_utf8_length(napi_env e, napi_value v, int* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v).As()->Utf8Length(); return napi_ok; } -napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bufsize, int* result) { +napi_status napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bufsize, int* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v).As() ->WriteUtf8( @@ -1450,94 +1380,105 @@ napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bu return napi_ok; } -napi_errorvalue napi_coerce_to_object(napi_env e, napi_value v, napi_value* result) { +napi_status napi_coerce_to_object(napi_env e, napi_value v, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal obj_maybe = v8impl::V8LocalValueFromJsValue(v)->ToObject(context); + v8::Local obj; + CHECK_TO_OBJECT(obj, v); - if (obj_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_object_expected); - } - - auto val = obj_maybe.ToLocalChecked(); - *result = v8impl::JsValueFromV8LocalValue(val); + *result = v8impl::JsValueFromV8LocalValue(obj); return napi_ok; } -napi_errorvalue napi_coerce_to_string(napi_env e, napi_value v, napi_value* result) { +napi_status napi_coerce_to_string(napi_env e, napi_value v, napi_value* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); auto string_maybe = v8impl::V8LocalValueFromJsValue(v)->ToString(context); - if (string_maybe.IsEmpty()) { - return napi_set_last_error_code(napi_string_expected); - } + CHECK_MAYBE_EMPTY(string_maybe, napi_string_expected); - auto val = string_maybe.ToLocalChecked(); - *result = v8impl::JsValueFromV8LocalValue(val); + *result = v8impl::JsValueFromV8LocalValue(string_maybe.ToLocalChecked()); return napi_ok; } -void napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, - napi_destruct destructor, napi_weakref* handle) { +napi_status napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, + napi_destruct destructor, napi_weakref* handle) { NAPI_PREAMBLE(e); - // object wrap api needs more thought - // e.g. who deletes this object? + v8impl::ObjectWrapWrapper* wrap = new v8impl::ObjectWrapWrapper(jsObject, nativeObj, destructor); - if (handle != nullptr) { - *handle = napi_create_weakref( - e, - v8impl::JsValueFromV8LocalValue(wrap->handle())); + + if (handle) + { + return napi_create_weakref( + e, + v8impl::JsValueFromV8LocalValue(wrap->handle()), + handle); + } + else + { + // TODO: Is the handle parameter really optional? + // Why would anyone want to construct an object wrap and immediately lose it? + return napi_ok; } } -void* napi_unwrap(napi_env e, napi_value jsObject) { +napi_status napi_unwrap(napi_env e, napi_value jsObject, void** result) { NAPI_PREAMBLE(e); - return v8impl::ObjectWrapWrapper::Unwrap(jsObject); + CHECK_ARG(result); + + *result = v8impl::ObjectWrapWrapper::Unwrap(jsObject); + return napi_ok; } -napi_persistent napi_create_persistent(napi_env e, napi_value v) { +napi_status napi_create_persistent(napi_env e, napi_value v, napi_persistent* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Persistent *thePersistent = new v8::Persistent( isolate, v8impl::V8LocalValueFromJsValue(v)); - return v8impl::JsPersistentFromV8PersistentValue(thePersistent); + + *result = v8impl::JsPersistentFromV8PersistentValue(thePersistent); + return napi_ok; } -void napi_release_persistent(napi_env e, napi_persistent p) { +napi_status napi_release_persistent(napi_env e, napi_persistent p) { NAPI_PREAMBLE(e); + v8::Persistent *thePersistent = v8impl::V8PersistentValueFromJsPersistentValue(p); thePersistent->Reset(); delete thePersistent; + + return napi_ok; } -napi_value napi_get_persistent_value(napi_env e, napi_persistent p) { +napi_status napi_get_persistent_value(napi_env e, napi_persistent p, napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Persistent *thePersistent = v8impl::V8PersistentValueFromJsPersistentValue(p); v8::Local napi_value = v8::Local::New(isolate, *thePersistent); - return v8impl::JsValueFromV8LocalValue(napi_value); + + *result = v8impl::JsValueFromV8LocalValue(napi_value); + return napi_ok; } -napi_weakref napi_create_weakref(napi_env e, napi_value v) { +napi_status napi_create_weakref(napi_env e, napi_value v, napi_weakref* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Persistent *thePersistent = new v8::Persistent( @@ -1545,132 +1486,162 @@ napi_weakref napi_create_weakref(napi_env e, napi_value v) { thePersistent->SetWeak(static_cast(nullptr), v8impl::WeakRefCallback, v8::WeakCallbackType::kParameter); // need to mark independent? - return v8impl::JsWeakRefFromV8PersistentValue(thePersistent); + *result = v8impl::JsWeakRefFromV8PersistentValue(thePersistent); + return napi_ok; } -bool napi_get_weakref_value(napi_env e, napi_weakref w, napi_value* pv) { +napi_status napi_get_weakref_value(napi_env e, napi_weakref w, napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Persistent *thePersistent = v8impl::V8PersistentValueFromJsWeakRefValue(w); v8::Local v = v8::Local::New(isolate, *thePersistent); if (v.IsEmpty()) { - *pv = nullptr; - return false; + *result = NULL; + return napi_ok; } - *pv = v8impl::JsValueFromV8LocalValue(v); - return true; + *result = v8impl::JsValueFromV8LocalValue(v); + return napi_ok; } -void napi_release_weakref(napi_env e, napi_weakref w) { +napi_status napi_release_weakref(napi_env e, napi_weakref w) { NAPI_PREAMBLE(e); + v8::Persistent *thePersistent = v8impl::V8PersistentValueFromJsWeakRefValue(w); + thePersistent->Reset(); delete thePersistent; + + return napi_ok; } napi_handle_scope napi_open_handle_scope(napi_env e) { - NAPI_PREAMBLE(e); return v8impl::JsHandleScopeFromV8HandleScope( new v8impl::HandleScopeWrapper(v8impl::V8IsolateFromJsEnv(e))); } void napi_close_handle_scope(napi_env e, napi_handle_scope scope) { - NAPI_PREAMBLE(e); delete v8impl::V8HandleScopeFromJsHandleScope(scope); } napi_escapable_handle_scope napi_open_escapable_handle_scope(napi_env e) { - NAPI_PREAMBLE(e); return v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( new v8impl::EscapableHandleScopeWrapper(v8impl::V8IsolateFromJsEnv(e))); } void napi_close_escapable_handle_scope(napi_env e, napi_escapable_handle_scope scope) { - NAPI_PREAMBLE(e); delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); } napi_value napi_escape_handle(napi_env e, napi_escapable_handle_scope scope, napi_value escapee) { - NAPI_PREAMBLE(e); v8impl::EscapableHandleScopeWrapper* s = v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); return v8impl::JsValueFromV8LocalValue( s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); } -napi_value napi_new_instance(napi_env e, napi_value cons, - int argc, napi_value *argv) { +napi_status napi_new_instance(napi_env e, + napi_value cons, + int argc, + napi_value* argv, + napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local v8cons = v8impl::V8LocalFunctionFromJsValue(cons); + v8::Local context = isolate->GetCurrentContext(); + std::vector> args(argc); for (int i = 0; i < argc; i++) { args[i] = v8impl::V8LocalValueFromJsValue(argv[i]); } - v8::Handle result = v8cons->NewInstance( - isolate->GetCurrentContext(), - argc, - args.data()).ToLocalChecked(); - return v8impl::JsValueFromV8LocalValue(result); + v8::Local v8cons = v8impl::V8LocalFunctionFromJsValue(cons); + + auto maybe = v8cons->NewInstance(context, argc, args.data()); + CHECK_MAYBE_EMPTY(maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return napi_ok; } -bool napi_instanceof(napi_env e, napi_value obj, napi_value cons) { - bool returnValue = false; +napi_status napi_instanceof(napi_env e, napi_value obj, napi_value cons, bool* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + *result = false; - v8::Local v8Obj = v8impl::V8LocalValueFromJsValue(obj); - v8::Local v8Cons = v8impl::V8LocalValueFromJsValue(cons); + v8::Local v8Cons; + v8::Local prototypeString; v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + + CHECK_TO_OBJECT(v8Cons, cons); if (!v8Cons->IsFunction()) { napi_throw_type_error(e, "constructor must be a function"); - // Error handling needs to be done here - return false; + return napi_set_last_error_code(napi_function_expected); } - v8Cons = - v8Cons->ToObject()->Get(v8::String::NewFromUtf8(isolate, "prototype")); + CHECK_NEW_FROM_UTF8(prototypeString, "prototype"); + + auto maybe = v8Cons->Get(context, prototypeString); - if (!v8Cons->IsObject()) { + CHECK_MAYBE_EMPTY(maybe, napi_generic_failure); + + v8::Local prototypeProperty = maybe.ToLocalChecked(); + + if (!prototypeProperty->IsObject()) { napi_throw_type_error(e, "constructor prototype must be an object"); - // Error handling needs to be done here - return false; + return napi_set_last_error_code(napi_object_expected); } + v8::Local v8Obj = v8impl::V8LocalValueFromJsValue(obj); if (!v8Obj->StrictEquals(v8Cons)) { for (v8::Local originalObj = v8Obj; !(v8Obj->IsNull() || v8Obj->IsUndefined()); - v8Obj = v8Obj->ToObject()->GetPrototype()) { + ) { if (v8Obj->StrictEquals(v8Cons)) { - returnValue = + *result = !(originalObj->IsNumber() || originalObj->IsBoolean() || originalObj->IsString()); break; } + v8::Local obj; + CHECK_TO_OBJECT(obj, v8impl::JsValueFromV8LocalValue(v8Obj)); + v8Obj = obj->GetPrototype(); } } - return returnValue; + return napi_ok; } -napi_value napi_make_external(napi_env e, napi_value v) { - +napi_status napi_make_external(napi_env e, napi_value v, napi_value* result) { + CHECK_ARG(result); // v8impl::TryCatch doesn't make sense here since we're not calling into the // engine at all. - return v; + *result = v; + return napi_ok; } -napi_value napi_make_callback(napi_env e, napi_value recv, - napi_value func, int argc, napi_value* argv) { +napi_status napi_make_callback(napi_env e, + napi_value recv, + napi_value func, + int argc, + napi_value* argv, + napi_value* result) { NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv).As(); @@ -1681,9 +1652,11 @@ napi_value napi_make_callback(napi_env e, napi_value recv, args[i] = v8impl::V8LocalValueFromJsValue(argv[i]); } - v8::Handle result = - node::MakeCallback(isolate, v8recv, v8func, argc, args.data()); - return v8impl::JsValueFromV8LocalValue(result); + v8::Handle retval = + node::MakeCallback(isolate, v8recv, v8func, argc, args.data()); + *result = v8impl::JsValueFromV8LocalValue(retval); + + return napi_ok; } // Methods to support catching exceptions @@ -1706,32 +1679,51 @@ napi_value napi_get_and_clear_last_exception(napi_env e) { return returnValue; } -napi_value napi_buffer_new(napi_env e, char* data, uint32_t size) { +napi_status napi_buffer_new(napi_env e, char* data, uint32_t size, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - node::Buffer::New( - v8impl::V8IsolateFromJsEnv(e), data, size).ToLocalChecked()); + CHECK_ARG(result); + + auto maybe = node::Buffer::New(v8impl::V8IsolateFromJsEnv(e), data, size); + + CHECK_MAYBE_EMPTY(maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return napi_ok; } -napi_value napi_buffer_copy(napi_env e, const char* data, uint32_t size) { +napi_status napi_buffer_copy(napi_env e, const char* data, + uint32_t size, napi_value* result) { NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - node::Buffer::Copy( - v8impl::V8IsolateFromJsEnv(e), data, size).ToLocalChecked()); + CHECK_ARG(result); + + auto maybe = node::Buffer::Copy(v8impl::V8IsolateFromJsEnv(e), data, size); + + CHECK_MAYBE_EMPTY(maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return napi_ok; } -bool napi_buffer_has_instance(napi_env e, napi_value v) { +napi_status napi_buffer_has_instance(napi_env e, napi_value v, bool* result) { NAPI_PREAMBLE(e); - return node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(v)); + CHECK_ARG(result); + + *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(v)); + return napi_ok; } -char* napi_buffer_data(napi_env e, napi_value v) { +napi_status napi_buffer_data(napi_env e, napi_value v, char** result) { NAPI_PREAMBLE(e); - return node::Buffer::Data( - v8impl::V8LocalValueFromJsValue(v).As()); + CHECK_ARG(result); + + *result = node::Buffer::Data(v8impl::V8LocalValueFromJsValue(v).As()); + return napi_ok; } -size_t napi_buffer_length(napi_env e, napi_value v) { +napi_status napi_buffer_length(napi_env e, napi_value v, size_t* result) { NAPI_PREAMBLE(e); - return node::Buffer::Length(v8impl::V8LocalValueFromJsValue(v)); + CHECK_ARG(result); + + *result = node::Buffer::Length(v8impl::V8LocalValueFromJsValue(v)); + return napi_ok; } diff --git a/src/node_jsvmapi.h b/src/node_jsvmapi.h index bfb1d21341..c802829066 100644 --- a/src/node_jsvmapi.h +++ b/src/node_jsvmapi.h @@ -116,19 +116,22 @@ enum napi_valuetype { napi_function, }; -enum napi_errorvalue { +enum napi_status { napi_ok, napi_invalid_arg, napi_object_expected, napi_string_expected, - napi_generic_failure + napi_function_expected, + napi_generic_failure, + napi_pending_exception, + napi_status_last }; struct napi_extended_error_info { const char* error_message; void* engine_reserved; uint32_t engine_error_code; - napi_errorvalue error_code; + napi_status error_code; napi_extended_error_info() : error_message(NULL), @@ -142,105 +145,116 @@ struct napi_extended_error_info { NODE_EXTERN const napi_extended_error_info* napi_get_last_error_info(); // Environment -NODE_EXTERN napi_errorvalue napi_get_current_env(napi_env* e); +NODE_EXTERN napi_status napi_get_current_env(napi_env* e); // Getters for defined singletons -NODE_EXTERN napi_errorvalue napi_get_undefined(napi_env e, napi_value* result); -NODE_EXTERN napi_errorvalue napi_get_null(napi_env e, napi_value* result); -NODE_EXTERN napi_errorvalue napi_get_false(napi_env e, napi_value* result); -NODE_EXTERN napi_errorvalue napi_get_true(napi_env e, napi_value* result); -NODE_EXTERN napi_errorvalue napi_get_global_scope(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_get_undefined(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_get_null(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_get_false(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_get_true(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_get_global_scope(napi_env e, napi_value* result); // Methods to create Primitive types/Objects -NODE_EXTERN napi_errorvalue napi_create_object(napi_env e, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_array(napi_env e, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_array_with_length(napi_env e, int length, +NODE_EXTERN napi_status napi_create_object(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_create_array(napi_env e, napi_value* result); +NODE_EXTERN napi_status napi_create_array_with_length(napi_env e, int length, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_number(napi_env e, double val, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_string(napi_env e, const char* s, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_string_with_length(napi_env e, const char* s, +NODE_EXTERN napi_status napi_create_number(napi_env e, double val, napi_value* result); +NODE_EXTERN napi_status napi_create_string(napi_env e, const char* s, napi_value* result); +NODE_EXTERN napi_status napi_create_string_with_length(napi_env e, const char* s, size_t length, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_boolean(napi_env e, bool b, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_symbol(napi_env e, const char* s, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_function(napi_env e, napi_callback cb, +NODE_EXTERN napi_status napi_create_boolean(napi_env e, bool b, napi_value* result); +NODE_EXTERN napi_status napi_create_symbol(napi_env e, const char* s, napi_value* result); +NODE_EXTERN napi_status napi_create_function(napi_env e, napi_callback cb, void* data, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_error(napi_env e, napi_value msg, napi_value* result); -NODE_EXTERN napi_errorvalue napi_create_type_error(napi_env e, napi_value msg, napi_value* result); +NODE_EXTERN napi_status napi_create_error(napi_env e, napi_value msg, napi_value* result); +NODE_EXTERN napi_status napi_create_type_error(napi_env e, napi_value msg, napi_value* result); // Methods to get the the native napi_value from Primitive type -NODE_EXTERN napi_errorvalue napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* valuetype); -NODE_EXTERN napi_errorvalue napi_get_number_from_value(napi_env e, napi_value v, double* result); -NODE_EXTERN napi_errorvalue napi_get_string_from_value(napi_env e, napi_value v, +NODE_EXTERN napi_status napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* result); +NODE_EXTERN napi_status napi_get_number_from_value(napi_env e, napi_value v, double* result); +NODE_EXTERN napi_status napi_get_string_from_value(napi_env e, napi_value v, char* buf, const int buf_size, int* result); -NODE_EXTERN napi_errorvalue napi_get_value_int32(napi_env e, napi_value v, int32_t* result); -NODE_EXTERN napi_errorvalue napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result); -NODE_EXTERN napi_errorvalue napi_get_value_int64(napi_env e, napi_value v, int64_t* result); -NODE_EXTERN napi_errorvalue napi_get_value_bool(napi_env e, napi_value v, bool* result); +NODE_EXTERN napi_status napi_get_value_int32(napi_env e, napi_value v, int32_t* result); +NODE_EXTERN napi_status napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result); +NODE_EXTERN napi_status napi_get_value_int64(napi_env e, napi_value v, int64_t* result); +NODE_EXTERN napi_status napi_get_value_bool(napi_env e, napi_value v, bool* result); -NODE_EXTERN napi_errorvalue napi_get_string_length(napi_env e, napi_value v, int* result); +NODE_EXTERN napi_status napi_get_string_length(napi_env e, napi_value v, int* result); // Do we need utf16 as well? -NODE_EXTERN napi_errorvalue napi_get_string_utf8_length(napi_env e, napi_value v, int* result); -NODE_EXTERN napi_errorvalue napi_get_string_utf8(napi_env e, napi_value v, +NODE_EXTERN napi_status napi_get_string_utf8_length(napi_env e, napi_value v, int* result); +NODE_EXTERN napi_status napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bufsize, int* result); // Methods to coerce values // These APIs may execute user script -NODE_EXTERN napi_errorvalue napi_coerce_to_object(napi_env e, napi_value v, napi_value* result); -NODE_EXTERN napi_errorvalue napi_coerce_to_string(napi_env e, napi_value v, napi_value* result); +NODE_EXTERN napi_status napi_coerce_to_object(napi_env e, napi_value v, napi_value* result); +NODE_EXTERN napi_status napi_coerce_to_string(napi_env e, napi_value v, napi_value* result); // Methods to work with Objects -NODE_EXTERN napi_errorvalue napi_get_prototype(napi_env e, napi_value object, napi_value* result); -NODE_EXTERN napi_errorvalue napi_property_name(napi_env e, const char* utf8name, +NODE_EXTERN napi_status napi_get_prototype(napi_env e, napi_value object, napi_value* result); +NODE_EXTERN napi_status napi_property_name(napi_env e, const char* utf8name, napi_propertyname* result); -NODE_EXTERN napi_errorvalue napi_get_propertynames(napi_env e, napi_value object, +NODE_EXTERN napi_status napi_get_propertynames(napi_env e, napi_value object, napi_value* result); -NODE_EXTERN napi_errorvalue napi_set_property(napi_env e, napi_value object, +NODE_EXTERN napi_status napi_set_property(napi_env e, napi_value object, napi_propertyname name, napi_value v); -NODE_EXTERN napi_errorvalue napi_has_property(napi_env e, napi_value object, - napi_propertyname name, bool* has); -NODE_EXTERN napi_errorvalue napi_get_property(napi_env e, napi_value object, +NODE_EXTERN napi_status napi_has_property(napi_env e, napi_value object, + napi_propertyname name, bool* result); +NODE_EXTERN napi_status napi_get_property(napi_env e, napi_value object, napi_propertyname name, napi_value* result); -NODE_EXTERN napi_errorvalue napi_set_element(napi_env e, napi_value object, +NODE_EXTERN napi_status napi_set_element(napi_env e, napi_value object, uint32_t i, napi_value v); -NODE_EXTERN napi_errorvalue napi_has_element(napi_env e, napi_value object, - uint32_t i, bool* has); -NODE_EXTERN napi_errorvalue napi_get_element(napi_env e, napi_value object, +NODE_EXTERN napi_status napi_has_element(napi_env e, napi_value object, + uint32_t i, bool* result); +NODE_EXTERN napi_status napi_get_element(napi_env e, napi_value object, uint32_t i, napi_value* result); -NODE_EXTERN napi_errorvalue napi_define_property(napi_env e, napi_value object, - napi_property_descriptor* property); - +NODE_EXTERN napi_status napi_define_property(napi_env e, napi_value object, + napi_property_descriptor* property); // Methods to work with Arrays -NODE_EXTERN bool napi_is_array(napi_env e, napi_value v); -NODE_EXTERN uint32_t napi_get_array_length(napi_env e, napi_value v); +NODE_EXTERN napi_status napi_is_array(napi_env e, napi_value v, bool* result); +NODE_EXTERN napi_status napi_get_array_length(napi_env e, + napi_value v, uint32_t* result); // Methods to compare values -NODE_EXTERN bool napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs); - +NODE_EXTERN napi_status napi_strict_equals(napi_env e, napi_value lhs, + napi_value rhs, bool* result); // Methods to work with Functions -NODE_EXTERN void napi_set_function_name(napi_env e, napi_value func, +NODE_EXTERN napi_status napi_set_function_name(napi_env e, napi_value func, napi_propertyname napi_value); -NODE_EXTERN napi_value napi_call_function(napi_env e, napi_value scope, - napi_value func, - int argc, napi_value* argv); -NODE_EXTERN napi_value napi_new_instance(napi_env e, napi_value cons, - int argc, napi_value* argv); -NODE_EXTERN bool napi_instanceof(napi_env e, napi_value obj, napi_value cons); +NODE_EXTERN napi_status napi_call_function(napi_env e, + napi_value scope, + napi_value func, + int argc, + napi_value* argv, + napi_value* result); + +NODE_EXTERN napi_status napi_new_instance(napi_env e, + napi_value cons, + int argc, + napi_value* argv, + napi_value* result); + +NODE_EXTERN napi_status napi_instanceof(napi_env e, napi_value obj, + napi_value cons, bool* result); // Temporary method needed to support wrapping JavascriptObject in an external // object wrapper capable of storing external data. This workaround is only // required by ChakraCore and should be removed when we have a method of // constructing external objects from the constructor function itself. -NODE_EXTERN napi_value napi_make_external(napi_env e, napi_value v); +NODE_EXTERN napi_status napi_make_external(napi_env e, napi_value v, napi_value* result); // Napi version of node::MakeCallback(...) -NODE_EXTERN napi_value napi_make_callback(napi_env e, napi_value recv, - napi_value func, - int argc, napi_value* argv); - +NODE_EXTERN napi_status napi_make_callback(napi_env e, + napi_value recv, + napi_value func, + int argc, + napi_value* argv, + napi_value* result); // Methods to work with napi_callbacks NODE_EXTERN int napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo); @@ -251,10 +265,9 @@ NODE_EXTERN napi_value napi_get_cb_this(napi_env e, napi_callback_info cbinfo); NODE_EXTERN napi_value napi_get_cb_holder(napi_env e, napi_callback_info cbinfo); NODE_EXTERN void* napi_get_cb_data(napi_env e, napi_callback_info cbinfo); NODE_EXTERN bool napi_is_construct_call(napi_env e, napi_callback_info cbinfo); -NODE_EXTERN void napi_set_return_value(napi_env e, napi_callback_info cbinfo, +NODE_EXTERN napi_status napi_set_return_value(napi_env e, napi_callback_info cbinfo, napi_value v); - // Methods to support ObjectWrap // Consider: current implementation for supporting ObjectWrap pattern is // difficult to implement for other VMs because of the dependence on node @@ -265,38 +278,39 @@ NODE_EXTERN void napi_set_return_value(napi_env e, napi_callback_info cbinfo, // best way to attach external data to a javascript object. Perhaps // instead NAPI should do an external data concept like JsSetExternalData // and use that for "wrapping a native object". -NODE_EXTERN void napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, +NODE_EXTERN napi_status napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, napi_destruct napi_destructor, napi_weakref* handle); -NODE_EXTERN void* napi_unwrap(napi_env e, napi_value jsObject); -NODE_EXTERN napi_value napi_create_constructor( - napi_env e, - char* utf8name, - napi_callback cb, - void* data, - int property_count, - napi_property_descriptor* properties); +NODE_EXTERN napi_status napi_unwrap(napi_env e, napi_value jsObject, void** result); + +NODE_EXTERN napi_status napi_create_constructor(napi_env e, + char* utf8name, + napi_callback cb, + void* data, + int property_count, + napi_property_descriptor* properties, + napi_value* result); // Methods to control object lifespan -NODE_EXTERN napi_persistent napi_create_persistent(napi_env e, napi_value v); -NODE_EXTERN void napi_release_persistent(napi_env e, napi_persistent p); -NODE_EXTERN napi_value napi_get_persistent_value(napi_env e, napi_persistent p); -NODE_EXTERN napi_weakref napi_create_weakref(napi_env e, napi_value v); -NODE_EXTERN bool napi_get_weakref_value(napi_env e, napi_weakref w, - napi_value *pv); -NODE_EXTERN void napi_release_weakref(napi_env e, napi_weakref w); +NODE_EXTERN napi_status napi_create_persistent(napi_env e, napi_value v, napi_persistent* result); +NODE_EXTERN napi_status napi_release_persistent(napi_env e, napi_persistent p); +NODE_EXTERN napi_status napi_get_persistent_value(napi_env e, napi_persistent p, napi_value* result); + +NODE_EXTERN napi_status napi_create_weakref(napi_env e, napi_value v, napi_weakref* result); +NODE_EXTERN napi_status napi_get_weakref_value(napi_env e, napi_weakref w, + napi_value* result); +NODE_EXTERN napi_status napi_release_weakref(napi_env e, napi_weakref w); + NODE_EXTERN napi_handle_scope napi_open_handle_scope(napi_env e); NODE_EXTERN void napi_close_handle_scope(napi_env e, napi_handle_scope s); NODE_EXTERN napi_escapable_handle_scope napi_open_escapable_handle_scope(napi_env e); -NODE_EXTERN void napi_close_escapable_handle_scope( - napi_env e, - napi_escapable_handle_scope s); +NODE_EXTERN void napi_close_escapable_handle_scope(napi_env e, + napi_escapable_handle_scope s); NODE_EXTERN napi_value napi_escape_handle(napi_env e, napi_escapable_handle_scope s, napi_value v); - // Methods to support error handling NODE_EXTERN void napi_throw(napi_env e, napi_value error); NODE_EXTERN void napi_throw_error(napi_env e, const char* msg); @@ -307,13 +321,17 @@ NODE_EXTERN bool napi_is_exception_pending(napi_env e); NODE_EXTERN napi_value napi_get_and_clear_last_exception(napi_env e); // Methods to provide node::Buffer functionality with napi types -NODE_EXTERN napi_value napi_buffer_new(napi_env e, - char* data, uint32_t size); -NODE_EXTERN napi_value napi_buffer_copy(napi_env e, - const char* data, uint32_t size); -NODE_EXTERN bool napi_buffer_has_instance(napi_env e, napi_value v); -NODE_EXTERN char* napi_buffer_data(napi_env e, napi_value v); -NODE_EXTERN size_t napi_buffer_length(napi_env e, napi_value v); +NODE_EXTERN napi_status napi_buffer_new(napi_env e, + char* data, + uint32_t size, + napi_value* result); +NODE_EXTERN napi_status napi_buffer_copy(napi_env e, + const char* data, + uint32_t size, + napi_value* result); +NODE_EXTERN napi_status napi_buffer_has_instance(napi_env e, napi_value v, bool* result); +NODE_EXTERN napi_status napi_buffer_data(napi_env e, napi_value v, char** result); +NODE_EXTERN napi_status napi_buffer_length(napi_env e, napi_value v, size_t* result); } // extern "C" #endif // SRC_NODE_JSVMAPI_H__ From 8cfbf3aa994fe8fbd642042c04ba343edc9dcfaf Mon Sep 17 00:00:00 2001 From: Taylor Woll Date: Sun, 29 Jan 2017 19:28:48 -0800 Subject: [PATCH 4/4] Add more error handling instrumentation Refactor some of the helper macros, add error handling to the entire public NAPI surface area. --- src/node_jsvmapi.cc | 330 ++++++++++++++++++++++++++++---------------- src/node_jsvmapi.h | 70 +++++----- 2 files changed, 250 insertions(+), 150 deletions(-) diff --git a/src/node_jsvmapi.cc b/src/node_jsvmapi.cc index f171a1585a..43af867fe8 100644 --- a/src/node_jsvmapi.cc +++ b/src/node_jsvmapi.cc @@ -370,23 +370,22 @@ namespace v8impl { const v8::Local& _value; }; - class ObjectWrapWrapper: public node::ObjectWrap { + class ObjectWrapWrapper: public node::ObjectWrap { public: - ObjectWrapWrapper(napi_value jsObject, void* nativeObj, + ObjectWrapWrapper(v8::Local jsObject, void* nativeObj, napi_destruct destructor) { _destructor = destructor; _nativeObj = nativeObj; - Wrap(V8LocalValueFromJsValue(jsObject)->ToObject()); + Wrap(jsObject); } void* getValue() { return _nativeObj; } - static void* Unwrap(napi_value jsObject) { + static void* Unwrap(v8::Local jsObject) { ObjectWrapWrapper* wrapper = - ObjectWrap::Unwrap( - V8LocalValueFromJsValue(jsObject)->ToObject()); + ObjectWrap::Unwrap(jsObject); return wrapper->getValue(); } @@ -465,55 +464,62 @@ namespace v8impl { } // end of namespace v8impl -#define RETURN_STATUS_IF_FAIL(condition, status) \ +#define RETURN_STATUS_IF_FALSE(condition, status) \ do { \ if (!(condition)) { \ - return napi_set_last_error_code((status)); \ + return napi_set_last_error((status)); \ } \ } while(0) #define CHECK_ARG(arg) \ - RETURN_STATUS_IF_FAIL((arg), napi_invalid_arg) + RETURN_STATUS_IF_FALSE((arg), napi_invalid_arg) #define CHECK_MAYBE_EMPTY(maybe, status) \ - RETURN_STATUS_IF_FAIL(!((maybe).IsEmpty()), (status)) + RETURN_STATUS_IF_FALSE(!((maybe).IsEmpty()), (status)) #define CHECK_MAYBE_NOTHING(maybe, status) \ - RETURN_STATUS_IF_FAIL(!((maybe).IsNothing()), (status)) + RETURN_STATUS_IF_FALSE(!((maybe).IsNothing()), (status)) #define NAPI_PREAMBLE(e) \ do { \ - RETURN_STATUS_IF_FAIL(v8impl::TryCatch::lastException().IsEmpty(), \ + CHECK_ARG(e); \ + RETURN_STATUS_IF_FALSE(v8impl::TryCatch::lastException().IsEmpty(), \ napi_pending_exception); \ - napi_clear_last_error_code(); \ + napi_clear_last_error(); \ v8impl::TryCatch tryCatch(v8impl::V8IsolateFromJsEnv((e))); \ } while(0) -#define CHECK_TO_OBJECT(result, o) \ +#define CHECK_TO_TYPE(type, context, result, src, status) \ do { \ - auto obj_maybe = \ - v8impl::V8LocalValueFromJsValue((o))->ToObject(context); \ - CHECK_MAYBE_EMPTY(obj_maybe, napi_object_expected); \ - result = obj_maybe.ToLocalChecked(); \ + auto maybe = \ + v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY(maybe, (status)); \ + result = maybe.ToLocalChecked(); \ } while(0) -#define CHECK_NEW_FROM_UTF8_LEN(result, str, len) \ +#define CHECK_TO_OBJECT(context, result, src) \ + CHECK_TO_TYPE(Object, context, result, src, napi_object_expected) + +#define CHECK_TO_STRING(context, result, src) \ + CHECK_TO_TYPE(String, context, result, src, napi_string_expected) + +#define CHECK_NEW_FROM_UTF8_LEN(isolate, result, str, len) \ do { \ - auto name_maybe = v8::String::NewFromUtf8(isolate, (str), \ - v8::NewStringType::kInternalized, len); \ + auto name_maybe = v8::String::NewFromUtf8((isolate), (str), \ + v8::NewStringType::kInternalized, (len)); \ CHECK_MAYBE_EMPTY(name_maybe, napi_generic_failure); \ result = name_maybe.ToLocalChecked(); \ } while(0) -#define CHECK_NEW_FROM_UTF8(result, str) \ - CHECK_NEW_FROM_UTF8_LEN((result), (str), -1) +#define CHECK_NEW_FROM_UTF8(isolate, result, str) \ + CHECK_NEW_FROM_UTF8_LEN((isolate), (result), (str), -1) - // Static last error returned from napi_get_last_error_info +// Static last error returned from napi_get_last_error_info napi_extended_error_info static_last_error; // Warning: Keep in-sync with napi_status enum const char* error_messages[] = { - NULL, + nullptr, "Invalid pointer passed as argument", "An object was expected", "A string was expected", @@ -522,9 +528,12 @@ const char* error_messages[] = { "An exception is pending" }; -void napi_clear_last_error_code() { - // TODO: Should this also clear engine_specific fields? +void napi_clear_last_error() { static_last_error.error_code = napi_ok; + + // TODO: Should this be a callback? + static_last_error.engine_error_code = 0; + static_last_error.engine_reserved = nullptr; } const napi_extended_error_info* napi_get_last_error_info() { @@ -537,15 +546,9 @@ const napi_extended_error_info* napi_get_last_error_info() { return &static_last_error; } -void napi_clear_extended_error_info() { - // TODO: Should this be a callback? - static_last_error.engine_error_code = 0; - static_last_error.engine_reserved = NULL; -} - -napi_status napi_set_last_error_code(napi_status error_code, - uint32_t engine_error_code = 0, - void* engine_reserved = NULL) { +napi_status napi_set_last_error(napi_status error_code, + uint32_t engine_error_code = 0, + void* engine_reserved = nullptr) { static_last_error.error_code = error_code; static_last_error.engine_error_code = engine_error_code; static_last_error.engine_reserved = engine_reserved; @@ -576,6 +579,9 @@ napi_status napi_create_function( v8::Local cbdata = v8impl::CreateFunctionCallbackData(e, cb, data); + + RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure); + v8::Local tpl = v8::FunctionTemplate::New( isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); @@ -603,6 +609,9 @@ napi_status napi_create_constructor( v8::EscapableHandleScope scope(isolate); v8::Local cbdata = v8impl::CreateFunctionCallbackData(e, cb, data); + + RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure); + v8::Local tpl = v8::FunctionTemplate::New( isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); @@ -610,13 +619,13 @@ napi_status napi_create_constructor( tpl->InstanceTemplate()->SetInternalFieldCount(1); v8::Local namestring; - CHECK_NEW_FROM_UTF8(namestring, utf8name); + CHECK_NEW_FROM_UTF8(isolate, namestring, utf8name); tpl->SetClassName(namestring); for (int i = 0; i < property_count; i++) { napi_property_descriptor* p = properties + i; v8::Local propertyname; - CHECK_NEW_FROM_UTF8(propertyname, p->utf8name); + CHECK_NEW_FROM_UTF8(isolate, propertyname, p->utf8name); v8::PropertyAttribute attributes = static_cast(p->attributes); @@ -627,6 +636,8 @@ napi_status napi_create_constructor( v8::Local cbdata = v8impl::CreateFunctionCallbackData( e, p->method, p->data); + RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure); + v8::Local t = v8::FunctionTemplate::New( isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata, v8::Signature::New(isolate, tpl)); @@ -691,7 +702,7 @@ napi_status napi_property_name(napi_env e, v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local name; - CHECK_NEW_FROM_UTF8(name, utf8name); + CHECK_NEW_FROM_UTF8(isolate, name, utf8name); *result = reinterpret_cast( v8impl::JsValueFromV8LocalValue(name)); @@ -706,7 +717,7 @@ napi_status napi_get_propertynames(napi_env e, napi_value o, napi_value* result) v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); auto maybe_propertynames = obj->GetPropertyNames(context); @@ -726,15 +737,14 @@ napi_status napi_set_property(napi_env e, v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); v8::Local val = v8impl::V8LocalValueFromJsValue(v); v8::Maybe set_maybe = obj->Set(context, key, val); - RETURN_STATUS_IF_FAIL(set_maybe.FromMaybe(false), napi_generic_failure); - + RETURN_STATUS_IF_FALSE(set_maybe.FromMaybe(false), napi_generic_failure); return napi_ok; } @@ -746,7 +756,7 @@ napi_status napi_has_property(napi_env e, napi_value o, napi_propertyname k, boo v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); v8::Maybe has_maybe = obj->Has(context, key); @@ -769,7 +779,7 @@ napi_status napi_get_property(napi_env e, v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); auto get_maybe = obj->Get(context, key); @@ -787,12 +797,12 @@ napi_status napi_set_element(napi_env e, napi_value o, uint32_t i, napi_value v) v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); v8::Local val = v8impl::V8LocalValueFromJsValue(v); auto set_maybe = obj->Set(context, i, val); - RETURN_STATUS_IF_FAIL(set_maybe.FromMaybe(false), napi_generic_failure); + RETURN_STATUS_IF_FALSE(set_maybe.FromMaybe(false), napi_generic_failure); return napi_ok; } @@ -805,7 +815,7 @@ napi_status napi_has_element(napi_env e, napi_value o, uint32_t i, bool* result) v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); v8::Maybe has_maybe = obj->Has(context, i); @@ -826,7 +836,7 @@ napi_status napi_get_element(napi_env e, v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); auto get_maybe = obj->Get(context, i); @@ -843,10 +853,10 @@ napi_status napi_define_property(napi_env e, napi_value o, v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); v8::Local name; - CHECK_NEW_FROM_UTF8(name, p->utf8name); + CHECK_NEW_FROM_UTF8(isolate, name, p->utf8name); v8::PropertyAttribute attributes = static_cast(p->attributes); @@ -855,6 +865,8 @@ napi_status napi_define_property(napi_env e, napi_value o, v8::Local cbdata = v8impl::CreateFunctionCallbackData( e, p->method, p->data); + RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure); + v8::Local t = v8::FunctionTemplate::New( isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); @@ -868,7 +880,7 @@ napi_status napi_define_property(napi_env e, napi_value o, // should we return a different error code if the define failed? if (define_maybe.IsNothing() || !define_maybe.FromMaybe(false)) { - return napi_set_last_error_code(napi_generic_failure); + return napi_set_last_error(napi_generic_failure); } } else if (p->getter || p->setter) { @@ -888,7 +900,7 @@ napi_status napi_define_property(napi_env e, napi_value o, // should we return a different error code if the set failed? if (set_maybe.IsNothing() || !set_maybe.FromMaybe(false)) { - return napi_set_last_error_code(napi_generic_failure); + return napi_set_last_error(napi_generic_failure); } } else { @@ -904,7 +916,7 @@ napi_status napi_define_property(napi_env e, napi_value o, // should we return a different error code if the define failed? if (define_maybe.IsNothing() || !define_maybe.FromMaybe(false)) { - return napi_set_last_error_code(napi_generic_failure); + return napi_set_last_error(napi_generic_failure); } } @@ -952,7 +964,7 @@ napi_status napi_get_prototype(napi_env e, napi_value o, napi_value* result) { v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, o); + CHECK_TO_OBJECT(context, obj, o); v8::Local val = obj->GetPrototype(); *result = v8impl::JsValueFromV8LocalValue(val); @@ -993,9 +1005,11 @@ napi_status napi_create_string(napi_env e, const char* s, napi_value* result) { NAPI_PREAMBLE(e); CHECK_ARG(result); - *result = v8impl::JsValueFromV8LocalValue( - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s)); + auto isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local str; + CHECK_NEW_FROM_UTF8(isolate, str, s); + *result = v8impl::JsValueFromV8LocalValue(str); return napi_ok; } @@ -1007,8 +1021,8 @@ napi_status napi_create_string_with_length(napi_env e, CHECK_ARG(result); auto isolate = v8impl::V8IsolateFromJsEnv(e); - v8::MaybeLocal str; - CHECK_NEW_FROM_UTF8_LEN(str, s, length); + v8::Local str; + CHECK_NEW_FROM_UTF8_LEN(isolate, str, s, length); *result = v8impl::JsValueFromV8LocalValue(str); return napi_ok; @@ -1039,11 +1053,11 @@ napi_status napi_create_symbol(napi_env e, const char* s, napi_value* result) { CHECK_ARG(result); v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(e); - if (s == NULL) { + if (s == nullptr) { *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); } else { v8::Local string; - CHECK_NEW_FROM_UTF8(string, s); + CHECK_NEW_FROM_UTF8(isolate, string, s); *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate, string)); } @@ -1144,47 +1158,79 @@ napi_status napi_get_true(napi_env e, napi_value* result) { return napi_ok; } -int napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo) { +napi_status napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo, int* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); - return info->ArgsLength(); + *result = info->ArgsLength(); + return napi_ok; } -bool napi_is_construct_call(napi_env e, napi_callback_info cbinfo) { +napi_status napi_is_construct_call(napi_env e, napi_callback_info cbinfo, bool* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); - return info->IsConstructCall(); + + *result = info->IsConstructCall(); + return napi_ok; } // copy encoded arguments into provided buffer or return direct pointer to // encoded arguments array? -void napi_get_cb_args(napi_env e, napi_callback_info cbinfo, +napi_status napi_get_cb_args(napi_env e, napi_callback_info cbinfo, napi_value* buffer, size_t bufferlength) { + NAPI_PREAMBLE(e); + CHECK_ARG(buffer); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); + info->Args(buffer, bufferlength); + return napi_ok; } -napi_value napi_get_cb_this(napi_env e, napi_callback_info cbinfo) { +napi_status napi_get_cb_this(napi_env e, napi_callback_info cbinfo, napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); - return info->This(); + + *result = info->This(); + return napi_ok; } // Holder is a V8 concept. Is not clear if this can be emulated with other VMs // AFAIK Holder should be the owner of the JS function, which should be in the // prototype chain of This, so maybe it is possible to emulate. -napi_value napi_get_cb_holder(napi_env e, napi_callback_info cbinfo) { +napi_status napi_get_cb_holder( + napi_env e, + napi_callback_info cbinfo, + napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); - return info->Holder(); + + *result = info->Holder(); + return napi_ok; } -void* napi_get_cb_data(napi_env e, napi_callback_info cbinfo) { +napi_status napi_get_cb_data(napi_env e, napi_callback_info cbinfo, void** result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); - return info->Data(); + + *result = info->Data(); + return napi_ok; } napi_status napi_call_function(napi_env e, @@ -1201,7 +1247,7 @@ napi_status napi_call_function(napi_env e, v8::Local context = isolate->GetCurrentContext(); v8::Handle v8scope; - CHECK_TO_OBJECT(v8scope, scope); + CHECK_TO_OBJECT(context, v8scope, scope); for (int i = 0; i < argc; i++) { args[i] = v8impl::V8LocalValueFromJsValue(argv[i]); @@ -1230,39 +1276,47 @@ napi_status napi_get_global_scope(napi_env e, napi_value* result) { return napi_ok; } -void napi_throw(napi_env e, napi_value error) { +napi_status napi_throw(napi_env e, napi_value error) { + NAPI_PREAMBLE(e); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); isolate->ThrowException( v8impl::V8LocalValueFromJsValue(error)); // any VM calls after this point and before returning // to the javascript invoker will fail + return napi_ok; } -void napi_throw_error(napi_env e, const char* msg) { +napi_status napi_throw_error(napi_env e, const char* msg) { + NAPI_PREAMBLE(e); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local str; + CHECK_NEW_FROM_UTF8(isolate, str, msg); - isolate->ThrowException( - v8::Exception::Error(v8::String::NewFromUtf8(isolate, msg))); + isolate->ThrowException(v8::Exception::Error(str)); // any VM calls after this point and before returning // to the javascript invoker will fail + return napi_ok; } -void napi_throw_type_error(napi_env e, const char* msg) { +napi_status napi_throw_type_error(napi_env e, const char* msg) { + NAPI_PREAMBLE(e); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local str; + CHECK_NEW_FROM_UTF8(isolate, str, msg); - isolate->ThrowException( - v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, msg))); + isolate->ThrowException(v8::Exception::TypeError(str)); // any VM calls after this point and before returning // to the javascript invoker will fail + return napi_ok; } napi_status napi_get_number_from_value(napi_env e, napi_value v, double* result) { NAPI_PREAMBLE(e); - - if (!result) { - return napi_set_last_error_code(napi_invalid_arg); - } + CHECK_ARG(result); *result = v8impl::V8LocalValueFromJsValue(v)->NumberValue(); @@ -1270,10 +1324,10 @@ napi_status napi_get_number_from_value(napi_env e, napi_value v, double* result) } napi_status napi_get_string_from_value(napi_env e, - napi_value v, - char* buf, - const int buf_size, - int* result) { + napi_value v, + char* buf, + const int buf_size, + int* result) { NAPI_PREAMBLE(e); CHECK_ARG(result); @@ -1281,7 +1335,7 @@ napi_status napi_get_string_from_value(napi_env e, napi_status err = napi_get_type_of_value(e, v, &v_type); // Consider: Should we pre-emptively set the length to zero ? - RETURN_STATUS_IF_FAIL(err == napi_ok, err); + RETURN_STATUS_IF_FALSE(err == napi_ok, err); if (v_type == napi_number) { v8::String::Utf8Value str(v8impl::V8LocalValueFromJsValue(v)); @@ -1296,7 +1350,7 @@ napi_status napi_get_string_from_value(napi_env e, } else { int len = 0; err = napi_get_string_utf8_length(e, v, &len); - RETURN_STATUS_IF_FAIL(err == napi_ok, err); + RETURN_STATUS_IF_FALSE(err == napi_ok, err); int copied = v8impl::V8LocalValueFromJsValue(v).As() ->WriteUtf8( @@ -1387,7 +1441,7 @@ napi_status napi_coerce_to_object(napi_env e, napi_value v, napi_value* result) v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); v8::Local obj; - CHECK_TO_OBJECT(obj, v); + CHECK_TO_OBJECT(context, obj, v); *result = v8impl::JsValueFromV8LocalValue(obj); return napi_ok; @@ -1399,11 +1453,11 @@ napi_status napi_coerce_to_string(napi_env e, napi_value v, napi_value* result) v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - auto string_maybe = v8impl::V8LocalValueFromJsValue(v)->ToString(context); + v8::Local str; - CHECK_MAYBE_EMPTY(string_maybe, napi_string_expected); + CHECK_TO_STRING(context, str, v); - *result = v8impl::JsValueFromV8LocalValue(string_maybe.ToLocalChecked()); + *result = v8impl::JsValueFromV8LocalValue(str); return napi_ok; } @@ -1411,8 +1465,13 @@ napi_status napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, napi_destruct destructor, napi_weakref* handle) { NAPI_PREAMBLE(e); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + CHECK_TO_OBJECT(context, obj, jsObject); + v8impl::ObjectWrapWrapper* wrap = - new v8impl::ObjectWrapWrapper(jsObject, nativeObj, destructor); + new v8impl::ObjectWrapWrapper(obj, nativeObj, destructor); if (handle) { @@ -1433,7 +1492,12 @@ napi_status napi_unwrap(napi_env e, napi_value jsObject, void** result) { NAPI_PREAMBLE(e); CHECK_ARG(result); - *result = v8impl::ObjectWrapWrapper::Unwrap(jsObject); + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + CHECK_TO_OBJECT(context, obj, jsObject); + + *result = v8impl::ObjectWrapWrapper::Unwrap(obj); return napi_ok; } @@ -1500,7 +1564,7 @@ napi_status napi_get_weakref_value(napi_env e, napi_weakref w, napi_value* resul v8::Local v = v8::Local::New(isolate, *thePersistent); if (v.IsEmpty()) { - *result = NULL; + *result = nullptr; return napi_ok; } *result = v8impl::JsValueFromV8LocalValue(v); @@ -1519,31 +1583,53 @@ napi_status napi_release_weakref(napi_env e, napi_weakref w) { return napi_ok; } -napi_handle_scope napi_open_handle_scope(napi_env e) { - return v8impl::JsHandleScopeFromV8HandleScope( +napi_status napi_open_handle_scope(napi_env e, napi_handle_scope* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + *result = v8impl::JsHandleScopeFromV8HandleScope( new v8impl::HandleScopeWrapper(v8impl::V8IsolateFromJsEnv(e))); + return napi_ok; } -void napi_close_handle_scope(napi_env e, napi_handle_scope scope) { +napi_status napi_close_handle_scope(napi_env e, napi_handle_scope scope) { + NAPI_PREAMBLE(e); + CHECK_ARG(scope); + delete v8impl::V8HandleScopeFromJsHandleScope(scope); + return napi_ok; } -napi_escapable_handle_scope napi_open_escapable_handle_scope(napi_env e) { - return v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( + napi_status napi_open_escapable_handle_scope(napi_env e, + napi_escapable_handle_scope* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + *result =v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( new v8impl::EscapableHandleScopeWrapper(v8impl::V8IsolateFromJsEnv(e))); + return napi_ok; } -void napi_close_escapable_handle_scope(napi_env e, - napi_escapable_handle_scope scope) { + napi_status napi_close_escapable_handle_scope(napi_env e, + napi_escapable_handle_scope scope) { + NAPI_PREAMBLE(e); + CHECK_ARG(scope); + delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + return napi_ok; } -napi_value napi_escape_handle(napi_env e, napi_escapable_handle_scope scope, - napi_value escapee) { + napi_status napi_escape_handle(napi_env e, napi_escapable_handle_scope scope, + napi_value escapee, napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(scope); + CHECK_ARG(result); + v8impl::EscapableHandleScopeWrapper* s = v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); - return v8impl::JsValueFromV8LocalValue( + *result = v8impl::JsValueFromV8LocalValue( s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); + return napi_ok; } napi_status napi_new_instance(napi_env e, @@ -1582,15 +1668,15 @@ napi_status napi_instanceof(napi_env e, napi_value obj, napi_value cons, bool* r v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); - CHECK_TO_OBJECT(v8Cons, cons); + CHECK_TO_OBJECT(context, v8Cons, cons); if (!v8Cons->IsFunction()) { napi_throw_type_error(e, "constructor must be a function"); - return napi_set_last_error_code(napi_function_expected); + return napi_set_last_error(napi_function_expected); } - CHECK_NEW_FROM_UTF8(prototypeString, "prototype"); + CHECK_NEW_FROM_UTF8(isolate, prototypeString, "prototype"); auto maybe = v8Cons->Get(context, prototypeString); @@ -1601,7 +1687,7 @@ napi_status napi_instanceof(napi_env e, napi_value obj, napi_value cons, bool* r if (!prototypeProperty->IsObject()) { napi_throw_type_error(e, "constructor prototype must be an object"); - return napi_set_last_error_code(napi_object_expected); + return napi_set_last_error(napi_object_expected); } v8::Local v8Obj = v8impl::V8LocalValueFromJsValue(obj); @@ -1617,7 +1703,7 @@ napi_status napi_instanceof(napi_env e, napi_value obj, napi_value cons, bool* r break; } v8::Local obj; - CHECK_TO_OBJECT(obj, v8impl::JsValueFromV8LocalValue(v8Obj)); + CHECK_TO_OBJECT(context, obj, v8impl::JsValueFromV8LocalValue(v8Obj)); v8Obj = obj->GetPrototype(); } } @@ -1660,23 +1746,29 @@ napi_status napi_make_callback(napi_env e, } // Methods to support catching exceptions -bool napi_is_exception_pending(napi_env) { - return !v8impl::TryCatch::lastException().IsEmpty(); +napi_status napi_is_exception_pending(napi_env e, bool* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + *result = !v8impl::TryCatch::lastException().IsEmpty(); + return napi_ok; } -napi_value napi_get_and_clear_last_exception(napi_env e) { +napi_status napi_get_and_clear_last_exception(napi_env e, napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); // TODO: Is there a chance that an exception will be thrown in the process of // attempting to retrieve the global static exception? - napi_value returnValue = nullptr; if (v8impl::TryCatch::lastException().IsEmpty()) { - napi_get_undefined(e, &returnValue); + return napi_get_undefined(e, result); } else { - returnValue = v8impl::JsValueFromV8LocalValue( + *result = v8impl::JsValueFromV8LocalValue( v8impl::TryCatch::lastException().Get(v8impl::V8IsolateFromJsEnv(e))); v8impl::TryCatch::lastException().Reset(); } - return returnValue; + + return napi_ok; } napi_status napi_buffer_new(napi_env e, char* data, uint32_t size, napi_value* result) { diff --git a/src/node_jsvmapi.h b/src/node_jsvmapi.h index c802829066..69d36e61cd 100644 --- a/src/node_jsvmapi.h +++ b/src/node_jsvmapi.h @@ -11,9 +11,8 @@ * requiring all operations to go across the DLL boundary, i.e. no inlining * even for operations such as asking for the type of a napi_value or retrieving a * function napi_callback's arguments. - * - The V8 implementation of the API is roughly hacked together with no - * attention paid to error handling or fault tolerance. - * - Error handling in general is not included in the API at this time. + * - The V8 implementation of the API is roughly hacked together with only basic + * error handling or fault tolerance. * ******************************************************************************/ #ifndef SRC_NODE_JSVMAPI_H_ @@ -257,16 +256,21 @@ NODE_EXTERN napi_status napi_make_callback(napi_env e, napi_value* result); // Methods to work with napi_callbacks -NODE_EXTERN int napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo); -NODE_EXTERN void napi_get_cb_args(napi_env e, napi_callback_info cbinfo, +NODE_EXTERN napi_status napi_get_cb_args_length(napi_env e, + napi_callback_info cbinfo, int* result); +NODE_EXTERN napi_status napi_get_cb_args(napi_env e, napi_callback_info cbinfo, napi_value* buffer, size_t bufferlength); -NODE_EXTERN napi_value napi_get_cb_this(napi_env e, napi_callback_info cbinfo); +NODE_EXTERN napi_status napi_get_cb_this(napi_env e, + napi_callback_info cbinfo, napi_value* result); // V8 concept; see note in .cc file -NODE_EXTERN napi_value napi_get_cb_holder(napi_env e, napi_callback_info cbinfo); -NODE_EXTERN void* napi_get_cb_data(napi_env e, napi_callback_info cbinfo); -NODE_EXTERN bool napi_is_construct_call(napi_env e, napi_callback_info cbinfo); -NODE_EXTERN napi_status napi_set_return_value(napi_env e, napi_callback_info cbinfo, - napi_value v); +NODE_EXTERN napi_status napi_get_cb_holder(napi_env e, + napi_callback_info cbinfo, napi_value* result); +NODE_EXTERN napi_status napi_get_cb_data(napi_env e, + napi_callback_info cbinfo, void** result); +NODE_EXTERN napi_status napi_is_construct_call(napi_env e, + napi_callback_info cbinfo, bool* result); +NODE_EXTERN napi_status napi_set_return_value(napi_env e, + napi_callback_info cbinfo, napi_value v); // Methods to support ObjectWrap // Consider: current implementation for supporting ObjectWrap pattern is @@ -279,8 +283,8 @@ NODE_EXTERN napi_status napi_set_return_value(napi_env e, napi_callback_info cbi // instead NAPI should do an external data concept like JsSetExternalData // and use that for "wrapping a native object". NODE_EXTERN napi_status napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, - napi_destruct napi_destructor, - napi_weakref* handle); + napi_destruct napi_destructor, + napi_weakref* handle); NODE_EXTERN napi_status napi_unwrap(napi_env e, napi_value jsObject, void** result); NODE_EXTERN napi_status napi_create_constructor(napi_env e, @@ -292,33 +296,37 @@ NODE_EXTERN napi_status napi_create_constructor(napi_env e, napi_value* result); // Methods to control object lifespan -NODE_EXTERN napi_status napi_create_persistent(napi_env e, napi_value v, napi_persistent* result); +NODE_EXTERN napi_status napi_create_persistent(napi_env e, napi_value v, + napi_persistent* result); NODE_EXTERN napi_status napi_release_persistent(napi_env e, napi_persistent p); -NODE_EXTERN napi_status napi_get_persistent_value(napi_env e, napi_persistent p, napi_value* result); +NODE_EXTERN napi_status napi_get_persistent_value(napi_env e, napi_persistent p, + napi_value* result); -NODE_EXTERN napi_status napi_create_weakref(napi_env e, napi_value v, napi_weakref* result); +NODE_EXTERN napi_status napi_create_weakref(napi_env e, napi_value v, + napi_weakref* result); NODE_EXTERN napi_status napi_get_weakref_value(napi_env e, napi_weakref w, - napi_value* result); + napi_value* result); NODE_EXTERN napi_status napi_release_weakref(napi_env e, napi_weakref w); -NODE_EXTERN napi_handle_scope napi_open_handle_scope(napi_env e); -NODE_EXTERN void napi_close_handle_scope(napi_env e, napi_handle_scope s); -NODE_EXTERN napi_escapable_handle_scope - napi_open_escapable_handle_scope(napi_env e); -NODE_EXTERN void napi_close_escapable_handle_scope(napi_env e, - napi_escapable_handle_scope s); -NODE_EXTERN napi_value napi_escape_handle(napi_env e, - napi_escapable_handle_scope s, - napi_value v); +NODE_EXTERN napi_status napi_open_handle_scope(napi_env e, napi_handle_scope* result); +NODE_EXTERN napi_status napi_close_handle_scope(napi_env e, napi_handle_scope s); +NODE_EXTERN napi_status napi_open_escapable_handle_scope(napi_env e, + napi_escapable_handle_scope* result); +NODE_EXTERN napi_status napi_close_escapable_handle_scope(napi_env e, + napi_escapable_handle_scope s); +NODE_EXTERN napi_status napi_escape_handle(napi_env e, + napi_escapable_handle_scope s, + napi_value v, + napi_value* result); // Methods to support error handling -NODE_EXTERN void napi_throw(napi_env e, napi_value error); -NODE_EXTERN void napi_throw_error(napi_env e, const char* msg); -NODE_EXTERN void napi_throw_type_error(napi_env e, const char* msg); +NODE_EXTERN napi_status napi_throw(napi_env e, napi_value error); +NODE_EXTERN napi_status napi_throw_error(napi_env e, const char* msg); +NODE_EXTERN napi_status napi_throw_type_error(napi_env e, const char* msg); // Methods to support catching exceptions -NODE_EXTERN bool napi_is_exception_pending(napi_env e); -NODE_EXTERN napi_value napi_get_and_clear_last_exception(napi_env e); +NODE_EXTERN napi_status napi_is_exception_pending(napi_env e, bool* result); +NODE_EXTERN napi_status napi_get_and_clear_last_exception(napi_env e, napi_value* result); // Methods to provide node::Buffer functionality with napi types NODE_EXTERN napi_status napi_buffer_new(napi_env e,