diff --git a/Makefile b/Makefile index 98a1615698..d0e436acd0 100644 --- a/Makefile +++ b/Makefile @@ -122,10 +122,10 @@ v8: test: all $(MAKE) build-addons - $(MAKE) build-addons-abi + $(MAKE) build-addons-napi $(MAKE) cctest $(PYTHON) tools/test.py --mode=release -J \ - addons addons-abi doctool inspector known_issues message pseudo-tty parallel sequential + addons addons-napi doctool inspector known_issues message pseudo-tty parallel sequential $(MAKE) lint test-parallel: all @@ -190,16 +190,16 @@ test/addons/.buildstamp: config.gypi \ build-addons: $(NODE_EXE) test/addons/.buildstamp ADDONS_ABI_BINDING_GYPS := \ - $(filter-out test/addons-abi/??_*/binding.gyp, \ - $(wildcard test/addons-abi/*/binding.gyp)) + $(filter-out test/addons-napi/??_*/binding.gyp, \ + $(wildcard test/addons-napi/*/binding.gyp)) -# Implicitly depends on $(NODE_EXE), see the build-addons-abi rule for rationale. -test/addons-abi/.buildstamp: $(ADDONS_ABI_BINDING_GYPS) \ +# Implicitly depends on $(NODE_EXE), see the build-addons-napi rule for rationale. +test/addons-napi/.buildstamp: $(ADDONS_ABI_BINDING_GYPS) \ deps/uv/include/*.h deps/v8/include/*.h \ src/node.h src/node_buffer.h src/node_object_wrap.h - # Cannot use $(wildcard test/addons-abi/*/) here, it's evaluated before + # Cannot use $(wildcard test/addons-napi/*/) here, it's evaluated before # embedded addons have been generated from the documentation. - for dirname in test/addons-abi/*/; do \ + for dirname in test/addons-napi/*/; do \ $(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp rebuild \ --python="$(PYTHON)" \ --directory="$$PWD/$$dirname" \ @@ -213,13 +213,13 @@ test/addons-abi/.buildstamp: $(ADDONS_ABI_BINDING_GYPS) \ # .buildstamp and .docbuildstamp are out of date and need a rebuild. # Just goes to show that recursive make really is harmful... # TODO(bnoordhuis) Force rebuild after gyp or node-gyp update. -build-addons-abi: $(NODE_EXE) test/addons-abi/.buildstamp +build-addons-napi: $(NODE_EXE) test/addons-napi/.buildstamp test-gc: all test/gc/node_modules/weak/build/Release/weakref.node $(PYTHON) tools/test.py --mode=release gc -test-build: | all build-addons build-addons-abi +test-build: | all build-addons build-addons-napi test-all: test-build test/gc/node_modules/weak/build/Release/weakref.node $(PYTHON) tools/test.py --mode=debug,release @@ -244,11 +244,11 @@ test-ci-js: $(TEST_CI_ARGS) $(CI_JS_SUITES) test-ci: LOGLEVEL := info -test-ci: | build-addons build-addons-abi +test-ci: | build-addons build-addons-napi out/Release/cctest --gtest_output=tap:cctest.tap $(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \ --mode=release --flaky-tests=$(FLAKY_TESTS) \ - $(TEST_CI_ARGS) addons-abi $(CI_NATIVE_SUITES) $(CI_JS_SUITES) + $(TEST_CI_ARGS) addons-napi $(CI_NATIVE_SUITES) $(CI_JS_SUITES) test-release: test-build $(PYTHON) tools/test.py --mode=release @@ -289,8 +289,8 @@ test-npm-publish: $(NODE_EXE) test-addons: test-build $(PYTHON) tools/test.py --mode=release addons -test-addons-abi: test-build - $(PYTHON) tools/test.py --mode=release addons-abi +test-addons-napi: test-build + $(PYTHON) tools/test.py --mode=release addons-napi test-timers: $(MAKE) --directory=tools faketime @@ -744,7 +744,7 @@ CPPLINT_EXCLUDE += src/node_root_certs.h CPPLINT_EXCLUDE += src/queue.h CPPLINT_EXCLUDE += src/tree.h CPPLINT_EXCLUDE += $(wildcard test/addons/??_*/*.cc test/addons/??_*/*.h) -CPPLINT_EXCLUDE += $(wildcard test/addons-abi/??_*/*.cc test/addons-abi/??_*/*.h) +CPPLINT_EXCLUDE += $(wildcard test/addons-napi/??_*/*.cc test/addons-napi/??_*/*.h) CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \ src/*.c \ @@ -754,8 +754,8 @@ CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \ test/addons/*/*.h \ test/cctest/*.cc \ test/cctest/*.h \ - test/addons-abi/*/*.cc \ - test/addons-abi/*/*.h \ + test/addons-napi/*/*.cc \ + test/addons-napi/*/*.h \ tools/icu/*.cc \ tools/icu/*.h \ )) @@ -788,8 +788,8 @@ endif .PHONY: lint cpplint jslint bench clean docopen docclean doc dist distclean \ check uninstall install install-includes install-bin all staticlib \ dynamiclib test test-all \ - test-addons build-addons test-addons-abi build-addons-abi \ - test-addons-abi build-addons-abi website-upload pkg \ + test-addons build-addons test-addons-napi build-addons-napi \ + test-addons-napi build-addons-napi website-upload pkg \ blog blogclean tar binary release-only bench-http-simple bench-idle \ bench-all bench bench-misc bench-array bench-buffer bench-net \ bench-http bench-fs bench-tls cctest run-ci test-v8 test-v8-intl \ diff --git a/node.gyp b/node.gyp index c62922ded3..bb741a557a 100644 --- a/node.gyp +++ b/node.gyp @@ -155,7 +155,8 @@ 'src/handle_wrap.cc', 'src/js_stream.cc', 'src/node.cc', - 'src/node_asyncapi.cc', +# 'src/node_api.cc', + 'src/node_api_jsrt.cc', 'src/node_buffer.cc', 'src/node_config.cc', 'src/node_constants.cc', @@ -164,8 +165,6 @@ 'src/node_file.cc', 'src/node_http_parser.cc', 'src/node_javascript.cc', - 'src/node_jsrtapi.cc', -# 'src/node_jsvmapi.cc', 'src/node_main.cc', 'src/node_os.cc', 'src/node_revert.cc', @@ -201,10 +200,8 @@ 'src/handle_wrap.h', 'src/js_stream.h', 'src/node.h', - 'src/node_api_helpers.h', - 'src/node_asyncapi.h', - 'src/node_asyncapi_internal.h', - 'src/node_asyncapi_types.h', + 'src/node_api.h', + 'src/node_api_types.h', 'src/node_buffer.h', 'src/node_constants.h', 'src/node_debug_options.h', @@ -213,10 +210,6 @@ 'src/node_internals.h', 'src/node_javascript.h', 'src/node_mutex.h', - 'src/node_jsvmapi.h', - 'src/node_jsvmapi_internal.h', - 'src/node_jsvmapi_types.h', - 'src/node_mutex.h', 'src/node_root_certs.h', 'src/node_version.h', 'src/node_watchdog.h', diff --git a/src/node.cc b/src/node.cc index 8a45747ef8..3ecd39981c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -29,8 +29,6 @@ #include "node_lttng.h" #endif -#include "node_jsvmapi_internal.h" - #include "ares.h" #include "async-wrap.h" #include "async-wrap-inl.h" @@ -2502,19 +2500,27 @@ void DLOpen(const FunctionCallbackInfo& args) { return; } - bool isNapiModule = mp->nm_version == -1; - - if (mp->nm_version != NODE_MODULE_VERSION && !isNapiModule) { + if (mp->nm_version != NODE_MODULE_VERSION) { char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "The module '%s'" - "\nwas compiled against a different Node.js version using" - "\nNODE_MODULE_VERSION %d. This version of Node.js requires" - "\nNODE_MODULE_VERSION %d. Please try re-compiling or " - "re-installing\nthe module (for instance, using `npm rebuild` or" - "`npm install`).", - *filename, mp->nm_version, NODE_MODULE_VERSION); + if (mp->nm_version == -1) { + snprintf(errmsg, + sizeof(errmsg), + "The module '%s'" + "\nwas compiled against the ABI-stable Node.js API (N-API)." + "\nThis feature is experimental and must be enabled on the " + "\ncommand-line by adding --napi-modules.", + *filename); + } else { + snprintf(errmsg, + sizeof(errmsg), + "The module '%s'" + "\nwas compiled against a different Node.js version using" + "\nNODE_MODULE_VERSION %d. This version of Node.js requires" + "\nNODE_MODULE_VERSION %d. Please try re-compiling or " + "re-installing\nthe module (for instance, using `npm rebuild` " + "or `npm install`).", + *filename, mp->nm_version, NODE_MODULE_VERSION); + } // NOTE: `mp` is allocated inside of the shared library's memory, calling // `uv_dlclose` will deallocate it @@ -2535,28 +2541,14 @@ void DLOpen(const FunctionCallbackInfo& args) { Local exports_string = env->exports_string(); Local exports = module->Get(exports_string)->ToObject(env->isolate()); - if (isNapiModule) { - if (mp->nm_register_func != nullptr) { - reinterpret_cast(mp->nm_register_func)( - v8impl::JsEnvFromV8Isolate(v8::Isolate::GetCurrent()), - v8impl::JsValueFromV8LocalValue(exports), - v8impl::JsValueFromV8LocalValue(module), - mp->nm_priv); - } else { - uv_dlclose(&lib); - env->ThrowError("Module has no declared entry point."); - return; - } + if (mp->nm_context_register_func != nullptr) { + mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv); + } else if (mp->nm_register_func != nullptr) { + mp->nm_register_func(exports, module, mp->nm_priv); } else { - if (mp->nm_context_register_func != nullptr) { - mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv); - } else if (mp->nm_register_func != nullptr) { - mp->nm_register_func(exports, module, mp->nm_priv); - } else { - uv_dlclose(&lib); - env->ThrowError("Module has no declared entry point."); - return; - } + uv_dlclose(&lib); + env->ThrowError("Module has no declared entry point."); + return; } // Tell coverity that 'handle' should not be freed when we return. @@ -2784,15 +2776,7 @@ static void LinkedBinding(const FunctionCallbackInfo& args) { env->context(), mod->nm_priv); } else if (mod->nm_register_func != nullptr) { - if (mod->nm_version != -1) { - mod->nm_register_func(exports, module, mod->nm_priv); - } else { - reinterpret_cast(mod->nm_register_func)( - v8impl::JsEnvFromV8Isolate(v8::Isolate::GetCurrent()), - v8impl::JsValueFromV8LocalValue(exports), - v8impl::JsValueFromV8LocalValue(module), - mod->nm_priv); - } + mod->nm_register_func(exports, module, mod->nm_priv); } else { return env->ThrowError("Linked module has no declared entry point."); } diff --git a/src/node.h b/src/node.h index 8ae0d3d378..759875c477 100644 --- a/src/node.h +++ b/src/node.h @@ -42,7 +42,6 @@ #include "v8.h" // NOLINT(build/include_order) #include "node_version.h" // NODE_MODULE_VERSION #include "tracing/trace_event.h" -#include "node_jsvmapi.h" #define NODE_MAKE_VERSION(major, minor, patch) \ ((major) * 0x1000 + (minor) * 0x100 + (patch)) @@ -393,12 +392,6 @@ typedef void (*addon_context_register_func)( v8::Local context, void* priv); -typedef void (*addon_abi_register_func)( - napi_env env, - napi_value exports, - napi_value module, - void* priv); - #define NM_F_BUILTIN 0x01 #define NM_F_LINKED 0x02 diff --git a/src/node_api.cc b/src/node_api.cc new file mode 100644 index 0000000000..49b7bcedca --- /dev/null +++ b/src/node_api.cc @@ -0,0 +1,2759 @@ +/****************************************************************************** + * Experimental prototype for demonstrating VM agnostic and ABI stable API + * for native modules to use instead of using Nan and V8 APIs directly. + * + * The current status is "Experimental" and should not be used for + * production applications. The API is still subject to change + * and as an experimental feature is NOT subject to semver. + * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "node_api.h" +#include "env-inl.h" + +static +napi_status napi_set_last_error(napi_env env, napi_status error_code, + uint32_t engine_error_code = 0, + void* engine_reserved = nullptr); +static +void napi_clear_last_error(napi_env env); + +struct napi_env__ { + explicit napi_env__(v8::Isolate* _isolate): isolate(_isolate), + has_instance_available(true), last_error() {} + ~napi_env__() { + last_exception.Reset(); + has_instance.Reset(); + } + v8::Isolate* isolate; + v8::Persistent last_exception; + v8::Persistent has_instance; + bool has_instance_available; + napi_extended_error_info last_error; +}; + +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((env), (status)); \ + } \ + } while (0) + +#define CHECK_ENV(env) \ + if ((env) == nullptr) { \ + node::FatalError(__func__, "environment(env) must not be null"); \ + } + +#define CHECK_ARG(env, arg) \ + RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) + +#define CHECK_MAYBE_EMPTY(env, maybe, status) \ + RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) + +#define CHECK_MAYBE_NOTHING(env, maybe, status) \ + RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) + +// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope +#define NAPI_PREAMBLE(env) \ + CHECK_ENV((env)); \ + RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \ + napi_pending_exception); \ + napi_clear_last_error((env)); \ + v8impl::TryCatch try_catch((env)) + +#define CHECK_TO_TYPE(env, type, context, result, src, status) \ + do { \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_TO_OBJECT(env, context, result, src) \ + CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) + +#define CHECK_TO_STRING(env, context, result, src) \ + CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) + +#define CHECK_TO_NUMBER(env, context, result, src) \ + CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) + +#define CHECK_TO_BOOL(env, context, result, src) \ + CHECK_TO_TYPE((env), Boolean, (context), (result), (src), \ + napi_boolean_expected) + +#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ + do { \ + auto str_maybe = v8::String::NewFromUtf8( \ + (env)->isolate, (str), v8::NewStringType::kInternalized, (len)); \ + CHECK_MAYBE_EMPTY((env), str_maybe, napi_generic_failure); \ + (result) = str_maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_NEW_FROM_UTF8(env, result, str) \ + CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), -1) + +#define GET_RETURN_STATUS(env) \ + (!try_catch.HasCaught() ? napi_ok \ + : napi_set_last_error((env), napi_pending_exception)) + +namespace { +namespace v8impl { + +// convert from n-api property attributes to v8::PropertyAttribute +static inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( + const napi_property_descriptor* descriptor) { + unsigned int attribute_flags = v8::PropertyAttribute::None; + + if (descriptor->getter != nullptr || descriptor->setter != nullptr) { + // The napi_writable attribute is ignored for accessor descriptors, but + // V8 requires the ReadOnly attribute to match nonexistence of a setter. + attribute_flags |= (descriptor->setter == nullptr ? + v8::PropertyAttribute::ReadOnly : v8::PropertyAttribute::None); + } else if ((descriptor->attributes & napi_writable) == 0) { + attribute_flags |= v8::PropertyAttribute::ReadOnly; + } + + if ((descriptor->attributes & napi_enumerable) == 0) { + attribute_flags |= v8::PropertyAttribute::DontEnum; + } + if ((descriptor->attributes & napi_configurable) == 0) { + attribute_flags |= v8::PropertyAttribute::DontDelete; + } + + return static_cast(attribute_flags); +} + +class HandleScopeWrapper { + public: + explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {} + + private: + v8::HandleScope scope; +}; + +// In node v0.10 version of v8, there is no EscapableHandleScope and the +// node v0.10 port use HandleScope::Close(Local v) to mimic the behavior +// of a EscapableHandleScope::Escape(Local v), but it is not the same +// semantics. This is an example of where the api abstraction fail to work +// across different versions. +class EscapableHandleScopeWrapper { + public: + explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {} + template + v8::Local Escape(v8::Local handle) { + return scope.Escape(handle); + } + + private: + v8::EscapableHandleScope scope; +}; + +napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) { + return reinterpret_cast(s); +} + +HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) { + return reinterpret_cast(s); +} + +napi_escapable_handle_scope JsEscapableHandleScopeFromV8EscapableHandleScope( + EscapableHandleScopeWrapper* s) { + return reinterpret_cast(s); +} + +EscapableHandleScopeWrapper* +V8EscapableHandleScopeFromJsEscapableHandleScope( + napi_escapable_handle_scope s) { + return reinterpret_cast(s); +} + +//=== Conversion between V8 Handles and napi_value ======================== + +// This asserts v8::Local<> will always be implemented with a single +// pointer field so that we can pass it around as a void*. +static_assert(sizeof(v8::Local) == sizeof(napi_value), + "Cannot convert between v8::Local and napi_value"); + +napi_value JsValueFromV8LocalValue(v8::Local local) { + return reinterpret_cast(*local); +} + +v8::Local V8LocalValueFromJsValue(napi_value v) { + v8::Local local; + memcpy(&local, &v, sizeof(v)); + return local; +} + +static inline napi_status V8NameFromPropertyDescriptor(napi_env env, + const napi_property_descriptor* p, + v8::Local* result) { + if (p->utf8name != nullptr) { + CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); + } else { + v8::Local property_value = + v8impl::V8LocalValueFromJsValue(p->name); + + RETURN_STATUS_IF_FALSE(env, property_value->IsName(), napi_name_expected); + *result = property_value.As(); + } + + return napi_ok; +} + +// Adapter for napi_finalize callbacks. +class Finalizer { + protected: + Finalizer(napi_env env, + napi_finalize finalize_callback, + void* finalize_data, + void* finalize_hint) + : _env(env), + _finalize_callback(finalize_callback), + _finalize_data(finalize_data), + _finalize_hint(finalize_hint) { + } + + ~Finalizer() { + } + + public: + static Finalizer* New(napi_env env, + napi_finalize finalize_callback = nullptr, + void* finalize_data = nullptr, + void* finalize_hint = nullptr) { + return new Finalizer( + env, finalize_callback, finalize_data, finalize_hint); + } + + static void Delete(Finalizer* finalizer) { + delete finalizer; + } + + // node::Buffer::FreeCallback + static void FinalizeBufferCallback(char* data, void* hint) { + Finalizer* finalizer = static_cast(hint); + if (finalizer->_finalize_callback != nullptr) { + finalizer->_finalize_callback( + finalizer->_env, + data, + finalizer->_finalize_hint); + } + + Delete(finalizer); + } + + protected: + napi_env _env; + napi_finalize _finalize_callback; + void* _finalize_data; + void* _finalize_hint; +}; + +// Wrapper around v8::Persistent that implements reference counting. +class Reference : private Finalizer { + private: + Reference(napi_env env, + v8::Local value, + uint32_t initial_refcount, + bool delete_self, + napi_finalize finalize_callback, + void* finalize_data, + void* finalize_hint) + : Finalizer(env, finalize_callback, finalize_data, finalize_hint), + _persistent(env->isolate, value), + _refcount(initial_refcount), + _delete_self(delete_self) { + if (initial_refcount == 0) { + _persistent.SetWeak( + this, FinalizeCallback, v8::WeakCallbackType::kParameter); + _persistent.MarkIndependent(); + } + } + + ~Reference() { + // The V8 Persistent class currently does not reset in its destructor: + // see NonCopyablePersistentTraits::kResetInDestructor = false. + // (Comments there claim that might change in the future.) + // To avoid memory leaks, it is better to reset at this time, however + // care must be taken to avoid attempting this after the Isolate has + // shut down, for example via a static (atexit) destructor. + _persistent.Reset(); + } + + public: + static Reference* New(napi_env env, + v8::Local value, + uint32_t initial_refcount, + bool delete_self, + napi_finalize finalize_callback = nullptr, + void* finalize_data = nullptr, + void* finalize_hint = nullptr) { + return new Reference(env, + value, + initial_refcount, + delete_self, + finalize_callback, + finalize_data, + finalize_hint); + } + + static void Delete(Reference* reference) { + delete reference; + } + + uint32_t Ref() { + if (++_refcount == 1) { + _persistent.ClearWeak(); + } + + return _refcount; + } + + uint32_t Unref() { + if (_refcount == 0) { + return 0; + } + if (--_refcount == 0) { + _persistent.SetWeak( + this, FinalizeCallback, v8::WeakCallbackType::kParameter); + _persistent.MarkIndependent(); + } + + return _refcount; + } + + uint32_t RefCount() { + return _refcount; + } + + v8::Local Get() { + if (_persistent.IsEmpty()) { + return v8::Local(); + } else { + return v8::Local::New(_env->isolate, _persistent); + } + } + + private: + static void FinalizeCallback(const v8::WeakCallbackInfo& data) { + Reference* reference = data.GetParameter(); + reference->_persistent.Reset(); + + // Check before calling the finalize callback, because the callback might + // delete it. + bool delete_self = reference->_delete_self; + + if (reference->_finalize_callback != nullptr) { + reference->_finalize_callback( + reference->_env, + reference->_finalize_data, + reference->_finalize_hint); + } + + if (delete_self) { + Delete(reference); + } + } + + v8::Persistent _persistent; + uint32_t _refcount; + bool _delete_self; +}; + +class TryCatch : public v8::TryCatch { + public: + explicit TryCatch(napi_env env) + : v8::TryCatch(env->isolate), _env(env) {} + + ~TryCatch() { + if (HasCaught()) { + _env->last_exception.Reset(_env->isolate, Exception()); + } + } + + private: + napi_env _env; +}; + +//=== Function napi_callback wrapper ================================= + +static const int kDataIndex = 0; +static const int kEnvIndex = 1; + +static const int kFunctionIndex = 2; +static const int kFunctionFieldCount = 3; + +static const int kGetterIndex = 2; +static const int kSetterIndex = 3; +static const int kAccessorFieldCount = 4; + +// Base class extended by classes that wrap V8 function and property callback +// info. +class CallbackWrapper { + public: + CallbackWrapper(napi_value this_arg, size_t args_length, void* data) + : _this(this_arg), _args_length(args_length), _data(data) {} + + virtual bool IsConstructCall() = 0; + virtual void Args(napi_value* buffer, size_t bufferlength) = 0; + virtual void SetReturnValue(napi_value value) = 0; + + napi_value This() { return _this; } + + size_t ArgsLength() { return _args_length; } + + void* Data() { return _data; } + + protected: + const napi_value _this; + const size_t _args_length; + void* _data; +}; + +template +class CallbackWrapperBase : public CallbackWrapper { + public: + CallbackWrapperBase(const Info& cbinfo, const size_t args_length) + : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), + args_length, + nullptr), + _cbinfo(cbinfo), + _cbdata(v8::Local::Cast(cbinfo.Data())) { + _data = v8::Local::Cast(_cbdata->GetInternalField(kDataIndex)) + ->Value(); + } + + /*virtual*/ + bool IsConstructCall() override { return false; } + + protected: + void InvokeCallback() { + napi_callback_info cbinfo_wrapper = reinterpret_cast( + static_cast(this)); + napi_callback cb = reinterpret_cast( + v8::Local::Cast( + _cbdata->GetInternalField(kInternalFieldIndex))->Value()); + v8::Isolate* isolate = _cbinfo.GetIsolate(); + + napi_env env = static_cast( + v8::Local::Cast( + _cbdata->GetInternalField(kEnvIndex))->Value()); + + // Make sure any errors encountered last time we were in N-API are gone. + napi_clear_last_error(env); + + napi_value result = cb(env, cbinfo_wrapper); + + if (result != nullptr) { + this->SetReturnValue(result); + } + + if (!env->last_exception.IsEmpty()) { + isolate->ThrowException( + v8::Local::New(isolate, env->last_exception)); + env->last_exception.Reset(); + } + } + + const Info& _cbinfo; + const v8::Local _cbdata; +}; + +class FunctionCallbackWrapper + : public CallbackWrapperBase, + kFunctionIndex> { + public: + static void Invoke(const v8::FunctionCallbackInfo& info) { + FunctionCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } + + explicit FunctionCallbackWrapper( + const v8::FunctionCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, cbinfo.Length()) {} + + /*virtual*/ + bool IsConstructCall() override { return _cbinfo.IsConstructCall(); } + + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override { + size_t i = 0; + size_t min = std::min(buffer_length, _args_length); + + for (; i < min; i += 1) { + buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]); + } + + if (i < buffer_length) { + napi_value undefined = + v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); + for (; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + + /*virtual*/ + void SetReturnValue(napi_value value) override { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + _cbinfo.GetReturnValue().Set(val); + } +}; + +class GetterCallbackWrapper + : public CallbackWrapperBase, + kGetterIndex> { + public: + static void Invoke(v8::Local property, + const v8::PropertyCallbackInfo& info) { + GetterCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } + + explicit GetterCallbackWrapper( + const v8::PropertyCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, 0) {} + + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override { + if (buffer_length > 0) { + napi_value undefined = + v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate())); + for (size_t i = 0; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + + /*virtual*/ + void SetReturnValue(napi_value value) override { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + _cbinfo.GetReturnValue().Set(val); + } +}; + +class SetterCallbackWrapper + : public CallbackWrapperBase, kSetterIndex> { + public: + static void Invoke(v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) { + SetterCallbackWrapper cbwrapper(info, value); + cbwrapper.InvokeCallback(); + } + + SetterCallbackWrapper(const v8::PropertyCallbackInfo& cbinfo, + const v8::Local& value) + : CallbackWrapperBase(cbinfo, 1), _value(value) {} + + /*virtual*/ + void Args(napi_value* buffer, size_t buffer_length) override { + if (buffer_length > 0) { + buffer[0] = v8impl::JsValueFromV8LocalValue(_value); + + if (buffer_length > 1) { + napi_value undefined = v8impl::JsValueFromV8LocalValue( + v8::Undefined(_cbinfo.GetIsolate())); + for (size_t i = 1; i < buffer_length; i += 1) { + buffer[i] = undefined; + } + } + } + } + + /*virtual*/ + void SetReturnValue(napi_value value) override { + node::FatalError("napi_set_return_value", + "Cannot return a value from a setter callback."); + } + + private: + const v8::Local& _value; +}; + +// Creates an object to be made available to the static function callback +// wrapper, used to retrieve the native callback function and data pointer. +v8::Local CreateFunctionCallbackData(napi_env env, + napi_callback cb, + void* data) { + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local otpl = v8::ObjectTemplate::New(isolate); + otpl->SetInternalFieldCount(v8impl::kFunctionFieldCount); + v8::Local cbdata = otpl->NewInstance(context).ToLocalChecked(); + + cbdata->SetInternalField( + v8impl::kEnvIndex, + v8::External::New(isolate, static_cast(env))); + cbdata->SetInternalField( + v8impl::kFunctionIndex, + v8::External::New(isolate, reinterpret_cast(cb))); + cbdata->SetInternalField( + v8impl::kDataIndex, + v8::External::New(isolate, data)); + return cbdata; +} + +// Creates an object to be made available to the static getter/setter +// callback wrapper, used to retrieve the native getter/setter callback +// function and data pointer. +v8::Local CreateAccessorCallbackData(napi_env env, + napi_callback getter, + napi_callback setter, + void* data) { + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local otpl = v8::ObjectTemplate::New(isolate); + otpl->SetInternalFieldCount(v8impl::kAccessorFieldCount); + v8::Local cbdata = otpl->NewInstance(context).ToLocalChecked(); + + cbdata->SetInternalField( + v8impl::kEnvIndex, + v8::External::New(isolate, static_cast(env))); + + if (getter != nullptr) { + cbdata->SetInternalField( + v8impl::kGetterIndex, + v8::External::New(isolate, reinterpret_cast(getter))); + } + + if (setter != nullptr) { + cbdata->SetInternalField( + v8impl::kSetterIndex, + v8::External::New(isolate, reinterpret_cast(setter))); + } + + cbdata->SetInternalField( + v8impl::kDataIndex, + v8::External::New(isolate, data)); + return cbdata; +} + +} // end of namespace v8impl + +// Intercepts the Node-V8 module registration callback. Converts parameters +// to NAPI equivalents and then calls the registration callback specified +// by the NAPI module. +void napi_module_register_cb(v8::Local exports, + v8::Local module, + v8::Local context, + void* priv) { + napi_module* mod = static_cast(priv); + + // Create a new napi_env for this module. Once module unloading is supported + // we shall have to call delete on this object from there. + napi_env env = new napi_env__(context->GetIsolate()); + + mod->nm_register_func( + env, + v8impl::JsValueFromV8LocalValue(exports), + v8impl::JsValueFromV8LocalValue(module), + mod->nm_priv); +} + +} // end of anonymous namespace + +#ifndef EXTERNAL_NAPI +namespace node { + // Indicates whether NAPI was enabled/disabled via the node command-line. + extern bool load_napi_modules; +} +#endif // EXTERNAL_NAPI + +// Registers a NAPI module. +void napi_module_register(napi_module* mod) { + // NAPI modules always work with the current node version. + int module_version = NODE_MODULE_VERSION; + +#ifndef EXTERNAL_NAPI + if (!node::load_napi_modules) { + // NAPI is disabled, so set the module version to -1 to cause the module + // to be unloaded. + module_version = -1; + } +#endif // EXTERNAL_NAPI + + node::node_module* nm = new node::node_module { + module_version, + mod->nm_flags, + nullptr, + mod->nm_filename, + nullptr, + napi_module_register_cb, + mod->nm_modname, + mod, // priv + nullptr, + }; + node::node_module_register(nm); +} + +// Warning: Keep in-sync with napi_status enum +const char* error_messages[] = {nullptr, + "Invalid pointer passed as argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled"}; + +void napi_clear_last_error(napi_env env) { + env->last_error.error_code = napi_ok; + + // TODO(boingoing): Should this be a callback? + env->last_error.engine_error_code = 0; + env->last_error.engine_reserved = nullptr; +} + +napi_status napi_set_last_error(napi_env env, napi_status error_code, + uint32_t engine_error_code, + void* engine_reserved) { + env->last_error.error_code = error_code; + env->last_error.engine_error_code = engine_error_code; + env->last_error.engine_reserved = engine_reserved; + + return error_code; +} + +napi_status napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result) { + CHECK_ENV(env); + + static_assert(node::arraysize(error_messages) == napi_status_last, + "Count of error messages must match count of error values"); + assert(env->last_error.error_code < napi_status_last); + + // Wait until someone requests the last error information to fetch the error + // message string + env->last_error.error_message = + error_messages[env->last_error.error_code]; + + *result = &(env->last_error); + return napi_ok; +} + +napi_status napi_create_function(napi_env env, + const char* utf8name, + napi_callback cb, + void* callback_data, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local return_value; + v8::EscapableHandleScope scope(isolate); + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, cb, callback_data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local tpl = v8::FunctionTemplate::New( + isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); + + v8::Local context = isolate->GetCurrentContext(); + v8::MaybeLocal maybe_function = tpl->GetFunction(context); + CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); + + return_value = scope.Escape(maybe_function.ToLocalChecked()); + + if (utf8name != nullptr) { + v8::Local name_string; + CHECK_NEW_FROM_UTF8(env, name_string, utf8name); + return_value->SetName(name_string); + } + + *result = v8impl::JsValueFromV8LocalValue(return_value); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_define_class(napi_env env, + const char* utf8name, + napi_callback constructor, + void* callback_data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + v8::EscapableHandleScope scope(isolate); + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, constructor, callback_data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local tpl = v8::FunctionTemplate::New( + isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); + + // we need an internal field to stash the wrapped object + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + v8::Local name_string; + CHECK_NEW_FROM_UTF8(env, name_string, utf8name); + tpl->SetClassName(name_string); + + size_t static_property_count = 0; + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p = properties + i; + + if ((p->attributes & napi_static) != 0) { + // Static properties are handled separately below. + static_property_count++; + continue; + } + + v8::Local property_name; + napi_status status = + v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); + + if (status != napi_ok) { + return napi_set_last_error(env, status); + } + + v8::PropertyAttribute attributes = + v8impl::V8PropertyAttributesFromDescriptor(p); + + // This code is similar to that in napi_define_properties(); the + // difference is it applies to a template instead of an object. + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local cbdata = v8impl::CreateAccessorCallbackData( + env, p->getter, p->setter, p->data); + + tpl->PrototypeTemplate()->SetAccessor( + property_name, + p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr, + p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, + cbdata, + v8::AccessControl::DEFAULT, + attributes); + } else if (p->method != nullptr) { + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, p->method, p->data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local t = + v8::FunctionTemplate::New(isolate, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata, + v8::Signature::New(isolate, tpl)); + + tpl->PrototypeTemplate()->Set(property_name, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(property_name, value, attributes); + } + } + + *result = v8impl::JsValueFromV8LocalValue(scope.Escape(tpl->GetFunction())); + + if (static_property_count > 0) { + std::vector static_descriptors; + static_descriptors.reserve(static_property_count); + + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p = properties + i; + if ((p->attributes & napi_static) != 0) { + static_descriptors.push_back(*p); + } + } + + napi_status status = + napi_define_properties(env, + *result, + static_descriptors.size(), + static_descriptors.data()); + if (status != napi_ok) return status; + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_property_names(napi_env env, + napi_value object, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + auto maybe_propertynames = obj->GetPropertyNames(context); + + CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue( + maybe_propertynames.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_set_property(napi_env env, + napi_value object, + napi_value key, + napi_value value) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe set_maybe = obj->Set(context, k, val); + + RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Maybe has_maybe = obj->Has(context, k); + + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + + *result = has_maybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_property(napi_env env, + napi_value object, + napi_value key, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto get_maybe = obj->Get(context, k); + + CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + + v8::Local val = get_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +napi_status napi_set_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value value) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe set_maybe = obj->Set(context, key, val); + + RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_named_property(napi_env env, + napi_value object, + const char* utf8name, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Maybe has_maybe = obj->Has(context, key); + + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + + *result = has_maybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto get_maybe = obj->Get(context, key); + + CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + + v8::Local val = get_maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +napi_status napi_set_element(napi_env env, + napi_value object, + uint32_t index, + napi_value value) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + auto set_maybe = obj->Set(context, index, val); + + RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), napi_generic_failure); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_has_element(napi_env env, + napi_value object, + uint32_t index, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Maybe has_maybe = obj->Has(context, index); + + CHECK_MAYBE_NOTHING(env, has_maybe, napi_generic_failure); + + *result = has_maybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_element(napi_env env, + napi_value object, + uint32_t index, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto get_maybe = obj->Get(context, index); + + CHECK_MAYBE_EMPTY(env, get_maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_define_properties(napi_env env, + napi_value object, + size_t property_count, + const napi_property_descriptor* properties) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj = + v8impl::V8LocalValueFromJsValue(object).As(); + + for (size_t i = 0; i < property_count; i++) { + const napi_property_descriptor* p = &properties[i]; + + v8::Local property_name; + napi_status status = + v8impl::V8NameFromPropertyDescriptor(env, p, &property_name); + + if (status != napi_ok) { + return napi_set_last_error(env, status); + } + + v8::PropertyAttribute attributes = + v8impl::V8PropertyAttributesFromDescriptor(p); + + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local cbdata = v8impl::CreateAccessorCallbackData( + env, + p->getter, + p->setter, + p->data); + + auto set_maybe = obj->SetAccessor( + context, + property_name, + p->getter ? v8impl::GetterCallbackWrapper::Invoke : nullptr, + p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, + cbdata, + v8::AccessControl::DEFAULT, + attributes); + + if (!set_maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_invalid_arg); + } + } else if (p->method != nullptr) { + v8::Local cbdata = + v8impl::CreateFunctionCallbackData(env, p->method, p->data); + + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + + v8::Local t = v8::FunctionTemplate::New( + isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); + + auto define_maybe = obj->DefineOwnProperty( + context, property_name, t->GetFunction(), attributes); + + if (!define_maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_generic_failure); + } + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + + auto define_maybe = + obj->DefineOwnProperty(context, property_name, value, attributes); + + if (!define_maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_invalid_arg); + } + } + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_is_array(napi_env env, napi_value value, bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *result = val->IsArray(); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_array_length(napi_env env, + napi_value value, + uint32_t* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsArray(), napi_array_expected); + + v8::Local arr = val.As(); + *result = arr->Length(); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_strict_equals(napi_env env, + napi_value lhs, + napi_value rhs, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); + v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); + + *result = a->StrictEquals(b); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_prototype(napi_env env, + napi_value object, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = obj->GetPrototype(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_object(napi_env env, napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Object::New(env->isolate)); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_array(napi_env env, napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Array::New(env->isolate)); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_array_with_length(napi_env env, + size_t length, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Array::New(env->isolate, length)); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + auto isolate = env->isolate; + auto str_maybe = + v8::String::NewFromOneByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + length); + CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_string_utf8(napi_env env, + const char* str, + size_t length, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local s; + CHECK_NEW_FROM_UTF8_LEN(env, s, str, length); + + *result = v8impl::JsValueFromV8LocalValue(s); + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_string_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + auto isolate = env->isolate; + auto str_maybe = + v8::String::NewFromTwoByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + length); + CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_number(napi_env env, + double value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Number::New(env->isolate, value)); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (value) { + *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate)); + } else { + *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate)); + } + + return napi_ok; +} + +napi_status napi_create_symbol(napi_env env, + napi_value description, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (description == nullptr) { + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); + } else { + v8::Local desc = v8impl::V8LocalValueFromJsValue(description); + RETURN_STATUS_IF_FALSE(env, desc->IsString(), napi_string_expected); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Symbol::New(isolate, desc.As())); + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_error(napi_env env, + napi_value msg, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); + + *result = v8impl::JsValueFromV8LocalValue(v8::Exception::Error( + message_value.As())); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_type_error(napi_env env, + napi_value msg, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); + + *result = v8impl::JsValueFromV8LocalValue(v8::Exception::TypeError( + message_value.As())); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_range_error(napi_env env, + napi_value msg, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local message_value = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, message_value->IsString(), napi_string_expected); + + *result = v8impl::JsValueFromV8LocalValue(v8::Exception::RangeError( + message_value.As())); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_typeof(napi_env env, + napi_value value, + napi_valuetype* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + + if (v->IsNumber()) { + *result = napi_number; + } else if (v->IsString()) { + *result = napi_string; + } else if (v->IsFunction()) { + // This test has to come before IsObject because IsFunction + // implies IsObject + *result = napi_function; + } else if (v->IsObject()) { + *result = napi_object; + } else if (v->IsBoolean()) { + *result = napi_boolean; + } else if (v->IsUndefined()) { + *result = napi_undefined; + } else if (v->IsSymbol()) { + *result = napi_symbol; + } else if (v->IsNull()) { + *result = napi_null; + } else if (v->IsExternal()) { + *result = napi_external; + } else { + // Should not get here unless V8 has added some new kind of value. + return napi_set_last_error(env, napi_invalid_arg); + } + + return napi_ok; +} + +napi_status napi_get_undefined(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Undefined(env->isolate)); + + return napi_ok; +} + +napi_status napi_get_null(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::Null(env->isolate)); + + return napi_ok; +} + +// Gets all callback info in a single call. (Ugly, but faster.) +napi_status napi_get_cb_info( + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + void** data) { // [out] Receives the data pointer for the callback. + CHECK_ENV(env); + + v8impl::CallbackWrapper* info = + reinterpret_cast(cbinfo); + + if (argv != nullptr) { + CHECK_ARG(env, argc); + info->Args(argv, std::min(*argc, info->ArgsLength())); + } + if (argc != nullptr) { + *argc = info->ArgsLength(); + } + if (this_arg != nullptr) { + *this_arg = info->This(); + } + if (data != nullptr) { + *data = info->Data(); + } + + return napi_ok; +} + +napi_status napi_is_construct_call(napi_env env, + napi_callback_info cbinfo, + bool* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called. + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8impl::CallbackWrapper* info = + reinterpret_cast(cbinfo); + + *result = info->IsConstructCall(); + return napi_ok; +} + +napi_status napi_call_function(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv); + + v8::Local v8value = v8impl::V8LocalValueFromJsValue(func); + RETURN_STATUS_IF_FALSE(env, v8value->IsFunction(), napi_invalid_arg); + + v8::Local v8func = v8value.As(); + auto maybe = v8func->Call(context, v8recv, argc, + reinterpret_cast*>(const_cast(argv))); + + if (try_catch.HasCaught()) { + return napi_set_last_error(env, napi_pending_exception); + } else { + if (result != nullptr) { + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + } + return napi_ok; + } +} + +napi_status napi_get_global(napi_env env, napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + // 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(); + *result = v8impl::JsValueFromV8LocalValue(context->Global()); + + return napi_ok; +} + +napi_status napi_throw(napi_env env, napi_value error) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + + isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error)); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return napi_ok; +} + +napi_status napi_throw_error(napi_env env, const char* msg) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, 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; +} + +napi_status napi_throw_type_error(napi_env env, const char* msg) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, 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_throw_range_error(napi_env env, const char* msg) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + isolate->ThrowException(v8::Exception::RangeError(str)); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return napi_ok; +} + +napi_status napi_is_error(napi_env env, napi_value value, bool* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot + // throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsNativeError(); + + return napi_ok; +} + +napi_status napi_get_value_double(napi_env env, + napi_value value, + double* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + *result = val.As()->Value(); + + return napi_ok; +} + +napi_status napi_get_value_int32(napi_env env, + napi_value value, + int32_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + *result = val->Int32Value(context).ToChecked(); + + return napi_ok; +} + +napi_status napi_get_value_uint32(napi_env env, + napi_value value, + uint32_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + *result = val->Uint32Value(context).ToChecked(); + + return napi_ok; +} + +napi_status napi_get_value_int64(napi_env env, + napi_value value, + int64_t* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected); + + // v8::Value::IntegerValue() converts NaN to INT64_MIN, inconsistent with + // v8::Value::Int32Value() that converts NaN to 0. So special-case NaN here. + double doubleValue = val.As()->Value(); + if (std::isnan(doubleValue)) { + *result = 0; + } else { + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + *result = val->IntegerValue(context).ToChecked(); + } + + return napi_ok; +} + +napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), napi_boolean_expected); + + *result = val.As()->Value(); + + return napi_ok; +} + +// Copies a JavaScript string into a LATIN-1 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_latin1(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + NAPI_PREAMBLE(env); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); + + if (!buf) { + CHECK_ARG(env, result); + *result = val.As()->Length(); + } else { + int copied = val.As()->WriteOneByte( + reinterpret_cast(buf), 0, bufsize - 1, + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } + + return GET_RETURN_STATUS(env); +} + +// Copies a JavaScript string into a UTF-8 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf8(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + NAPI_PREAMBLE(env); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); + + if (!buf) { + CHECK_ARG(env, result); + *result = val.As()->Utf8Length(); + } else { + int copied = val.As()->WriteUtf8( + buf, bufsize - 1, nullptr, v8::String::REPLACE_INVALID_UTF8 | + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } + + return GET_RETURN_STATUS(env); +} + +// Copies a JavaScript string into a UTF-16 string buffer. The result is the +// number of 2-byte code units (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in 2-byte +// code units) via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf16(napi_env env, + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result) { + NAPI_PREAMBLE(env); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected); + + if (!buf) { + CHECK_ARG(env, result); + // V8 assumes UTF-16 length is the same as the number of characters. + *result = val.As()->Length(); + } else { + int copied = val.As()->Write( + reinterpret_cast(buf), 0, bufsize - 1, + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_object(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, value); + + *result = v8impl::JsValueFromV8LocalValue(obj); + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_bool(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local b; + + CHECK_TO_BOOL(env, context, b, value); + + *result = v8impl::JsValueFromV8LocalValue(b); + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_number(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local num; + + CHECK_TO_NUMBER(env, context, num, value); + + *result = v8impl::JsValueFromV8LocalValue(num); + return GET_RETURN_STATUS(env); +} + +napi_status napi_coerce_to_string(napi_env env, + napi_value value, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + v8::Local str; + + CHECK_TO_STRING(env, context, str, value); + + *result = v8impl::JsValueFromV8LocalValue(str); + return GET_RETURN_STATUS(env); +} + +napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + + v8::Isolate* isolate = env->isolate; + v8::Local obj = + v8impl::V8LocalValueFromJsValue(js_object).As(); + + // Only objects that were created from a NAPI constructor's prototype + // via napi_define_class() can be (un)wrapped. + RETURN_STATUS_IF_FALSE(env, obj->InternalFieldCount() > 0, napi_invalid_arg); + + obj->SetInternalField(0, v8::External::New(isolate, native_object)); + + if (result != nullptr) { + // The returned reference should be deleted via napi_delete_reference() + // ONLY in response to the finalize callback invocation. (If it is deleted + // before then, then the finalize callback will never be invoked.) + // Therefore a finalize callback is required when returning a reference. + CHECK_ARG(env, finalize_cb); + v8impl::Reference* reference = v8impl::Reference::New( + env, obj, 0, false, finalize_cb, native_object, finalize_hint); + *result = reinterpret_cast(reference); + } else if (finalize_cb != nullptr) { + // Create a self-deleting reference just for the finalize callback. + v8impl::Reference::New( + env, obj, 0, true, finalize_cb, native_object, finalize_hint); + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local obj = value.As(); + + // Only objects that were created from a NAPI constructor's prototype + // via napi_define_class() can be (un)wrapped. + RETURN_STATUS_IF_FALSE(env, obj->InternalFieldCount() > 0, napi_invalid_arg); + + v8::Local unwrappedValue = obj->GetInternalField(0); + RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg); + + *result = unwrappedValue.As()->Value(); + + return napi_ok; +} + +napi_status napi_create_external(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + v8::Local external_value = v8::External::New(isolate, data); + + // The Reference object will delete itself after invoking the finalizer + // callback. + v8impl::Reference::New(env, + external_value, + 0, + true, + finalize_cb, + data, + finalize_hint); + + *result = v8impl::JsValueFromV8LocalValue(external_value); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_value_external(napi_env env, + napi_value value, + void** result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); + + v8::Local external_value = val.As(); + *result = external_value->Value(); + + return GET_RETURN_STATUS(env); +} + +// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. +napi_status napi_create_reference(napi_env env, + napi_value value, + uint32_t initial_refcount, + napi_ref* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8impl::Reference* reference = v8impl::Reference::New( + env, v8impl::V8LocalValueFromJsValue(value), initial_refcount, false); + + *result = reinterpret_cast(reference); + return GET_RETURN_STATUS(env); +} + +// Deletes a reference. The referenced value is released, and may be GC'd unless +// there are other references to it. +napi_status napi_delete_reference(napi_env env, napi_ref ref) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + v8impl::Reference::Delete(reinterpret_cast(ref)); + + return napi_ok; +} + +// Increments the reference count, optionally returning the resulting count. +// After this call the reference will be a strong reference because its +// refcount is >0, and the referenced object is effectively "pinned". +// Calling this when the refcount is 0 and the object is unavailable +// results in an error. +napi_status napi_reference_ref(napi_env env, napi_ref ref, uint32_t* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + + v8impl::Reference* reference = reinterpret_cast(ref); + uint32_t count = reference->Ref(); + + if (result != nullptr) { + *result = count; + } + + return GET_RETURN_STATUS(env); +} + +// Decrements the reference count, optionally returning the resulting count. If +// the result is 0 the reference is now weak and the object may be GC'd at any +// time if there are no other references. Calling this when the refcount is +// already 0 results in an error. +napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + + v8impl::Reference* reference = reinterpret_cast(ref); + + if (reference->RefCount() == 0) { + return napi_set_last_error(env, napi_generic_failure); + } + + uint32_t count = reference->Unref(); + + if (result != nullptr) { + *result = count; + } + + return GET_RETURN_STATUS(env); +} + +// Attempts to get a referenced value. If the reference is weak, the value might +// no longer be available, in that case the call is still successful but the +// result is NULL. +napi_status napi_get_reference_value(napi_env env, + napi_ref ref, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + CHECK_ARG(env, result); + + v8impl::Reference* reference = reinterpret_cast(ref); + *result = v8impl::JsValueFromV8LocalValue(reference->Get()); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = v8impl::JsHandleScopeFromV8HandleScope( + new v8impl::HandleScopeWrapper(env->isolate)); + return GET_RETURN_STATUS(env); +} + +napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, scope); + + delete v8impl::V8HandleScopeFromJsHandleScope(scope); + return GET_RETURN_STATUS(env); +} + +napi_status napi_open_escapable_handle_scope( + napi_env env, + napi_escapable_handle_scope* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( + new v8impl::EscapableHandleScopeWrapper(env->isolate)); + return GET_RETURN_STATUS(env); +} + +napi_status napi_close_escapable_handle_scope( + napi_env env, + napi_escapable_handle_scope scope) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, scope); + + delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + return GET_RETURN_STATUS(env); +} + +napi_status napi_escape_handle(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, scope); + CHECK_ARG(env, result); + + v8impl::EscapableHandleScopeWrapper* s = + v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + *result = v8impl::JsValueFromV8LocalValue( + s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); + return GET_RETURN_STATUS(env); +} + +napi_status napi_new_instance(napi_env env, + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + v8::Local v8value = v8impl::V8LocalValueFromJsValue(constructor); + RETURN_STATUS_IF_FALSE(env, v8value->IsFunction(), napi_invalid_arg); + + v8::Local ctor = v8value.As(); + + auto maybe = ctor->NewInstance(context, argc, + reinterpret_cast*>(const_cast(argv))); + + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +napi_status napi_instanceof(napi_env env, + napi_value object, + napi_value constructor, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = false; + + v8::Local ctor; + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); + + CHECK_TO_OBJECT(env, context, ctor, constructor); + + if (!ctor->IsFunction()) { + napi_throw_type_error(env, "constructor must be a function"); + + return napi_set_last_error(env, napi_function_expected); + } + + if (env->has_instance_available) { + napi_value value, js_result, has_instance = nullptr; + napi_status status = napi_generic_failure; + napi_valuetype value_type; + + // Get "Symbol" from the global object + if (env->has_instance.IsEmpty()) { + status = napi_get_global(env, &value); + if (status != napi_ok) return status; + status = napi_get_named_property(env, value, "Symbol", &value); + if (status != napi_ok) return status; + status = napi_typeof(env, value, &value_type); + if (status != napi_ok) return status; + + // Get "hasInstance" from Symbol + if (value_type == napi_function) { + status = napi_get_named_property(env, value, "hasInstance", &value); + if (status != napi_ok) return status; + status = napi_typeof(env, value, &value_type); + if (status != napi_ok) return status; + + // Store Symbol.hasInstance in a global persistent reference + if (value_type == napi_symbol) { + env->has_instance.Reset(env->isolate, + v8impl::V8LocalValueFromJsValue(value)); + has_instance = value; + } + } + } else { + has_instance = v8impl::JsValueFromV8LocalValue( + v8::Local::New(env->isolate, env->has_instance)); + } + + if (has_instance) { + status = napi_get_property(env, constructor, has_instance, &value); + if (status != napi_ok) return status; + status = napi_typeof(env, value, &value_type); + if (status != napi_ok) return status; + + // Call the function to determine whether the object is an instance of the + // constructor + if (value_type == napi_function) { + status = napi_call_function(env, constructor, value, 1, &object, + &js_result); + if (status != napi_ok) return status; + return napi_get_value_bool(env, js_result, result); + } + } + + env->has_instance_available = false; + } + + // If running constructor[Symbol.hasInstance](object) did not work, we perform + // a traditional instanceof (early Node.js 6.x). + + v8::Local prototype_string; + CHECK_NEW_FROM_UTF8(env, prototype_string, "prototype"); + + auto maybe_prototype = ctor->Get(context, prototype_string); + CHECK_MAYBE_EMPTY(env, maybe_prototype, napi_generic_failure); + + v8::Local prototype_property = maybe_prototype.ToLocalChecked(); + if (!prototype_property->IsObject()) { + napi_throw_type_error(env, "constructor.prototype must be an object"); + + return napi_set_last_error(env, napi_object_expected); + } + + auto maybe_ctor = prototype_property->ToObject(context); + CHECK_MAYBE_EMPTY(env, maybe_ctor, napi_generic_failure); + ctor = maybe_ctor.ToLocalChecked(); + + v8::Local current_obj = v8impl::V8LocalValueFromJsValue(object); + if (!current_obj->StrictEquals(ctor)) { + for (v8::Local original_obj = current_obj; + !(current_obj->IsNull() || current_obj->IsUndefined());) { + if (current_obj->StrictEquals(ctor)) { + *result = !(original_obj->IsNumber() || + original_obj->IsBoolean() || + original_obj->IsString()); + break; + } + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, v8impl::JsValueFromV8LocalValue( + current_obj)); + current_obj = obj->GetPrototype(); + } + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_make_callback(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result) { + NAPI_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local v8recv = + v8impl::V8LocalValueFromJsValue(recv).As(); + v8::Local v8func = + v8impl::V8LocalValueFromJsValue(func).As(); + + v8::Local callback_result = node::MakeCallback( + isolate, v8recv, v8func, argc, + reinterpret_cast*>(const_cast(argv))); + + if (result != nullptr) { + *result = v8impl::JsValueFromV8LocalValue(callback_result); + } + + return GET_RETURN_STATUS(env); +} + +// Methods to support catching exceptions +napi_status napi_is_exception_pending(napi_env env, bool* result) { + // NAPI_PREAMBLE is not used here: this function must execute when there is a + // pending exception. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = !env->last_exception.IsEmpty(); + return napi_ok; +} + +napi_status napi_get_and_clear_last_exception(napi_env env, + napi_value* result) { + // NAPI_PREAMBLE is not used here: this function must execute when there is a + // pending exception. + CHECK_ENV(env); + CHECK_ARG(env, result); + + if (env->last_exception.IsEmpty()) { + return napi_get_undefined(env, result); + } else { + *result = v8impl::JsValueFromV8LocalValue( + v8::Local::New(env->isolate, env->last_exception)); + env->last_exception.Reset(); + } + + return napi_ok; +} + +napi_status napi_create_buffer(napi_env env, + size_t length, + void** data, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, data); + CHECK_ARG(env, result); + + auto maybe = node::Buffer::New(env->isolate, length); + + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + v8::Local buffer = maybe.ToLocalChecked(); + + *result = v8impl::JsValueFromV8LocalValue(buffer); + *data = node::Buffer::Data(buffer); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_external_buffer(napi_env env, + size_t length, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + // The finalizer object will delete itself after invoking the callback. + v8impl::Finalizer* finalizer = v8impl::Finalizer::New( + env, finalize_cb, nullptr, finalize_hint); + + auto maybe = node::Buffer::New(isolate, + static_cast(data), + length, + v8impl::Finalizer::FinalizeBufferCallback, + finalizer); + + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); + // Tell coverity that 'finalizer' should not be freed when we return + // as it will be deleted when the buffer to which it is associated + // is finalized. + // coverity[leaked_storage] +} + +napi_status napi_create_buffer_copy(napi_env env, + size_t length, + const void* data, + void** result_data, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + auto maybe = node::Buffer::Copy(env->isolate, + static_cast(data), length); + + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + v8::Local buffer = maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(buffer); + + if (result_data != nullptr) { + *result_data = node::Buffer::Data(buffer); + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value)); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_buffer_info(napi_env env, + napi_value value, + void** data, + size_t* length) { + NAPI_PREAMBLE(env); + + v8::Local buffer = + v8impl::V8LocalValueFromJsValue(value).As(); + + if (data != nullptr) { + *data = node::Buffer::Data(buffer); + } + if (length != nullptr) { + *length = node::Buffer::Length(buffer); + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsArrayBuffer(); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_arraybuffer(napi_env env, + size_t byte_length, + void** data, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local buffer = + v8::ArrayBuffer::New(isolate, byte_length); + + // Optionally return a pointer to the buffer's data, to avoid another call to + // retreive it. + if (data != nullptr) { + *data = buffer->GetContents().Data(); + } + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_external_arraybuffer(napi_env env, + void* external_data, + size_t byte_length, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local buffer = + v8::ArrayBuffer::New(isolate, external_data, byte_length); + + if (finalize_cb != nullptr) { + // Create a self-deleting weak reference that invokes the finalizer + // callback. + v8impl::Reference::New(env, + buffer, + 0, + true, + finalize_cb, + external_data, + finalize_hint); + } + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_arraybuffer_info(napi_env env, + napi_value arraybuffer, + void** data, + size_t* byte_length) { + NAPI_PREAMBLE(env); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); + + v8::ArrayBuffer::Contents contents = + value.As()->GetContents(); + + if (data != nullptr) { + *data = contents.Data(); + } + + if (byte_length != nullptr) { + *byte_length = contents.ByteLength(); + } + + return GET_RETURN_STATUS(env); +} + +napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsTypedArray(); + + return GET_RETURN_STATUS(env); +} + +napi_status napi_create_typedarray(napi_env env, + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); + + v8::Local buffer = value.As(); + v8::Local typedArray; + + switch (type) { + case napi_int8_array: + typedArray = v8::Int8Array::New(buffer, byte_offset, length); + break; + case napi_uint8_array: + typedArray = v8::Uint8Array::New(buffer, byte_offset, length); + break; + case napi_uint8_clamped_array: + typedArray = v8::Uint8ClampedArray::New(buffer, byte_offset, length); + break; + case napi_int16_array: + typedArray = v8::Int16Array::New(buffer, byte_offset, length); + break; + case napi_uint16_array: + typedArray = v8::Uint16Array::New(buffer, byte_offset, length); + break; + case napi_int32_array: + typedArray = v8::Int32Array::New(buffer, byte_offset, length); + break; + case napi_uint32_array: + typedArray = v8::Uint32Array::New(buffer, byte_offset, length); + break; + case napi_float32_array: + typedArray = v8::Float32Array::New(buffer, byte_offset, length); + break; + case napi_float64_array: + typedArray = v8::Float64Array::New(buffer, byte_offset, length); + break; + default: + return napi_set_last_error(env, napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue(typedArray); + return GET_RETURN_STATUS(env); +} + +napi_status napi_get_typedarray_info(napi_env env, + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + NAPI_PREAMBLE(env); + + v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); + RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), napi_invalid_arg); + + v8::Local array = value.As(); + + if (type != nullptr) { + if (value->IsInt8Array()) { + *type = napi_int8_array; + } else if (value->IsUint8Array()) { + *type = napi_uint8_array; + } else if (value->IsUint8ClampedArray()) { + *type = napi_uint8_clamped_array; + } else if (value->IsInt16Array()) { + *type = napi_int16_array; + } else if (value->IsUint16Array()) { + *type = napi_uint16_array; + } else if (value->IsInt32Array()) { + *type = napi_int32_array; + } else if (value->IsUint32Array()) { + *type = napi_uint32_array; + } else if (value->IsFloat32Array()) { + *type = napi_float32_array; + } else if (value->IsFloat64Array()) { + *type = napi_float64_array; + } + } + + if (length != nullptr) { + *length = array->Length(); + } + + v8::Local buffer = array->Buffer(); + if (data != nullptr) { + *data = static_cast(buffer->GetContents().Data()) + + array->ByteOffset(); + } + + if (arraybuffer != nullptr) { + *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); + } + + if (byte_offset != nullptr) { + *byte_offset = array->ByteOffset(); + } + + return GET_RETURN_STATUS(env); +} + +namespace uvimpl { + +napi_status ConvertUVErrorCode(int code) { + switch (code) { + case 0: + return napi_ok; + case UV_EINVAL: + return napi_invalid_arg; + case UV_ECANCELED: + return napi_cancelled; + } + + return napi_generic_failure; +} + +// Wrapper around uv_work_t which calls user-provided callbacks. +class Work { + private: + explicit Work(napi_env env, + napi_async_execute_callback execute = nullptr, + napi_async_complete_callback complete = nullptr, + void* data = nullptr) + : _env(env), + _data(data), + _execute(execute), + _complete(complete) { + memset(&_request, 0, sizeof(_request)); + _request.data = this; + } + + ~Work() { } + + public: + static Work* New(napi_env env, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data) { + return new Work(env, execute, complete, data); + } + + static void Delete(Work* work) { + delete work; + } + + static void ExecuteCallback(uv_work_t* req) { + Work* work = static_cast(req->data); + work->_execute(work->_env, work->_data); + } + + static void CompleteCallback(uv_work_t* req, int status) { + Work* work = static_cast(req->data); + + if (work->_complete != nullptr) { + work->_complete(work->_env, ConvertUVErrorCode(status), work->_data); + } + } + + uv_work_t* Request() { + return &_request; + } + + private: + napi_env _env; + void* _data; + uv_work_t _request; + napi_async_execute_callback _execute; + napi_async_complete_callback _complete; +}; + +} // end of namespace uvimpl + +#define CALL_UV(env, condition) \ + do { \ + int result = (condition); \ + napi_status status = uvimpl::ConvertUVErrorCode(result); \ + if (status != napi_ok) { \ + return napi_set_last_error(env, status, result); \ + } \ + } while (0) + +napi_status napi_create_async_work(napi_env env, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result) { + CHECK_ENV(env); + CHECK_ARG(env, execute); + CHECK_ARG(env, result); + + uvimpl::Work* work = uvimpl::Work::New(env, execute, complete, data); + + *result = reinterpret_cast(work); + + return napi_ok; +} + +napi_status napi_delete_async_work(napi_env env, napi_async_work work) { + CHECK_ENV(env); + CHECK_ARG(env, work); + + uvimpl::Work::Delete(reinterpret_cast(work)); + + return napi_ok; +} + +napi_status napi_queue_async_work(napi_env env, napi_async_work work) { + CHECK_ENV(env); + CHECK_ARG(env, work); + + // Consider: Encapsulate the uv_loop_t into an opaque pointer parameter + uv_loop_t* event_loop = + node::Environment::GetCurrent(env->isolate)->event_loop(); + + uvimpl::Work* w = reinterpret_cast(work); + + CALL_UV(env, uv_queue_work(event_loop, + w->Request(), + uvimpl::Work::ExecuteCallback, + uvimpl::Work::CompleteCallback)); + + return napi_ok; +} + +napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { + CHECK_ENV(env); + CHECK_ARG(env, work); + + uvimpl::Work* w = reinterpret_cast(work); + + CALL_UV(env, uv_cancel(reinterpret_cast(w->Request()))); + + return napi_ok; +} diff --git a/src/node_api.h b/src/node_api.h new file mode 100644 index 0000000000..29c5c513f7 --- /dev/null +++ b/src/node_api.h @@ -0,0 +1,483 @@ +/****************************************************************************** + * Experimental prototype for demonstrating VM agnostic and ABI stable API + * for native modules to use instead of using Nan and V8 APIs directly. + * + * The current status is "Experimental" and should not be used for + * production applications. The API is still subject to change + * and as an experimental feature is NOT subject to semver. + * + ******************************************************************************/ +#ifndef SRC_NODE_API_H_ +#define SRC_NODE_API_H_ + +#include +#include +#include "node_api_types.h" + +#ifdef _WIN32 + #ifdef BUILDING_NODE_EXTENSION + #ifdef EXTERNAL_NAPI + // Building external N-API, or native module against external N-API + #define NAPI_EXTERN /* nothing */ + #else + // Building native module against node with built-in N-API + #define NAPI_EXTERN __declspec(dllimport) + #endif + #else + // Building node with built-in N-API + #define NAPI_EXTERN __declspec(dllexport) + #endif +#else + #define NAPI_EXTERN /* nothing */ +#endif + +#ifdef _WIN32 +# define NAPI_MODULE_EXPORT __declspec(dllexport) +#else +# define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) +#endif + + +typedef void (*napi_addon_register_func)(napi_env env, + napi_value exports, + napi_value module, + void* priv); + +typedef struct { + int nm_version; + unsigned int nm_flags; + const char* nm_filename; + napi_addon_register_func nm_register_func; + const char* nm_modname; + void* nm_priv; + void* reserved[4]; +} napi_module; + +#define NAPI_MODULE_VERSION 1 + +#if defined(_MSC_VER) +#pragma section(".CRT$XCU", read) +#define NAPI_C_CTOR(fn) \ + static void __cdecl fn(void); \ + __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \ + fn; \ + static void __cdecl fn(void) +#else +#define NAPI_C_CTOR(fn) \ + static void fn(void) __attribute__((constructor)); \ + static void fn(void) +#endif + +#ifdef __cplusplus +#define EXTERN_C_START extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_START +#define EXTERN_C_END +#endif + +#define NAPI_MODULE_X(modname, regfunc, priv, flags) \ + EXTERN_C_START \ + static napi_module _module = \ + { \ + NAPI_MODULE_VERSION, \ + flags, \ + __FILE__, \ + regfunc, \ + #modname, \ + priv, \ + {0}, \ + }; \ + NAPI_C_CTOR(_register_ ## modname) { \ + napi_module_register(&_module); \ + } \ + EXTERN_C_END + +#define NAPI_MODULE(modname, regfunc) \ + NAPI_MODULE_X(modname, regfunc, NULL, 0) + +EXTERN_C_START + +NAPI_EXTERN void napi_module_register(napi_module* mod); + +NAPI_EXTERN napi_status +napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result); + +// Getters for defined singletons +NAPI_EXTERN napi_status napi_get_undefined(napi_env env, napi_value* result); +NAPI_EXTERN napi_status napi_get_null(napi_env env, napi_value* result); +NAPI_EXTERN napi_status napi_get_global(napi_env env, napi_value* result); +NAPI_EXTERN napi_status napi_get_boolean(napi_env env, + bool value, + napi_value* result); + +// Methods to create Primitive types/Objects +NAPI_EXTERN napi_status napi_create_object(napi_env env, napi_value* result); +NAPI_EXTERN napi_status napi_create_array(napi_env env, napi_value* result); +NAPI_EXTERN napi_status napi_create_array_with_length(napi_env env, + size_t length, + napi_value* result); +NAPI_EXTERN napi_status napi_create_number(napi_env env, + double value, + napi_value* result); +NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result); +NAPI_EXTERN napi_status napi_create_string_utf8(napi_env env, + const char* str, + size_t length, + napi_value* result); +NAPI_EXTERN napi_status napi_create_string_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result); +NAPI_EXTERN napi_status napi_create_symbol(napi_env env, + napi_value description, + napi_value* result); +NAPI_EXTERN napi_status napi_create_function(napi_env env, + const char* utf8name, + napi_callback cb, + void* data, + napi_value* result); +NAPI_EXTERN napi_status napi_create_error(napi_env env, + napi_value msg, + napi_value* result); +NAPI_EXTERN napi_status napi_create_type_error(napi_env env, + napi_value msg, + napi_value* result); +NAPI_EXTERN napi_status napi_create_range_error(napi_env env, + napi_value msg, + napi_value* result); + +// Methods to get the the native napi_value from Primitive type +NAPI_EXTERN napi_status napi_typeof(napi_env env, + napi_value value, + napi_valuetype* result); +NAPI_EXTERN napi_status napi_get_value_double(napi_env env, + napi_value value, + double* result); +NAPI_EXTERN napi_status napi_get_value_int32(napi_env env, + napi_value value, + int32_t* result); +NAPI_EXTERN napi_status napi_get_value_uint32(napi_env env, + napi_value value, + uint32_t* result); +NAPI_EXTERN napi_status napi_get_value_int64(napi_env env, + napi_value value, + int64_t* result); +NAPI_EXTERN napi_status napi_get_value_bool(napi_env env, + napi_value value, + bool* result); + +// Copies LATIN-1 encoded bytes from a string into a buffer. +NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result); + +// Copies UTF-8 encoded bytes from a string into a buffer. +NAPI_EXTERN napi_status napi_get_value_string_utf8(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result); + +// Copies UTF-16 encoded bytes from a string into a buffer. +NAPI_EXTERN napi_status napi_get_value_string_utf16(napi_env env, + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result); + +// Methods to coerce values +// These APIs may execute user scripts +NAPI_EXTERN napi_status napi_coerce_to_bool(napi_env env, + napi_value value, + napi_value* result); +NAPI_EXTERN napi_status napi_coerce_to_number(napi_env env, + napi_value value, + napi_value* result); +NAPI_EXTERN napi_status napi_coerce_to_object(napi_env env, + napi_value value, + napi_value* result); +NAPI_EXTERN napi_status napi_coerce_to_string(napi_env env, + napi_value value, + napi_value* result); + +// Methods to work with Objects +NAPI_EXTERN napi_status napi_get_prototype(napi_env env, + napi_value object, + napi_value* result); +NAPI_EXTERN napi_status napi_get_property_names(napi_env env, + napi_value object, + napi_value* result); +NAPI_EXTERN napi_status napi_set_property(napi_env env, + napi_value object, + napi_value key, + napi_value value); +NAPI_EXTERN napi_status napi_has_property(napi_env env, + napi_value object, + napi_value key, + bool* result); +NAPI_EXTERN napi_status napi_get_property(napi_env env, + napi_value object, + napi_value key, + napi_value* result); +NAPI_EXTERN napi_status napi_set_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value value); +NAPI_EXTERN napi_status napi_has_named_property(napi_env env, + napi_value object, + const char* utf8name, + bool* result); +NAPI_EXTERN napi_status napi_get_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value* result); +NAPI_EXTERN napi_status napi_set_element(napi_env env, + napi_value object, + uint32_t index, + napi_value value); +NAPI_EXTERN napi_status napi_has_element(napi_env env, + napi_value object, + uint32_t index, + bool* result); +NAPI_EXTERN napi_status napi_get_element(napi_env env, + napi_value object, + uint32_t index, + napi_value* result); +NAPI_EXTERN napi_status +napi_define_properties(napi_env env, + napi_value object, + size_t property_count, + const napi_property_descriptor* properties); + +// Methods to work with Arrays +NAPI_EXTERN napi_status napi_is_array(napi_env env, + napi_value value, + bool* result); +NAPI_EXTERN napi_status napi_get_array_length(napi_env env, + napi_value value, + uint32_t* result); + +// Methods to compare values +NAPI_EXTERN napi_status napi_strict_equals(napi_env env, + napi_value lhs, + napi_value rhs, + bool* result); + +// Methods to work with Functions +NAPI_EXTERN napi_status napi_call_function(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); +NAPI_EXTERN napi_status napi_new_instance(napi_env env, + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result); +NAPI_EXTERN napi_status napi_instanceof(napi_env env, + napi_value object, + napi_value constructor, + bool* result); + +// Napi version of node::MakeCallback(...) +NAPI_EXTERN napi_status napi_make_callback(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result); + +// Methods to work with napi_callbacks + +// Gets all callback info in a single call. (Ugly, but faster.) +NAPI_EXTERN napi_status napi_get_cb_info( + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + void** data); // [out] Receives the data pointer for the callback. + +NAPI_EXTERN napi_status napi_is_construct_call(napi_env env, + napi_callback_info cbinfo, + bool* result); +NAPI_EXTERN napi_status +napi_define_class(napi_env env, + const char* utf8name, + napi_callback constructor, + void* data, + size_t property_count, + const napi_property_descriptor* properties, + napi_value* result); + +// Methods to work with external data objects +NAPI_EXTERN napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result); +NAPI_EXTERN napi_status napi_unwrap(napi_env env, + napi_value js_object, + void** result); +NAPI_EXTERN napi_status napi_create_external(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result); +NAPI_EXTERN napi_status napi_get_value_external(napi_env env, + napi_value value, + void** result); + +// Methods to control object lifespan + +// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. +NAPI_EXTERN napi_status napi_create_reference(napi_env env, + napi_value value, + uint32_t initial_refcount, + napi_ref* result); + +// Deletes a reference. The referenced value is released, and may +// be GC'd unless there are other references to it. +NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref); + +// Increments the reference count, optionally returning the resulting count. +// After this call the reference will be a strong reference because its +// refcount is >0, and the referenced object is effectively "pinned". +// Calling this when the refcount is 0 and the object is unavailable +// results in an error. +NAPI_EXTERN napi_status napi_reference_ref(napi_env env, + napi_ref ref, + uint32_t* result); + +// Decrements the reference count, optionally returning the resulting count. +// If the result is 0 the reference is now weak and the object may be GC'd +// at any time if there are no other references. Calling this when the +// refcount is already 0 results in an error. +NAPI_EXTERN napi_status napi_reference_unref(napi_env env, + napi_ref ref, + uint32_t* result); + +// Attempts to get a referenced value. If the reference is weak, +// the value might no longer be available, in that case the call +// is still successful but the result is NULL. +NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, + napi_ref ref, + napi_value* result); + +NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env, + napi_handle_scope* result); +NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env, + napi_handle_scope scope); +NAPI_EXTERN napi_status +napi_open_escapable_handle_scope(napi_env env, + napi_escapable_handle_scope* result); +NAPI_EXTERN napi_status +napi_close_escapable_handle_scope(napi_env env, + napi_escapable_handle_scope scope); + +NAPI_EXTERN napi_status napi_escape_handle(napi_env env, + napi_escapable_handle_scope scope, + napi_value escapee, + napi_value* result); + +// Methods to support error handling +NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); +NAPI_EXTERN napi_status napi_throw_error(napi_env env, const char* msg); +NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg); +NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg); +NAPI_EXTERN napi_status napi_is_error(napi_env env, + napi_value value, + bool* result); + +// Methods to support catching exceptions +NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result); +NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env, + napi_value* result); + +// Methods to provide node::Buffer functionality with napi types +NAPI_EXTERN napi_status napi_create_buffer(napi_env env, + size_t length, + void** data, + napi_value* result); +NAPI_EXTERN napi_status napi_create_external_buffer(napi_env env, + size_t length, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result); +NAPI_EXTERN napi_status napi_create_buffer_copy(napi_env env, + size_t length, + const void* data, + void** result_data, + napi_value* result); +NAPI_EXTERN napi_status napi_is_buffer(napi_env env, + napi_value value, + bool* result); +NAPI_EXTERN napi_status napi_get_buffer_info(napi_env env, + napi_value value, + void** data, + size_t* length); + +// Methods to work with array buffers and typed arrays +NAPI_EXTERN napi_status napi_is_arraybuffer(napi_env env, + napi_value value, + bool* result); +NAPI_EXTERN napi_status napi_create_arraybuffer(napi_env env, + size_t byte_length, + void** data, + napi_value* result); +NAPI_EXTERN napi_status +napi_create_external_arraybuffer(napi_env env, + void* external_data, + size_t byte_length, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result); +NAPI_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, + napi_value arraybuffer, + void** data, + size_t* byte_length); +NAPI_EXTERN napi_status napi_is_typedarray(napi_env env, + napi_value value, + bool* result); +NAPI_EXTERN napi_status napi_create_typedarray(napi_env env, + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result); +NAPI_EXTERN napi_status napi_get_typedarray_info(napi_env env, + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset); + +// Methods to manage simple async operations +NAPI_EXTERN +napi_status napi_create_async_work(napi_env env, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result); +NAPI_EXTERN napi_status napi_delete_async_work(napi_env env, + napi_async_work work); +NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, + napi_async_work work); +NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, + napi_async_work work); + +EXTERN_C_END + +#endif // SRC_NODE_API_H__ diff --git a/src/node_api_helpers.h b/src/node_api_helpers.h deleted file mode 100644 index f6d09c9ef8..0000000000 --- a/src/node_api_helpers.h +++ /dev/null @@ -1,466 +0,0 @@ -#ifndef SRC_NODE_API_HELPERS_H_ -#define SRC_NODE_API_HELPERS_H_ - -//////////////////////////////////////////////////////////////////////////////// -// Nan-like Helpers -//////////////////////////////////////////////////////////////////////////////// -// Must not have symbol exports to achieve clean C opaque API/ABI -// -// TODO(ianhall): This should all move into its own header file and perhaps be -// optional since it contains a lot of inline code and brings in a lot of header -// dependencies. -//////////////////////////////////////////////////////////////////////////////// -#include "node_jsvmapi.h" -#include "node_asyncapi.h" -#include -#include -#include - -#define NAPI_METHOD(name) \ - void name(napi_env env, napi_callback_info info) -#define NAPI_GETTER(name) NAPI_METHOD(name) -#define NAPI_SETTER(name) NAPI_METHOD(name) - -#define NAPI_MODULE_INIT(name) \ - void name(napi_env env, napi_value exports, napi_value module) - -// This is taken from NAN and is the C++11 version. -// TODO(ianhall): Support pre-C++11 compilation? -#define NAPI_DISALLOW_ASSIGN(CLASS) void operator=(const CLASS&) = delete; -#define NAPI_DISALLOW_COPY(CLASS) CLASS(const CLASS&) = delete; -#define NAPI_DISALLOW_MOVE(CLASS) \ - CLASS(CLASS&) = delete; \ - void operator=(CLASS&) = delete; - -#define NAPI_DISALLOW_ASSIGN_COPY_MOVE(CLASS) \ - NAPI_DISALLOW_ASSIGN(CLASS) \ - NAPI_DISALLOW_COPY(CLASS) \ - NAPI_DISALLOW_MOVE(CLASS) - -namespace Napi { - // RAII HandleScope helpers - // Mirror Nan versions for easy conversion - // Ensure scopes are closed in the correct order on the stack - class HandleScope { - public: - HandleScope() { - napi_get_current_env(&env); - napi_open_handle_scope(env, &scope); - } - explicit HandleScope(napi_env e) : env(e) { - napi_open_handle_scope(env, &scope); - } - ~HandleScope() { - napi_close_handle_scope(env, scope); - } - - private: - napi_env env; - napi_handle_scope scope; - }; - - class EscapableHandleScope { - public: - EscapableHandleScope() { - napi_get_current_env(&env); - napi_open_escapable_handle_scope(env, &scope); - } - explicit EscapableHandleScope(napi_env e) : env(e) { - napi_open_escapable_handle_scope(e, &scope); - } - ~EscapableHandleScope() { - napi_close_escapable_handle_scope(env, scope); - } - - napi_value Escape(napi_value escapee) { - napi_value result; - napi_escape_handle(env, scope, escapee, &result); - return result; - } - - private: - napi_env env; - napi_escapable_handle_scope scope; - }; - - class Utf8String { - public: - inline explicit Utf8String(napi_value from) : - length_(0), str_(str_st_) { - if (from != nullptr) { - napi_env env; - napi_get_current_env(&env); - napi_value string = nullptr; - napi_coerce_to_string(env, from, &string); - if (string != nullptr) { - napi_get_value_string_utf8_length(env, string, &length_); - int bufsize = length_ + 1; - if (static_cast(bufsize) > sizeof(str_st_)) { - str_ = new char[bufsize]; - assert(str_ != 0); - } - napi_get_value_string_utf8(env, string, str_, bufsize, nullptr); - } - } - } - - inline size_t length() const { - return length_; - } - - inline char* operator*() { return str_; } - inline const char* operator*() const { return str_; } - - inline ~Utf8String() { - if (str_ != str_st_) { - delete [] str_; - } - } - - private: - NAPI_DISALLOW_ASSIGN_COPY_MOVE(Utf8String) - - int length_; - char *str_; - char str_st_[1024]; - }; - - // TODO(ianhall): This class uses napi_get_current_env() extensively - // does that make sense? will this behave correctly when passed around - // workers? Is there a good reason to have napi_env? - // Perhaps the C++ layer shields the user from napi_env and behaves - // like Nan, always using the current one? - class Callback { - public: - Callback() { - napi_env env; - napi_get_current_env(&env); - HandleScope scope(env); - napi_value obj; - napi_create_object(env, &obj); - napi_create_reference(env, obj, 1, &handle); - } - - explicit Callback(napi_value fn) { - napi_env env; - napi_get_current_env(&env); - HandleScope scope(env); - napi_value obj; - napi_create_object(env, &obj); - napi_create_reference(env, obj, 1, &handle); - SetFunction(fn); - } - - ~Callback() { - if (handle == NULL) { - return; - } - - napi_env env; - napi_get_current_env(&env); - napi_delete_reference(env, handle); - } - - bool operator==(const Callback &other) const { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - - napi_value ha; - napi_get_reference_value(env, handle, &ha); - napi_value hb; - napi_get_reference_value(env, other.handle, &hb); - - napi_value a; - napi_get_element(env, ha, kCallbackIndex, &a); - napi_value b; - napi_get_element(env, hb, kCallbackIndex, &b); - - bool result; - napi_strict_equals(env, a, b, &result); - return result; - } - - bool operator!=(const Callback &other) const { - return !this->operator==(other); - } - - inline - napi_value operator*() const { return this->GetFunction(); } - - inline napi_value operator()( - napi_value target, - int argc = 0, - napi_value argv[] = 0) const { - return this->Call(target, argc, argv); - } - - inline napi_value operator()( - int argc = 0, - napi_value argv[] = 0) const { - return this->Call(argc, argv); - } - - inline void SetFunction(napi_value fn) { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, handle, &h); - napi_set_element(env, h, kCallbackIndex, fn); - } - - inline napi_value GetFunction() const { - EscapableHandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, handle, &h); - napi_value fn; - napi_get_element(env, h, kCallbackIndex, &fn); - return scope.Escape(fn); - } - - inline bool IsEmpty() const { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, handle, &h); - napi_value fn; - napi_get_element(env, h, kCallbackIndex, &fn); - napi_valuetype valuetype; - napi_get_type_of_value(env, fn, &valuetype); - return napi_undefined == valuetype; - } - - inline napi_value - Call(napi_value target, - int argc, - napi_value argv[]) const { - return Call_(target, argc, argv); - } - - inline napi_value - Call(int argc, napi_value argv[]) const { - napi_env env; - napi_get_current_env(&env); - napi_value global; - napi_get_global(env, &global); - return Call_(global, argc, argv); - } - - private: - NAPI_DISALLOW_ASSIGN_COPY_MOVE(Callback) - napi_ref handle; - static const uint32_t kCallbackIndex = 0; - - napi_value Call_(napi_value target, - int argc, - napi_value argv[]) const { - EscapableHandleScope scope; - napi_env env; - napi_get_current_env(&env); - - napi_value h; - napi_get_reference_value(env, handle, &h); - napi_value fn; - napi_get_element(env, h, kCallbackIndex, &fn); - - napi_value cb; - napi_make_callback( - env, - target, - fn, - argc, - argv, - &cb); - return scope.Escape(cb); - } - }; - - - // TODO(ianhall): This class uses napi_get_current_env() extensively - // See comment above on class Callback - /* abstract */ class AsyncWorker { - public: - explicit AsyncWorker(Callback *callback_) - : callback(callback_), errmsg_(NULL) { - request = napi_create_async_work(); - napi_env env; - napi_get_current_env(&env); - - HandleScope scope; - napi_value obj; - napi_create_object(env, &obj); - napi_create_reference(env, obj, 1, &persistentHandle); - } - - virtual ~AsyncWorker() { - HandleScope scope; - - if (persistentHandle != NULL) { - napi_env env; - napi_get_current_env(&env); - napi_delete_reference(env, persistentHandle); - persistentHandle = NULL; - } - delete callback; - delete[] errmsg_; - - napi_delete_async_work(request); - } - - virtual void WorkComplete() { - HandleScope scope; - - if (errmsg_ == NULL) - HandleOKCallback(); - else - HandleErrorCallback(); - delete callback; - callback = NULL; - } - - inline void SaveToPersistent( - const char *key, napi_value value) { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_propertyname pnKey; - napi_property_name(env, key, &pnKey); - napi_value h; - napi_get_reference_value(env, persistentHandle, &h); - napi_set_property(env, h, pnKey, value); - } - - inline void SaveToPersistent( - napi_propertyname key, napi_value value) { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, persistentHandle, &h); - napi_set_property(env, h, key, value); - } - - inline void SaveToPersistent( - uint32_t index, napi_value value) { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, persistentHandle, &h); - napi_set_element(env, h, index, value); - } - - inline napi_value GetFromPersistent(const char *key) const { - EscapableHandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_propertyname pnKey; - napi_property_name(env, key, &pnKey); - napi_value h; - napi_get_reference_value(env, persistentHandle, &h); - napi_value v; - napi_get_property(env, h, pnKey, &v); - return scope.Escape(v); - } - - inline napi_value - GetFromPersistent(napi_propertyname key) const { - EscapableHandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, persistentHandle, &h); - napi_value v; - napi_get_property(env, h, key, &v); - return scope.Escape(v); - } - - inline napi_value GetFromPersistent(uint32_t index) const { - EscapableHandleScope scope; - napi_env env; - napi_get_current_env(&env); - napi_value h; - napi_get_reference_value(env, persistentHandle, &h); - napi_value v; - napi_get_element(env, h, index, &v); - return scope.Escape(v); - } - - virtual void Execute() = 0; - - napi_work request; - - virtual void Destroy() { - delete this; - } - - static void CallExecute(void* this_pointer){ - AsyncWorker* self = static_cast(this_pointer); - self->Execute(); - } - - static void CallWorkComplete(void* this_pointer) { - AsyncWorker* self = static_cast(this_pointer); - self->WorkComplete(); - } - - static void CallDestroy(void* this_pointer) { - AsyncWorker* self = static_cast(this_pointer); - self->Destroy(); - } - - protected: - napi_ref persistentHandle; - Callback *callback; - - virtual void HandleOKCallback() { - callback->Call(0, NULL); - } - - virtual void HandleErrorCallback() { - HandleScope scope; - napi_env env; - napi_get_current_env(&env); - - napi_value s; - napi_create_string_utf8(env, ErrorMessage(), -1, &s); - - napi_value argv[1]; - napi_create_error(env, s, argv); - - callback->Call(1, argv); - } - - void SetErrorMessage(const char *msg) { - delete[] errmsg_; - - size_t size = strlen(msg) + 1; - errmsg_ = new char[size]; - memcpy(errmsg_, msg, size); - } - - const char* ErrorMessage() const { - return errmsg_; - } - - private: - NAPI_DISALLOW_ASSIGN_COPY_MOVE(AsyncWorker) - char *errmsg_; - }; - - inline void AsyncQueueWorker(AsyncWorker* worker) { - napi_work req = worker->request; - napi_async_set_data(req, static_cast(worker)); - napi_async_set_execute(req, &AsyncWorker::CallExecute); - napi_async_set_complete(req, &AsyncWorker::CallWorkComplete); - napi_async_set_destroy(req, &AsyncWorker::CallDestroy); - napi_async_queue_worker(req); - } -} // namespace Napi - - -#endif // SRC_NODE_API_HELPERS_H_ diff --git a/src/node_jsrtapi.cc b/src/node_api_jsrt.cc similarity index 54% rename from src/node_jsrtapi.cc rename to src/node_api_jsrt.cc index 8180edcf19..bd579a8898 100644 --- a/src/node_jsrtapi.cc +++ b/src/node_api_jsrt.cc @@ -1,4 +1,4 @@ -/******************************************************************************* +/******************************************************************************* * Experimental prototype for demonstrating VM agnostic and ABI stable API * for native modules to use instead of using Nan and V8 APIs directly. * @@ -18,7 +18,7 @@ ******************************************************************************/ #include -#include "node_jsvmapi_internal.h" +#include #include #include #include @@ -32,9 +32,44 @@ #define CALLBACK #endif -void napi_module_register(void* mod) { - node::node_module_register(mod); -} +#define RETURN_STATUS_IF_FALSE(condition, status) \ + do { \ + if (!(condition)) { \ + return napi_set_last_error((status)); \ + } \ + } while(0) + +#define CHECK_ARG(arg) \ + RETURN_STATUS_IF_FALSE((arg), napi_invalid_arg) + +#define CHECK_JSRT(expr) \ + do { \ + JsErrorCode err = (expr); \ + if (err != JsNoError) return napi_set_last_error(err); \ + } while(0) + +#define CHECK_JSRT_EXPECTED(expr, expected) \ + do { \ + JsErrorCode err = (expr); \ + if (err == JsErrorInvalidArgument) \ + return napi_set_last_error(expected); \ + if (err != JsNoError) return napi_set_last_error(err); \ + } while(0) + +// This does not call napi_set_last_error because the expression +// is assumed to be a NAPI function call that already did. +#define CHECK_NAPI(expr) \ + do { \ + napi_status status = (expr); \ + if (status != napi_ok) return status; \ + } while(0) + +static napi_status napi_set_last_error(napi_status error_code, + uint32_t engine_error_code = 0, + void* engine_reserved = nullptr); +static napi_status napi_set_last_error(JsErrorCode jsError, + void* engine_reserved = nullptr); +static void napi_clear_last_error(); //Callback Info struct as per JSRT native function. struct CallbackInfo { @@ -46,6 +81,13 @@ struct CallbackInfo { napi_value returnValue; }; +struct napi_env__ { + explicit napi_env__(v8::Isolate* _isolate): isolate(_isolate) {} + ~napi_env__() {} + v8::Isolate* isolate; +}; + +namespace { namespace v8impl { //=== Conversion between V8 Isolate and napi_env ========================== @@ -105,25 +147,13 @@ namespace v8impl { // use intptr_t instead of void*) napi_value JsValueFromV8LocalValue(v8::Local local) { - // This is awkward, better way? memcpy but don't want that dependency? - union U { - napi_value v; - v8::Local l; - U(v8::Local _l) : l(_l) { } - } u(local); - assert(sizeof(u.v) == sizeof(u.l)); - return u.v; + return reinterpret_cast(*local); }; v8::Local V8LocalValueFromJsValue(napi_value v) { - // Likewise awkward - union U { - napi_value v; - v8::Local l; - U(napi_value _v) : v(_v) { } - } u(v); - assert(sizeof(u.v) == sizeof(u.l)); - return u.l; + v8::Local local; + memcpy(&local, &v, sizeof(v)); + return local; } } // end of namespace v8impl @@ -132,7 +162,14 @@ namespace jsrtimpl { // Adapter for JSRT external data + finalize callback. class ExternalData { public: - ExternalData(void* data, napi_finalize finalize_cb) : _data(data), _cb(finalize_cb) { + ExternalData(napi_env env, + void* data, + napi_finalize finalize_cb, + void* hint) + : _env(env), + _data(data), + _cb(finalize_cb), + _hint(hint) { } void* Data() { @@ -144,7 +181,8 @@ namespace jsrtimpl { ExternalData* externalData = reinterpret_cast(callbackState); if (externalData != nullptr) { if (externalData->_cb != nullptr) { - externalData->_cb(externalData->_data); + externalData->_cb( + externalData->_env, externalData->_data, externalData->_hint); } delete externalData; @@ -152,19 +190,22 @@ namespace jsrtimpl { } // node::Buffer::FreeCallback - static void Finalize(char* data, void* callbackState) { + static void FinalizeBuffer(char* data, void* callbackState) { Finalize(callbackState); } private: + napi_env _env; void* _data; napi_finalize _cb; + void* _hint; }; // Adapter for JSRT external callback + callback data. class ExternalCallback { public: - ExternalCallback(napi_callback cb, void* data) : _cb(cb), _data(data) { + ExternalCallback(napi_env env, napi_callback cb, void* data) + : _env(env), _cb(cb), _data(data) { } // JsNativeFunction @@ -176,6 +217,9 @@ namespace jsrtimpl { jsrtimpl::ExternalCallback* externalCallback = reinterpret_cast(callbackState); + // Make sure any errors encountered last time we were in N-API are gone. + napi_clear_last_error(); + JsErrorCode error = JsNoError; JsValueRef undefinedValue; error = JsGetUndefinedValue(&undefinedValue); @@ -208,9 +252,9 @@ namespace jsrtimpl { cbInfo.data = externalCallback->_data; cbInfo.returnValue = reinterpret_cast(undefinedValue); - // TODO(tawoll): get environment pointer instead of nullptr? - externalCallback->_cb(nullptr, reinterpret_cast(&cbInfo)); - return reinterpret_cast(cbInfo.returnValue); + napi_value result = externalCallback->_cb( + externalCallback->_env, reinterpret_cast(&cbInfo)); + return reinterpret_cast(result); } // JsObjectBeforeCollectCallback @@ -221,43 +265,100 @@ namespace jsrtimpl { } private: + napi_env _env; napi_callback _cb; void* _data; }; -} // end of namespace jsrtimpl + inline napi_status JsPropertyIdFromKey(JsValueRef key, + JsPropertyIdRef* propertyId) { + JsValueType keyType; + CHECK_JSRT(JsGetValueType(key, &keyType)); -#define RETURN_STATUS_IF_FALSE(condition, status) \ - do { \ - if (!(condition)) { \ - return napi_set_last_error((status)); \ - } \ - } while(0) + if (keyType == JsString) { + size_t length; + CHECK_JSRT_EXPECTED( + JsCopyStringUtf8(key, nullptr, 0, &length), napi_string_expected); -#define CHECK_ARG(arg) \ - RETURN_STATUS_IF_FALSE((arg), napi_invalid_arg) + std::vector name; + name.reserve(length + 1); + CHECK_JSRT(JsCopyStringUtf8(key, name.data(), length + 1, &length)); -#define CHECK_JSRT(expr) \ - do { \ - JsErrorCode err = (expr); \ - if (err != JsNoError) return napi_set_last_error(err); \ - } while(0) + CHECK_JSRT(JsCreatePropertyIdUtf8( + reinterpret_cast(name.data()), length, propertyId)); + } else { + CHECK_JSRT(JsGetPropertyIdFromSymbol(key, propertyId)); + } + return napi_ok; + } -#define CHECK_JSRT_EXPECTED(expr, expected) \ - do { \ - JsErrorCode err = (expr); \ - if (err == JsErrorInvalidArgument) \ - return napi_set_last_error(expected); \ - if (err != JsNoError) return napi_set_last_error(err); \ - } while(0) + inline napi_status + JsPropertyIdFromPropertyDescriptor(const napi_property_descriptor* p, + JsPropertyIdRef* propertyId) { + if (p->utf8name != nullptr) { + CHECK_JSRT(JsCreatePropertyIdUtf8( + p->utf8name, strlen(p->utf8name), propertyId)); + return napi_ok; + } else { + return JsPropertyIdFromKey(p->name, propertyId); + } + } -// This does not call napi_set_last_error because the expression -// is assumed to be a NAPI function call that already did. -#define CHECK_NAPI(expr) \ - do { \ - napi_status status = (expr); \ - if (status != napi_ok) return status; \ - } while(0) + inline napi_status + JsNameValueFromPropertyDescriptor(const napi_property_descriptor* p, + napi_value* name) { + if (p->utf8name != nullptr) { + CHECK_JSRT(JsCreateStringUtf8( + reinterpret_cast(p->utf8name), + strlen(p->utf8name), + reinterpret_cast(name))); + } else { + *name = p->name; + } + return napi_ok; + } +} // end of namespace jsrtimpl + +// Intercepts the Node-V8 module registration callback. Converts parameters +// to NAPI equivalents and then calls the registration callback specified +// by the NAPI module. +void napi_module_register_cb(v8::Local exports, + v8::Local module, + v8::Local context, + void* priv) { + napi_module* mod = static_cast(priv); + + // Create a new napi_env for this module. Once module unloading is supported + // we shall have to call delete on this object from there. + napi_env env = new napi_env__(context->GetIsolate()); + + mod->nm_register_func( + env, + v8impl::JsValueFromV8LocalValue(exports), + v8impl::JsValueFromV8LocalValue(module), + mod->nm_priv); +} + +} // end of anonymous namespace + +// Registers a NAPI module. +void napi_module_register(napi_module* mod) { + // NAPI modules always work with the current node version. + int module_version = NODE_MODULE_VERSION; + + node::node_module* nm = new node::node_module { + module_version, + mod->nm_flags, + nullptr, + mod->nm_filename, + nullptr, + napi_module_register_cb, + mod->nm_modname, + mod, // priv + nullptr, + }; + node::node_module_register(nm); +} // Static last error returned from napi_get_last_error_info napi_extended_error_info static_last_error; @@ -268,14 +369,20 @@ const char* error_messages[] = { "Invalid pointer passed as argument", "An object was expected", "A string was expected", + "A string or symbol was expected", "A function was expected", "A number was expected", "A boolean was expected", + "An array was expected", "Unknown failure", - "An exception is pending" + "An exception is pending", + "The async work item was cancelled", }; -const napi_extended_error_info* napi_get_last_error_info() { +napi_status napi_get_last_error_info(napi_env env, + const napi_extended_error_info** result) { + CHECK_ARG(result); + static_assert(sizeof(error_messages) / sizeof(*error_messages) == napi_status_last, "Count of error messages must match count of error values"); assert(static_last_error.error_code < napi_status_last); @@ -283,12 +390,19 @@ 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; + *result = &static_last_error; + return napi_ok; +} + +void napi_clear_last_error() { + static_last_error.error_code = napi_ok; + static_last_error.engine_error_code = 0; + static_last_error.engine_reserved = nullptr; } napi_status napi_set_last_error(napi_status error_code, - uint32_t engine_error_code = 0, - void* engine_reserved = nullptr) { + uint32_t engine_error_code, + void* engine_reserved) { static_last_error.error_code = error_code; static_last_error.engine_error_code = engine_error_code; static_last_error.engine_reserved = engine_reserved; @@ -296,7 +410,7 @@ napi_status napi_set_last_error(napi_status error_code, return error_code; } -napi_status napi_set_last_error(JsErrorCode jsError, void* engine_reserved = nullptr) { +napi_status napi_set_last_error(JsErrorCode jsError, void* engine_reserved) { napi_status status; switch (jsError) { case JsNoError: status = napi_ok; break; @@ -321,14 +435,72 @@ napi_status napi_get_current_env(napi_env* e) { return napi_ok; } -napi_status napi_create_function(napi_env e, napi_callback cb, void* data, napi_value* result) { +napi_status napi_create_function(napi_env env, + const char* utf8name, + napi_callback cb, + void* callback_data, + napi_value* result) { + CHECK_ARG(result); + + jsrtimpl::ExternalCallback* externalCallback = + new jsrtimpl::ExternalCallback(env, cb, callback_data); + if (externalCallback == nullptr) return napi_set_last_error(napi_generic_failure); + + JsValueRef function; + if (utf8name != nullptr) { + JsValueRef name; + CHECK_JSRT(JsCreateStringUtf8( + reinterpret_cast(utf8name), + strlen(utf8name), + &name)); + CHECK_JSRT(JsCreateNamedFunction( + name, + jsrtimpl::ExternalCallback::Callback, + externalCallback, + &function)); + } + else { + CHECK_JSRT(JsCreateFunction( + jsrtimpl::ExternalCallback::Callback, + externalCallback, + &function)); + } + + CHECK_JSRT(JsSetObjectBeforeCollectCallback( + function, externalCallback, jsrtimpl::ExternalCallback::Finalize)); + + *result = reinterpret_cast(function); + return napi_ok; +} + +static napi_status napi_create_property_function(napi_env env, + napi_value property_name, + napi_callback cb, + void* callback_data, + napi_value* result) { CHECK_ARG(result); - jsrtimpl::ExternalCallback* externalCallback = new jsrtimpl::ExternalCallback(cb, data); + jsrtimpl::ExternalCallback* externalCallback = + new jsrtimpl::ExternalCallback(env, cb, callback_data); if (externalCallback == nullptr) return napi_set_last_error(napi_generic_failure); + napi_valuetype nameType; + CHECK_NAPI(napi_typeof(env, property_name, &nameType)); + JsValueRef function; - CHECK_JSRT(JsCreateFunction(jsrtimpl::ExternalCallback::Callback, externalCallback, &function)); + if (nameType == napi_string) { + CHECK_JSRT(JsCreateNamedFunction( + property_name, + jsrtimpl::ExternalCallback::Callback, + externalCallback, + &function)); + } + else { + CHECK_JSRT(JsCreateFunction( + jsrtimpl::ExternalCallback::Callback, + externalCallback, + &function)); + } CHECK_JSRT(JsSetObjectBeforeCollectCallback( function, externalCallback, jsrtimpl::ExternalCallback::Finalize)); @@ -337,19 +509,20 @@ napi_status napi_create_function(napi_env e, napi_callback cb, void* data, napi_ return napi_ok; } -napi_status napi_define_class(napi_env e, +napi_status napi_define_class(napi_env env, const char* utf8name, napi_callback cb, void* data, - int property_count, + size_t property_count, const napi_property_descriptor* properties, napi_value* result) { CHECK_ARG(result); napi_value namestring; - CHECK_NAPI(napi_create_string_utf8(e, utf8name, -1, &namestring)); + CHECK_NAPI(napi_create_string_utf8(env, utf8name, -1, &namestring)); - jsrtimpl::ExternalCallback* externalCallback = new jsrtimpl::ExternalCallback(cb, data); + jsrtimpl::ExternalCallback* externalCallback = + new jsrtimpl::ExternalCallback(env, cb, data); if (externalCallback == nullptr) return napi_set_last_error(napi_generic_failure); JsValueRef constructor; @@ -369,8 +542,8 @@ napi_status napi_define_class(napi_env e, int instancePropertyCount = 0; int staticPropertyCount = 0; - for (int i = 0; i < property_count; i++) { - if ((properties[i].attributes & napi_static_property) != 0) { + for (size_t i = 0; i < property_count; i++) { + if ((properties[i].attributes & napi_static) != 0) { staticPropertyCount++; } else { @@ -382,76 +555,68 @@ napi_status napi_define_class(napi_env e, descriptors.reserve(std::max(instancePropertyCount, staticPropertyCount)); if (instancePropertyCount > 0) { - for (int i = 0; i < property_count; i++) { - if ((properties[i].attributes & napi_static_property) == 0) { + for (size_t i = 0; i < property_count; i++) { + if ((properties[i].attributes & napi_static) == 0) { descriptors.push_back(properties[i]); } } CHECK_NAPI(napi_define_properties( - e, reinterpret_cast(prototype), descriptors.size(), descriptors.data())); + env, reinterpret_cast(prototype), descriptors.size(), descriptors.data())); } if (staticPropertyCount > 0) { descriptors.clear(); - for (int i = 0; i < property_count; i++) { - if (!(properties[i].attributes & napi_static_property) != 0) { + for (size_t i = 0; i < property_count; i++) { + if (!(properties[i].attributes & napi_static) != 0) { descriptors.push_back(properties[i]); } } CHECK_NAPI(napi_define_properties( - e, reinterpret_cast(constructor), descriptors.size(), descriptors.data())); + env, reinterpret_cast(constructor), descriptors.size(), descriptors.data())); } *result = reinterpret_cast(constructor); return napi_ok; } -napi_status napi_set_function_name(napi_env e, napi_value func, - napi_propertyname name) { - // TODO: Consider removing the napi_set_function_name API. - // Chakra can only set a function name when creating the function. - // So, add a name parameter to napi_create_function instead. - return napi_ok; -} - -napi_status napi_set_return_value(napi_env e, - napi_callback_info cbinfo, napi_value v) { - CallbackInfo *info = (CallbackInfo*)cbinfo; - info->returnValue = v; +napi_status napi_get_property_names(napi_env env, napi_value object, napi_value* result) { + CHECK_ARG(result); + JsValueRef obj = reinterpret_cast(object); + JsValueRef propertyNames; + CHECK_JSRT(JsGetOwnPropertyNames(obj, &propertyNames)); + *result = reinterpret_cast(propertyNames); return napi_ok; } -napi_status napi_property_name(napi_env e, const char* utf8name, napi_propertyname* result) { - CHECK_ARG(result); +napi_status napi_set_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value value) { + JsValueRef obj = reinterpret_cast(object); JsPropertyIdRef propertyId; CHECK_JSRT(JsCreatePropertyIdUtf8(utf8name, strlen(utf8name), &propertyId)); - *result = reinterpret_cast(propertyId); - return napi_ok; -} - -napi_status napi_get_propertynames(napi_env e, napi_value o, napi_value* result) { - CHECK_ARG(result); - JsValueRef object = reinterpret_cast(o); - JsValueRef propertyNames; - CHECK_JSRT(JsGetOwnPropertyNames(object, &propertyNames)); - *result = reinterpret_cast(propertyNames); + JsValueRef js_value = reinterpret_cast(value); + CHECK_JSRT(JsSetProperty(obj, propertyId, js_value, true)); return napi_ok; } -napi_status napi_set_property(napi_env e, napi_value o, - napi_propertyname k, napi_value v) { - JsValueRef object = reinterpret_cast(o); - JsPropertyIdRef propertyId = reinterpret_cast(k); - JsValueRef value = reinterpret_cast(v); - CHECK_JSRT(JsSetProperty(object, propertyId, value, true)); +napi_status napi_set_property(napi_env env, + napi_value object, + napi_value key, + napi_value value) { + JsValueRef obj = reinterpret_cast(object); + JsPropertyIdRef propertyId; + CHECK_NAPI(jsrtimpl::JsPropertyIdFromKey(key, &propertyId)); + JsValueRef js_value = reinterpret_cast(value); + CHECK_JSRT(JsSetProperty(obj, propertyId, value, true)); return napi_ok; } -napi_status napi_define_properties(napi_env e, - napi_value o, - int property_count, +napi_status napi_define_properties(napi_env env, + napi_value object, + size_t property_count, const napi_property_descriptor* properties) { JsPropertyIdRef configurableProperty; CHECK_JSRT(JsCreatePropertyIdUtf8("configurable", 12, &configurableProperty)); @@ -459,54 +624,58 @@ napi_status napi_define_properties(napi_env e, JsPropertyIdRef enumerableProperty; CHECK_JSRT(JsCreatePropertyIdUtf8("enumerable", 10, &enumerableProperty)); - for (int i = 0; i < property_count; i++) { + for (size_t i = 0; i < property_count; i++) { const napi_property_descriptor* p = properties + i; JsValueRef descriptor; CHECK_JSRT(JsCreateObject(&descriptor)); JsValueRef configurable; - CHECK_JSRT(JsBoolToBoolean(!(p->attributes & napi_dont_delete), &configurable)); + CHECK_JSRT(JsBoolToBoolean((p->attributes & napi_configurable), &configurable)); CHECK_JSRT(JsSetProperty(descriptor, configurableProperty, configurable, true)); JsValueRef enumerable; - CHECK_JSRT(JsBoolToBoolean(!(p->attributes & napi_dont_enum), &enumerable)); + CHECK_JSRT(JsBoolToBoolean((p->attributes & napi_enumerable), &enumerable)); CHECK_JSRT(JsSetProperty(descriptor, enumerableProperty, enumerable, true)); - if (p->method) { - JsPropertyIdRef valueProperty; - CHECK_JSRT(JsCreatePropertyIdUtf8("value", 5, &valueProperty)); - JsValueRef method; - CHECK_NAPI(napi_create_function(e, p->method, p->data, - reinterpret_cast(&method))); - CHECK_JSRT(JsSetProperty(descriptor, valueProperty, method, true)); - } - else if (p->getter || p->setter) { - if (p->getter) { + if (p->getter != nullptr || p->setter != nullptr) { + napi_value property_name; + CHECK_NAPI(jsrtimpl::JsNameValueFromPropertyDescriptor(p, &property_name)); + + if (p->getter != nullptr) { JsPropertyIdRef getProperty; CHECK_JSRT(JsCreatePropertyIdUtf8("get", 3, &getProperty)); JsValueRef getter; - CHECK_NAPI(napi_create_function(e, p->getter, p->data, - reinterpret_cast(&getter))); + CHECK_NAPI(napi_create_property_function(env, property_name, + p->getter, p->data, reinterpret_cast(&getter))); CHECK_JSRT(JsSetProperty(descriptor, getProperty, getter, true)); } - if (p->setter) { + if (p->setter != nullptr) { JsPropertyIdRef setProperty; CHECK_JSRT(JsCreatePropertyIdUtf8("set", 5, &setProperty)); JsValueRef setter; - CHECK_NAPI(napi_create_function(e, p->setter, p->data, - reinterpret_cast(&setter))); + CHECK_NAPI(napi_create_property_function(env, property_name, + p->setter, p->data, reinterpret_cast(&setter))); CHECK_JSRT(JsSetProperty(descriptor, setProperty, setter, true)); } - } - else { + } else if (p->method != nullptr) { + napi_value property_name; + CHECK_NAPI(jsrtimpl::JsNameValueFromPropertyDescriptor(p, &property_name)); + + JsPropertyIdRef valueProperty; + CHECK_JSRT(JsCreatePropertyIdUtf8("value", 5, &valueProperty)); + JsValueRef method; + CHECK_NAPI(napi_create_property_function(env, property_name, + p->method, p->data, reinterpret_cast(&method))); + CHECK_JSRT(JsSetProperty(descriptor, valueProperty, method, true)); + } else { RETURN_STATUS_IF_FALSE(p->value != nullptr, napi_invalid_arg); JsPropertyIdRef writableProperty; CHECK_JSRT(JsCreatePropertyIdUtf8("writable", 8, &writableProperty)); JsValueRef writable; - CHECK_JSRT(JsBoolToBoolean(!(p->attributes & napi_read_only), &writable)); + CHECK_JSRT(JsBoolToBoolean((p->attributes & napi_writable), &writable)); CHECK_JSRT(JsSetProperty(descriptor, writableProperty, writable, true)); JsPropertyIdRef valueProperty; @@ -516,10 +685,10 @@ napi_status napi_define_properties(napi_env e, } JsPropertyIdRef nameProperty; - CHECK_JSRT(JsCreatePropertyIdUtf8(p->utf8name, strlen(p->utf8name), &nameProperty)); + CHECK_NAPI(jsrtimpl::JsPropertyIdFromPropertyDescriptor(p, &nameProperty)); bool result; CHECK_JSRT(JsDefineProperty( - reinterpret_cast(o), + reinterpret_cast(object), reinterpret_cast(nameProperty), reinterpret_cast(descriptor), &result)); @@ -528,9 +697,9 @@ napi_status napi_define_properties(napi_env e, return napi_ok; } -napi_status napi_instanceof(napi_env e, napi_value o, napi_value c, bool* result) { +napi_status napi_instanceof(napi_env env, napi_value object, napi_value c, bool* result) { CHECK_ARG(result); - JsValueRef object = reinterpret_cast(o); + JsValueRef obj = reinterpret_cast(object); JsValueRef constructor = reinterpret_cast(c); // FIXME: Remove this type check when we switch to a version of Chakracore @@ -538,60 +707,86 @@ napi_status napi_instanceof(napi_env e, napi_value o, napi_value c, bool* result // does not cause a segfault. The need for this if-statement is removed in at // least Chakracore 1.4.0, but maybe in an earlier version too. napi_valuetype valuetype; - CHECK_NAPI(napi_get_type_of_value(e, c, &valuetype)); + CHECK_NAPI(napi_typeof(env, c, &valuetype)); if (valuetype != napi_function) { - napi_throw_type_error(e, "constructor must be a function"); + napi_throw_type_error(env, "constructor must be a function"); return napi_set_last_error(napi_invalid_arg); } - CHECK_JSRT(JsInstanceOf(object, constructor, result)); + CHECK_JSRT(JsInstanceOf(obj, constructor, result)); + return napi_ok; +} + +napi_status napi_has_named_property(napi_env env, + napi_value object, + const char* utf8name, + bool* result) { + CHECK_ARG(result); + JsPropertyIdRef propertyId; + CHECK_JSRT(JsCreatePropertyIdUtf8(utf8name, strlen(utf8name), &propertyId)); + JsValueRef obj = reinterpret_cast(object); + CHECK_JSRT(JsHasProperty(obj, propertyId, result)); return napi_ok; } -napi_status napi_has_property(napi_env e, napi_value o, napi_propertyname k, bool* result) { +napi_status napi_has_property(napi_env env, napi_value object, napi_value key, bool* result) { CHECK_ARG(result); - JsPropertyIdRef propertyId = reinterpret_cast(k); - JsValueRef object = reinterpret_cast(o); - CHECK_JSRT(JsHasProperty(object, propertyId, result)); + JsPropertyIdRef propertyId; + CHECK_NAPI(jsrtimpl::JsPropertyIdFromKey(key, &propertyId)); + JsValueRef obj = reinterpret_cast(object); + CHECK_JSRT(JsHasProperty(obj, propertyId, result)); return napi_ok; } -napi_status napi_get_property(napi_env e, napi_value o, napi_propertyname k, napi_value* result) { +napi_status napi_get_named_property(napi_env env, + napi_value object, + const char* utf8name, + napi_value* result) { CHECK_ARG(result); - JsValueRef object = reinterpret_cast(o); - JsPropertyIdRef propertyId = reinterpret_cast(k); - CHECK_JSRT(JsGetProperty(object, propertyId, reinterpret_cast(result))); + JsValueRef obj = reinterpret_cast(object); + JsPropertyIdRef propertyId; + CHECK_JSRT(JsCreatePropertyIdUtf8(utf8name, strlen(utf8name), &propertyId)); + CHECK_JSRT(JsGetProperty(obj, propertyId, reinterpret_cast(result))); return napi_ok; } -napi_status napi_set_element(napi_env e, napi_value o, uint32_t i, napi_value v) { +napi_status napi_get_property(napi_env env, napi_value object, napi_value key, napi_value* result) { + CHECK_ARG(result); + JsValueRef obj = reinterpret_cast(object); + JsPropertyIdRef propertyId; + CHECK_NAPI(jsrtimpl::JsPropertyIdFromKey(key, &propertyId)); + CHECK_JSRT(JsGetProperty(obj, propertyId, reinterpret_cast(result))); + return napi_ok; +} + +napi_status napi_set_element(napi_env env, napi_value object, uint32_t i, napi_value v) { JsValueRef index = nullptr; - JsValueRef object = reinterpret_cast(o); + JsValueRef obj = reinterpret_cast(object); JsValueRef value = reinterpret_cast(v); CHECK_JSRT(JsIntToNumber(i, &index)); - CHECK_JSRT(JsSetIndexedProperty(object, index, value)); + CHECK_JSRT(JsSetIndexedProperty(obj, index, value)); return napi_ok; } -napi_status napi_has_element(napi_env e, napi_value o, uint32_t i, bool* result) { +napi_status napi_has_element(napi_env env, napi_value object, uint32_t i, bool* result) { CHECK_ARG(result); JsValueRef index = nullptr; CHECK_JSRT(JsIntToNumber(i, &index)); - JsValueRef object = reinterpret_cast(o); - CHECK_JSRT(JsHasIndexedProperty(object, index, result)); + JsValueRef obj = reinterpret_cast(object); + CHECK_JSRT(JsHasIndexedProperty(obj, index, result)); return napi_ok; } -napi_status napi_get_element(napi_env e, napi_value o, uint32_t i, napi_value* result) { +napi_status napi_get_element(napi_env env, napi_value object, uint32_t i, napi_value* result) { CHECK_ARG(result); JsValueRef index = nullptr; - JsValueRef object = reinterpret_cast(o); + JsValueRef obj = reinterpret_cast(object); CHECK_JSRT(JsIntToNumber(i, &index)); - CHECK_JSRT(JsGetIndexedProperty(object, index, reinterpret_cast(result))); + CHECK_JSRT(JsGetIndexedProperty(obj, index, reinterpret_cast(result))); return napi_ok; } -napi_status napi_is_array(napi_env e, napi_value v, bool* result) { +napi_status napi_is_array(napi_env env, napi_value v, bool* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); JsValueType type = JsUndefined; @@ -600,7 +795,7 @@ napi_status napi_is_array(napi_env e, napi_value v, bool* result) { return napi_ok; } -napi_status napi_get_array_length(napi_env e, napi_value v, uint32_t* result) { +napi_status napi_get_array_length(napi_env env, napi_value v, uint32_t* result) { CHECK_ARG(result); JsPropertyIdRef propertyIdRef; CHECK_JSRT(JsCreatePropertyIdUtf8("length", 7, &propertyIdRef)); @@ -613,7 +808,7 @@ napi_status napi_get_array_length(napi_env e, napi_value v, uint32_t* result) { return napi_ok; } -napi_status napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs, bool* result) { +napi_status napi_strict_equals(napi_env env, napi_value lhs, napi_value rhs, bool* result) { CHECK_ARG(result); JsValueRef object1 = reinterpret_cast(lhs); JsValueRef object2 = reinterpret_cast(rhs); @@ -621,71 +816,87 @@ napi_status napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs, bool* return napi_ok; } -napi_status napi_get_prototype(napi_env e, napi_value o, napi_value* result) { +napi_status napi_get_prototype(napi_env env, napi_value object, napi_value* result) { CHECK_ARG(result); - JsValueRef object = reinterpret_cast(o); - CHECK_JSRT(JsGetPrototype(object, reinterpret_cast(result))); + JsValueRef obj = reinterpret_cast(object); + CHECK_JSRT(JsGetPrototype(obj, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_object(napi_env e, napi_value* result) { +napi_status napi_create_object(napi_env env, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsCreateObject(reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_array(napi_env e, napi_value* result) { +napi_status napi_create_array(napi_env env, napi_value* result) { CHECK_ARG(result); unsigned int length = 0; CHECK_JSRT(JsCreateArray(length, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_array_with_length(napi_env e, int length, napi_value* result) { +napi_status napi_create_array_with_length(napi_env env, int length, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsCreateArray(length, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_string_utf8(napi_env e, const char* s, int length, +napi_status napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result) { + // TODO: Convert from Latin1 to UTF-8 encoding. + CHECK_ARG(result); + CHECK_JSRT(JsCreateStringUtf8( + reinterpret_cast(str), + length, + reinterpret_cast(result))); + return napi_ok; +} + +napi_status napi_create_string_utf8(napi_env env, + const char* str, + size_t length, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsCreateStringUtf8( - reinterpret_cast(s), - length < 0 ? strlen(s) : static_cast(length), + reinterpret_cast(str), + length, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_string_utf16(napi_env e, const char16_t* s, int length, +napi_status napi_create_string_utf16(napi_env env, + const char16_t* str, + size_t length, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsCreateStringUtf16( - reinterpret_cast(s), - length < 0 ? std::char_traits::length(s) : static_cast(length), + reinterpret_cast(str), + length, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_number(napi_env e, double v, napi_value* result) { +napi_status napi_create_number(napi_env env, double value, napi_value* result) { CHECK_ARG(result); - CHECK_JSRT(JsDoubleToNumber(v, reinterpret_cast(result))); + CHECK_JSRT(JsDoubleToNumber(value, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_boolean(napi_env e, bool b, napi_value* result) { +napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { CHECK_ARG(result); - CHECK_JSRT(JsBoolToBoolean(b, reinterpret_cast(result))); + CHECK_JSRT(JsBoolToBoolean(value, reinterpret_cast(result))); return napi_ok; } -napi_status napi_create_symbol(napi_env e, const char* s, napi_value* result) { +napi_status napi_create_symbol(napi_env env, + napi_value description, + napi_value* result) { CHECK_ARG(result); - JsValueRef description = nullptr; - if (s != nullptr) { - CHECK_JSRT(JsCreateStringUtf8((uint8_t*)s, strlen(s), &description)); - } - CHECK_JSRT(JsCreateSymbol(description, reinterpret_cast(result))); + JsValueRef js_description = reinterpret_cast(description); + CHECK_JSRT(JsCreateSymbol(js_description, reinterpret_cast(result))); return napi_ok; } @@ -710,7 +921,7 @@ napi_status napi_create_range_error(napi_env, napi_value msg, napi_value* result return napi_ok; } -napi_status napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* result) { +napi_status napi_typeof(napi_env env, napi_value vv, napi_valuetype* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(vv); JsValueType valueType = JsUndefined; @@ -724,6 +935,7 @@ napi_status napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* re case JsBoolean: *result = napi_boolean; break; case JsFunction: *result = napi_function; break; case JsSymbol: *result = napi_symbol; break; + case JsError: *result = napi_object; break; default: // An "external" value is represented in JSRT as an Object that has external data and @@ -744,125 +956,109 @@ napi_status napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* re return napi_ok; } -napi_status napi_get_undefined(napi_env e, napi_value* result) { +napi_status napi_get_undefined(napi_env env, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsGetUndefinedValue(reinterpret_cast(result))); return napi_ok; } -napi_status napi_get_null(napi_env e, napi_value* result) { +napi_status napi_get_null(napi_env env, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsGetNullValue(reinterpret_cast(result))); return napi_ok; } -napi_status napi_get_false(napi_env e, napi_value* result) { - CHECK_ARG(result); - CHECK_JSRT(JsGetFalseValue(reinterpret_cast(result))); - return napi_ok; -} - -napi_status napi_get_true(napi_env e, napi_value* result) { - CHECK_ARG(result); - CHECK_JSRT(JsGetTrueValue(reinterpret_cast(result))); - return napi_ok; -} - -napi_status napi_get_cb_args_length(napi_env e, napi_callback_info cbinfo, int* result) { +napi_status napi_get_cb_info( + napi_env env, // [in] NAPI environment handle + napi_callback_info cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + napi_value* argv, // [out] Array of values + napi_value* this_arg, // [out] Receives the JS 'this' arg for the call + void** data) { // [out] Receives the data pointer for the callback. CHECK_ARG(cbinfo); - CHECK_ARG(result); const CallbackInfo *info = reinterpret_cast(cbinfo); - *result = info->argc; - return napi_ok; -} -napi_status napi_is_construct_call(napi_env e, napi_callback_info cbinfo, bool* result) { - CHECK_ARG(cbinfo); - CHECK_ARG(result); - const CallbackInfo *info = reinterpret_cast(cbinfo); - *result = info->isConstructCall; - return napi_ok; -} + if (argv != nullptr) { + CHECK_ARG(argc); -// copy encoded arguments into provided buffer or return direct pointer to -// encoded arguments array? -napi_status napi_get_cb_args(napi_env e, napi_callback_info cbinfo, - napi_value* buffer, int bufferlength) { - CHECK_ARG(cbinfo); - const CallbackInfo *info = reinterpret_cast(cbinfo); + size_t i = 0; + size_t min = std::min(*argc, static_cast(info->argc)); - int i = 0; - int min = std::min(bufferlength, static_cast(info->argc)); + for (; i < min; i++) { + argv[i] = info->argv[i]; + } - for (; i < min; i++) { - buffer[i] = info->argv[i]; + if (i < *argc) { + napi_value undefined; + CHECK_JSRT(JsGetUndefinedValue(reinterpret_cast(&undefined))); + for (; i < *argc; i++) { + argv[i] = undefined; + } + } } - if (i < bufferlength) { - napi_value undefined; - CHECK_JSRT(JsGetUndefinedValue(reinterpret_cast(&undefined))); - for (; i < bufferlength; i += 1) { - buffer[i] = undefined; - } + if (argc != nullptr) { + *argc = info->argc; } - return napi_ok; -} + if (this_arg != nullptr) { + *this_arg = info->thisArg; + } -napi_status napi_get_cb_this(napi_env e, napi_callback_info cbinfo, napi_value* result) { - CHECK_ARG(cbinfo); - CHECK_ARG(result); - const CallbackInfo *info = reinterpret_cast(cbinfo); - *result = info->thisArg; - return napi_ok; -} + if (data != nullptr) { + *data = info->data; + } -// 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_status napi_get_cb_holder(napi_env e, napi_callback_info cbinfo, napi_value* result) { - CHECK_ARG(cbinfo); - CHECK_ARG(result); - const CallbackInfo *info = reinterpret_cast(cbinfo); - *result = info->thisArg; return napi_ok; } -napi_status napi_get_cb_data(napi_env e, napi_callback_info cbinfo, void** result) { +napi_status napi_is_construct_call(napi_env env, napi_callback_info cbinfo, bool* result) { CHECK_ARG(cbinfo); CHECK_ARG(result); const CallbackInfo *info = reinterpret_cast(cbinfo); - *result = info->data; + *result = info->isConstructCall; return napi_ok; } -napi_status napi_call_function(napi_env e, napi_value recv, - napi_value func, int argc, napi_value* argv, napi_value* result) { - CHECK_ARG(result); +napi_status napi_call_function(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result) { JsValueRef object = reinterpret_cast(recv); JsValueRef function = reinterpret_cast(func); - std::vector args(argc+1); + std::vector args(argc + 1); args[0] = object; - for (int i = 0; i < argc; i++) { + for (size_t i = 0; i < argc; i++) { args[i + 1] = reinterpret_cast(argv[i]); } - CHECK_JSRT(JsCallFunction(function, args.data(), argc + 1, reinterpret_cast(result))); + JsValueRef returnValue; + CHECK_JSRT(JsCallFunction( + function, + args.data(), + static_cast(argc + 1), + &returnValue)); + if (result != nullptr) { + *result = reinterpret_cast(returnValue); + } return napi_ok; } -napi_status napi_get_global(napi_env e, napi_value* result) { +napi_status napi_get_global(napi_env env, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsGetGlobalObject(reinterpret_cast(result))); return napi_ok; } -napi_status napi_throw(napi_env e, napi_value error) { +napi_status napi_throw(napi_env env, napi_value error) { JsValueRef exception = reinterpret_cast(error); CHECK_JSRT(JsSetException(exception)); return napi_ok; } -napi_status napi_throw_error(napi_env e, const char* msg) { +napi_status napi_throw_error(napi_env env, const char* msg) { JsValueRef strRef; JsValueRef exception; size_t length = strlen(msg); @@ -872,7 +1068,7 @@ napi_status napi_throw_error(napi_env e, const char* msg) { return napi_ok; } -napi_status napi_throw_type_error(napi_env e, const char* msg) { +napi_status napi_throw_type_error(napi_env env, const char* msg) { JsValueRef strRef; JsValueRef exception; size_t length = strlen(msg); @@ -882,7 +1078,7 @@ napi_status napi_throw_type_error(napi_env e, const char* msg) { return napi_ok; } -napi_status napi_throw_range_error(napi_env e, const char* msg) { +napi_status napi_throw_range_error(napi_env env, const char* msg) { JsValueRef strRef; JsValueRef exception; size_t length = strlen(msg); @@ -892,7 +1088,15 @@ napi_status napi_throw_range_error(napi_env e, const char* msg) { return napi_ok; } -napi_status napi_get_value_double(napi_env e, napi_value v, double* result) { +napi_status napi_is_error(napi_env env, napi_value value, bool* result) { + CHECK_ARG(result); + JsValueType valueType; + CHECK_JSRT(JsGetValueType(value, &valueType)); + *result = (valueType == JsError); + return napi_ok; +} + +napi_status napi_get_value_double(napi_env env, napi_value v, double* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); JsValueRef numberValue = nullptr; @@ -901,7 +1105,7 @@ napi_status napi_get_value_double(napi_env e, napi_value v, double* result) { return napi_ok; } -napi_status napi_get_value_int32(napi_env e, napi_value v, int32_t* result) { +napi_status napi_get_value_int32(napi_env env, napi_value v, int32_t* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); int valueInt; @@ -911,7 +1115,7 @@ napi_status napi_get_value_int32(napi_env e, napi_value v, int32_t* result) { return napi_ok; } -napi_status napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result) { +napi_status napi_get_value_uint32(napi_env env, napi_value v, uint32_t* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); int valueInt; @@ -921,7 +1125,7 @@ napi_status napi_get_value_uint32(napi_env e, napi_value v, uint32_t* result) { return napi_ok; } -napi_status napi_get_value_int64(napi_env e, napi_value v, int64_t* result) { +napi_status napi_get_value_int64(napi_env env, napi_value v, int64_t* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); int valueInt; @@ -931,7 +1135,7 @@ napi_status napi_get_value_int64(napi_env e, napi_value v, int64_t* result) { return napi_ok; } -napi_status napi_get_value_bool(napi_env e, napi_value v, bool* result) { +napi_status napi_get_value_bool(napi_env env, napi_value v, bool* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); JsValueRef booleanValue = nullptr; @@ -939,156 +1143,213 @@ napi_status napi_get_value_bool(napi_env e, napi_value v, bool* result) { return napi_ok; } -// Gets the number of CHARACTERS in the string. -napi_status napi_get_value_string_length(napi_env e, napi_value v, int* result) { - CHECK_ARG(result); - - JsValueRef value = reinterpret_cast(v); - CHECK_JSRT_EXPECTED(JsGetStringLength(value, result), napi_string_expected); - return napi_ok; -} - -// Gets the number of BYTES in the UTF-8 encoded representation of the string. -napi_status napi_get_value_string_utf8_length(napi_env e, napi_value v, int* result) { - CHECK_ARG(result); +// Copies a JavaScript string into a LATIN-1 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_latin1(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + CHECK_ARG(value); + JsValueRef js_value = reinterpret_cast(value); + + // TODO: Convert from UTF-8 to Latin1 encoding. + if (!buf) { + CHECK_ARG(result); + + JsErrorCode err = JsCopyStringUtf8(js_value, nullptr, 0, result); + if (err != JsErrorInvalidArgument) { + return napi_set_last_error(err); + } + } + else { + size_t copied = 0; + CHECK_JSRT_EXPECTED( + JsCopyStringUtf8( + js_value, reinterpret_cast(buf), bufsize, &copied), + napi_string_expected); + + if (copied < bufsize) { + buf[copied] = 0; + copied++; + } + else { + buf[bufsize - 1] = 0; + } - JsValueRef value = reinterpret_cast(v); + if (result != nullptr) { + *result = copied - 1; + } + } - size_t length; - CHECK_JSRT_EXPECTED(JsCopyStringUtf8(value, nullptr, 0, &length), napi_string_expected); - *result = length; return napi_ok; } -// Copies a JavaScript string into a UTF-8 string buffer. The result is the number -// of bytes copied into buf, including the null terminator. If the buf size is -// insufficient, the string will be truncated, including a null terminator. -napi_status napi_get_value_string_utf8( - napi_env e, napi_value v, char* buf, int bufsize, int* result) { - CHECK_ARG(buf); - - if (bufsize == 0) { - *result = 0; - return napi_ok; - } - - JsValueRef value = reinterpret_cast(v); +// Copies a JavaScript string into a UTF-8 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf8(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) { + CHECK_ARG(value); + JsValueRef js_value = reinterpret_cast(value); - size_t copied = 0; - CHECK_JSRT_EXPECTED(JsCopyStringUtf8(value, reinterpret_cast(buf), - static_cast(bufsize), &copied), napi_string_expected); + if (!buf) { + CHECK_ARG(result); - if (copied < static_cast(bufsize)) { - buf[copied] = 0; - copied++; - } - else { - buf[bufsize - 1] = 0; - } + JsErrorCode err = JsCopyStringUtf8(js_value, nullptr, 0, result); + if (err != JsErrorInvalidArgument) { + return napi_set_last_error(err); + } + } else { + size_t copied = 0; + CHECK_JSRT_EXPECTED( + JsCopyStringUtf8( + js_value, reinterpret_cast(buf), bufsize, &copied), + napi_string_expected); + + if (copied < bufsize) { + buf[copied] = 0; + copied++; + } + else { + buf[bufsize - 1] = 0; + } - if (result != nullptr) { - *result = static_cast(copied); + if (result != nullptr) { + *result = copied - 1; + } } - return napi_ok; -} - -// Gets the number of 2-byte code units in the UTF-16 encoded representation of the string. -napi_status napi_get_value_string_utf16_length(napi_env e, napi_value v, int* result) { - CHECK_ARG(result); - - JsValueRef value = reinterpret_cast(v); - size_t length; - CHECK_JSRT_EXPECTED(JsCopyStringUtf16(value, 0, 0, nullptr, &length), napi_string_expected); - *result = length; return napi_ok; } -// Copies a JavaScript string into a UTF-16 string buffer. The result is the number -// of 2-byte code units copied into buf, including the null terminator. If the buf -// size is insufficient, the string will be truncated, including a null terminator. -napi_status napi_get_value_string_utf16( - napi_env e, napi_value v, char16_t* buf, int bufsize, int* result) { - CHECK_ARG(buf); - - if (bufsize == 0) { - *result = 0; - return napi_ok; - } - - JsValueRef value = reinterpret_cast(v); +// Copies a JavaScript string into a UTF-16 string buffer. The result is the +// number of 2-byte code units (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in 2-byte +// code units) via the result parameter. +// The result argument is optional unless buf is NULL. +napi_status napi_get_value_string_utf16(napi_env env, + napi_value value, + char16_t* buf, + size_t bufsize, + size_t* result) { + CHECK_ARG(value); + JsValueRef js_value = reinterpret_cast(value); - size_t copied = 0; - CHECK_JSRT_EXPECTED(JsCopyStringUtf16(value, 0, static_cast(bufsize), - reinterpret_cast(buf), &copied), napi_string_expected); + if (!buf) { + CHECK_ARG(result); - if (copied < static_cast(bufsize)) { - buf[copied] = 0; - copied++; + JsErrorCode err = JsCopyStringUtf16(js_value, 0, -1, nullptr, result); + if (err != JsErrorInvalidArgument) { + return napi_set_last_error(err); + } } else { - buf[bufsize - 1] = 0; - } + size_t copied = 0; + CHECK_JSRT_EXPECTED( + JsCopyStringUtf16( + js_value, + 0, + static_cast(bufsize), + reinterpret_cast(buf), + &copied), + napi_string_expected); + + if (copied < bufsize) { + buf[copied] = 0; + copied++; + } + else { + buf[bufsize - 1] = 0; + } - if (result != nullptr) { - *result = static_cast(copied); + if (result != nullptr) { + *result = copied - 1; + } } + return napi_ok; } -napi_status napi_coerce_to_number(napi_env e, napi_value v, napi_value* result) { +napi_status napi_coerce_to_number(napi_env env, napi_value v, napi_value* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); CHECK_JSRT(JsConvertValueToNumber(value, reinterpret_cast(result))); return napi_ok; } -napi_status napi_coerce_to_bool(napi_env e, napi_value v, napi_value* result) { +napi_status napi_coerce_to_bool(napi_env env, napi_value v, napi_value* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); CHECK_JSRT(JsConvertValueToBoolean(value, reinterpret_cast(result))); return napi_ok; } -napi_status napi_coerce_to_object(napi_env e, napi_value v, napi_value* result) { +napi_status napi_coerce_to_object(napi_env env, napi_value v, napi_value* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); CHECK_JSRT(JsConvertValueToObject(value, reinterpret_cast(result))); return napi_ok; } -napi_status napi_coerce_to_string(napi_env e, napi_value v, napi_value* result) { +napi_status napi_coerce_to_string(napi_env env, napi_value v, napi_value* result) { CHECK_ARG(result); JsValueRef value = reinterpret_cast(v); CHECK_JSRT(JsConvertValueToString(value, reinterpret_cast(result))); return napi_ok; } -napi_status napi_wrap(napi_env e, napi_value jsObject, void* nativeObj, - napi_finalize finalize_cb, napi_ref* result) { - JsValueRef value = reinterpret_cast(jsObject); +napi_status napi_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + JsValueRef value = reinterpret_cast(js_object); - jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData(nativeObj, finalize_cb); + jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData( + env, native_object, finalize_cb, finalize_hint); if (externalData == nullptr) return napi_set_last_error(napi_generic_failure); CHECK_JSRT(JsSetExternalData(value, externalData)); if (result != nullptr) { - napi_create_reference(e, jsObject, 0, result); + napi_create_reference(env, js_object, 0, result); } return napi_ok; } -napi_status napi_unwrap(napi_env e, napi_value jsObject, void** result) { - return napi_get_value_external(e, jsObject, result); +napi_status napi_unwrap(napi_env env, napi_value jsObject, void** result) { + return napi_get_value_external(env, jsObject, result); } -napi_status napi_create_external(napi_env e, void* data, napi_finalize finalize_cb, - napi_value* result) { +napi_status napi_create_external(napi_env env, + void* data, + napi_finalize finalize_cb, + void* finalize_hint, + napi_value* result) { CHECK_ARG(result); - jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData(data, finalize_cb); + jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData( + env, data, finalize_cb, finalize_hint); if (externalData == nullptr) return napi_set_last_error(napi_generic_failure); CHECK_JSRT(JsCreateExternalObject( @@ -1100,7 +1361,7 @@ napi_status napi_create_external(napi_env e, void* data, napi_finalize finalize_ return napi_ok; } -napi_status napi_get_value_external(napi_env e, napi_value v, void** result) { +napi_status napi_get_value_external(napi_env env, napi_value v, void** result) { CHECK_ARG(result); jsrtimpl::ExternalData* externalData; @@ -1114,8 +1375,10 @@ napi_status napi_get_value_external(napi_env e, napi_value v, void** result) { } // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. -napi_status napi_create_reference(napi_env e, napi_value v, int initial_refcount, - napi_ref* result) { +napi_status napi_create_reference(napi_env env, + napi_value v, + uint32_t initial_refcount, + napi_ref* result) { CHECK_ARG(result); JsValueRef strongRef = reinterpret_cast(v); @@ -1126,7 +1389,7 @@ napi_status napi_create_reference(napi_env e, napi_value v, int initial_refcount CHECK_JSRT(JsAddRef(weakRef, nullptr)); // Apply the initial refcount to the target value. - for (int i = 0; i < initial_refcount; i++) { + for (uint32_t i = 0; i < initial_refcount; i++) { CHECK_JSRT(JsAddRef(strongRef, nullptr)); // Also increment the refcount of the reference by the same amount, @@ -1140,7 +1403,7 @@ napi_status napi_create_reference(napi_env e, napi_value v, int initial_refcount // Deletes a reference. The referenced value is released, and may be GC'd unless there // are other references to it. -napi_status napi_delete_reference(napi_env e, napi_ref ref) { +napi_status napi_delete_reference(napi_env env, napi_ref ref) { JsRef weakRef = reinterpret_cast(ref); unsigned int count; @@ -1164,7 +1427,7 @@ napi_status napi_delete_reference(napi_env e, napi_ref ref) { // reference will be a strong reference because its refcount is >0, and the referenced object is // effectively "pinned". Calling this when the refcount is 0 and the target is unavailable // results in an error. -napi_status napi_reference_addref(napi_env e, napi_ref ref, int* result) { +napi_status napi_reference_addref(napi_env env, napi_ref ref, int* result) { JsRef weakRef = reinterpret_cast(ref); JsValueRef target; @@ -1190,7 +1453,7 @@ napi_status napi_reference_addref(napi_env e, napi_ref ref, int* result) { // Decrements the reference count, optionally returning the resulting count. If the result is // 0 the reference is now weak and the object may be GC'd at any time if there are no other // references. Calling this when the refcount is already 0 results in an error. -napi_status napi_reference_release(napi_env e, napi_ref ref, int* result) { +napi_status napi_reference_release(napi_env env, napi_ref ref, int* result) { JsRef weakRef = reinterpret_cast(ref); JsValueRef target; @@ -1218,7 +1481,7 @@ napi_status napi_reference_release(napi_env e, napi_ref ref, int* result) { // Attempts to get a referenced value. If the reference is weak, the value might no longer be // available, in that case the call is still successful but the result is NULL. -napi_status napi_get_reference_value(napi_env e, napi_ref ref, napi_value* result) { +napi_status napi_get_reference_value(napi_env env, napi_ref ref, napi_value* result) { CHECK_ARG(result); JsWeakRef weakRef = reinterpret_cast(ref); CHECK_JSRT(JsGetWeakReferenceValue(weakRef, reinterpret_cast(result))); @@ -1247,7 +1510,7 @@ napi_status napi_close_escapable_handle_scope(napi_env, napi_escapable_handle_sc } //This one will return escapee value as this is called from leveldown db. -napi_status napi_escape_handle(napi_env e, napi_escapable_handle_scope scope, +napi_status napi_escape_handle(napi_env env, napi_escapable_handle_scope scope, napi_value escapee, napi_value* result) { CHECK_ARG(result); *result = escapee; @@ -1255,21 +1518,27 @@ napi_status napi_escape_handle(napi_env e, napi_escapable_handle_scope scope, } /**************************************************************/ -napi_status napi_new_instance(napi_env e, napi_value cons, - int argc, napi_value *argv, napi_value* result) { +napi_status napi_new_instance(napi_env env, + napi_value constructor, + size_t argc, + const napi_value* argv, + napi_value* result) { CHECK_ARG(result); - JsValueRef function = reinterpret_cast(cons); + JsValueRef function = reinterpret_cast(constructor); std::vector args(argc + 1); CHECK_JSRT(JsGetUndefinedValue(&args[0])); - for (int i = 0; i < argc; i++) { + for (size_t i = 0; i < argc; i++) { args[i + 1] = reinterpret_cast(argv[i]); } - CHECK_JSRT(JsConstructObject(function, args.data(), argc + 1, + CHECK_JSRT(JsConstructObject( + function, + args.data(), + static_cast(argc + 1), reinterpret_cast(result))); return napi_ok; } -napi_status napi_make_external(napi_env e, napi_value v, napi_value* result) { +napi_status napi_make_external(napi_env env, napi_value v, napi_value* result) { CHECK_ARG(result); JsValueRef externalObj; CHECK_JSRT(JsCreateExternalObject(NULL, NULL, &externalObj)); @@ -1278,19 +1547,29 @@ napi_status napi_make_external(napi_env e, napi_value v, napi_value* result) { return napi_ok; } -napi_status napi_make_callback(napi_env e, napi_value recv, - napi_value func, int argc, napi_value* argv, napi_value* result) { - CHECK_ARG(result); +napi_status napi_make_callback(napi_env env, + napi_value recv, + napi_value func, + size_t argc, + const napi_value* argv, + napi_value* result) { JsValueRef object = reinterpret_cast(recv); JsValueRef function = reinterpret_cast(func); std::vector args(argc + 1); args[0] = object; - for (int i = 0; i < argc; i++) + for (size_t i = 0; i < argc; i++) { args[i + 1] = reinterpret_cast(argv[i]); } - CHECK_JSRT(JsCallFunction(function, args.data(), argc + 1, - reinterpret_cast(result))); + JsValueRef returnValue; + CHECK_JSRT(JsCallFunction( + function, + args.data(), + static_cast(argc + 1), + &returnValue)); + if (result != nullptr) { + *result = reinterpret_cast(returnValue); + } return napi_ok; } @@ -1308,11 +1587,14 @@ void CALLBACK ExternalArrayBufferFinalizeCallback(void *data) static_cast(data)->Free(); } -napi_status napi_create_buffer(napi_env e, size_t size, char** data, napi_value* result) { +napi_status napi_create_buffer(napi_env env, + size_t length, + void** data, + napi_value* result) { CHECK_ARG(result); // TODO(tawoll): Replace v8impl with jsrt-based version. - v8::MaybeLocal maybe = node::Buffer::New(v8impl::V8IsolateFromJsEnv(e), size); + v8::MaybeLocal maybe = node::Buffer::New(v8impl::V8IsolateFromJsEnv(env), length); if (maybe.IsEmpty()) { return napi_generic_failure; } @@ -1326,20 +1608,22 @@ napi_status napi_create_buffer(napi_env e, size_t size, char** data, napi_value* return napi_ok; } -napi_status napi_create_external_buffer(napi_env e, - size_t size, - char* data, +napi_status napi_create_external_buffer(napi_env env, + size_t length, + void* data, napi_finalize finalize_cb, + void* finalize_hint, napi_value* result) { CHECK_ARG(result); // TODO(tawoll): Replace v8impl with jsrt-based version. - jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData(data, finalize_cb); + jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData( + env, data, finalize_cb, finalize_hint); v8::MaybeLocal maybe = node::Buffer::New( - v8impl::V8IsolateFromJsEnv(e), - data, - size, - jsrtimpl::ExternalData::Finalize, + v8impl::V8IsolateFromJsEnv(env), + static_cast(data), + length, + jsrtimpl::ExternalData::FinalizeBuffer, externalData); if (maybe.IsEmpty()) { @@ -1350,23 +1634,31 @@ napi_status napi_create_external_buffer(napi_env e, return napi_ok; } -napi_status napi_create_buffer_copy(napi_env e, - const char* data, - size_t size, +napi_status napi_create_buffer_copy(napi_env env, + size_t length, + const void* data, + void** result_data, napi_value* result) { CHECK_ARG(result); // TODO(tawoll): Implement node::Buffer in terms of napi to avoid using chakra shim here. v8::MaybeLocal maybe = node::Buffer::Copy( - v8impl::V8IsolateFromJsEnv(e), data, size); + v8impl::V8IsolateFromJsEnv(env), static_cast(data), length); if (maybe.IsEmpty()) { return napi_generic_failure; } - *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + + v8::Local buffer = maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(buffer); + + if (result_data != nullptr) { + *result_data = node::Buffer::Data(buffer); + } + return napi_ok; } -napi_status napi_is_buffer(napi_env e, napi_value v, bool* result) { +napi_status napi_is_buffer(napi_env env, napi_value v, bool* result) { CHECK_ARG(result); JsValueRef typedArray = reinterpret_cast(v); JsValueType objectType; @@ -1381,8 +1673,11 @@ napi_status napi_is_buffer(napi_env e, napi_value v, bool* result) { return napi_ok; } -napi_status napi_get_buffer_info(napi_env e, napi_value v, char** data, size_t* length) { - JsValueRef typedArray = reinterpret_cast(v); +napi_status napi_get_buffer_info(napi_env env, + napi_value value, + void** data, + size_t* length) { + JsValueRef typedArray = reinterpret_cast(value); JsValueRef arrayBuffer; unsigned int byteOffset; ChakraBytePtr buffer; @@ -1391,7 +1686,7 @@ napi_status napi_get_buffer_info(napi_env e, napi_value v, char** data, size_t* CHECK_JSRT(JsGetArrayBufferStorage(arrayBuffer, &buffer, &bufferLength)); if (data != nullptr) { - *data = reinterpret_cast(buffer + byteOffset); + *data = static_cast(buffer + byteOffset); } if (length != nullptr) { @@ -1407,17 +1702,17 @@ napi_status napi_is_exception_pending(napi_env, bool* result) { return napi_ok; } -napi_status napi_get_and_clear_last_exception(napi_env e, napi_value* result) { +napi_status napi_get_and_clear_last_exception(napi_env env, napi_value* result) { CHECK_ARG(result); CHECK_JSRT(JsGetAndClearException(reinterpret_cast(result))); if (*result == nullptr) { // Is this necessary? - CHECK_NAPI(napi_get_undefined(e, result)); + CHECK_NAPI(napi_get_undefined(env, result)); } return napi_ok; } -napi_status napi_is_arraybuffer(napi_env e, napi_value value, bool* result) { +napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) { CHECK_ARG(result); JsValueRef jsValue = reinterpret_cast(value); @@ -1428,7 +1723,7 @@ napi_status napi_is_arraybuffer(napi_env e, napi_value value, bool* result) { return napi_ok; } -napi_status napi_create_arraybuffer(napi_env e, +napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, void** data, napi_value* result) { @@ -1448,14 +1743,16 @@ napi_status napi_create_arraybuffer(napi_env e, return napi_ok; } -napi_status napi_create_external_arraybuffer(napi_env e, +napi_status napi_create_external_arraybuffer(napi_env env, void* external_data, size_t byte_length, napi_finalize finalize_cb, + void* finalize_hint, napi_value* result) { CHECK_ARG(result); - jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData(external_data, finalize_cb); + jsrtimpl::ExternalData* externalData = new jsrtimpl::ExternalData( + env, external_data, finalize_cb, finalize_hint); if (externalData == nullptr) return napi_set_last_error(napi_generic_failure); JsValueRef arrayBuffer; @@ -1470,7 +1767,7 @@ napi_status napi_create_external_arraybuffer(napi_env e, return napi_ok; } -napi_status napi_get_arraybuffer_info(napi_env e, +napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, void** data, size_t* byte_length) { @@ -1492,7 +1789,7 @@ napi_status napi_get_arraybuffer_info(napi_env e, return napi_ok; } -napi_status napi_is_typedarray(napi_env e, napi_value value, bool* result) { +napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) { CHECK_ARG(result); JsValueRef jsValue = reinterpret_cast(value); @@ -1503,7 +1800,7 @@ napi_status napi_is_typedarray(napi_env e, napi_value value, bool* result) { return napi_ok; } -napi_status napi_create_typedarray(napi_env e, +napi_status napi_create_typedarray(napi_env env, napi_typedarray_type type, size_t length, napi_value arraybuffer, @@ -1513,31 +1810,31 @@ napi_status napi_create_typedarray(napi_env e, JsTypedArrayType jsType; switch (type) { - case napi_int8: + case napi_int8_array: jsType = JsArrayTypeInt8; break; - case napi_uint8: + case napi_uint8_array: jsType = JsArrayTypeUint8; break; - case napi_uint8_clamped: + case napi_uint8_clamped_array: jsType = JsArrayTypeUint8Clamped; break; - case napi_int16: + case napi_int16_array: jsType = JsArrayTypeInt16; break; - case napi_uint16: + case napi_uint16_array: jsType = JsArrayTypeUint16; break; - case napi_int32: + case napi_int32_array: jsType = JsArrayTypeInt32; break; - case napi_uint32: + case napi_uint32_array: jsType = JsArrayTypeUint32; break; - case napi_float32: + case napi_float32_array: jsType = JsArrayTypeFloat32; break; - case napi_float64: + case napi_float64_array: jsType = JsArrayTypeFloat64; break; default: @@ -1556,7 +1853,7 @@ napi_status napi_create_typedarray(napi_env e, return napi_ok; } -napi_status napi_get_typedarray_info(napi_env e, +napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray, napi_typedarray_type* type, size_t* length, @@ -1588,31 +1885,31 @@ napi_status napi_get_typedarray_info(napi_env e, if (type != nullptr) { switch (jsType) { case JsArrayTypeInt8: - *type = napi_int8; + *type = napi_int8_array; break; case JsArrayTypeUint8: - *type = napi_uint8; + *type = napi_uint8_array; break; case JsArrayTypeUint8Clamped: - *type = napi_uint8_clamped; + *type = napi_uint8_clamped_array; break; case JsArrayTypeInt16: - *type = napi_int16; + *type = napi_int16_array; break; case JsArrayTypeUint16: - *type = napi_uint16; + *type = napi_uint16_array; break; case JsArrayTypeInt32: - *type = napi_int32; + *type = napi_int32_array; break; case JsArrayTypeUint32: - *type = napi_uint32; + *type = napi_uint32_array; break; case JsArrayTypeFloat32: - *type = napi_float32; + *type = napi_float32_array; break; case JsArrayTypeFloat64: - *type = napi_float64; + *type = napi_float64_array; break; default: return napi_set_last_error(napi_generic_failure); @@ -1637,3 +1934,135 @@ napi_status napi_get_typedarray_info(napi_env e, return napi_ok; } + +namespace uvimpl { + + napi_status ConvertUVErrorCode(int code) { + switch (code) { + case 0: + return napi_ok; + case UV_EINVAL: + return napi_invalid_arg; + case UV_ECANCELED: + return napi_cancelled; + } + + return napi_generic_failure; + } + + // Wrapper around uv_work_t which calls user-provided callbacks. + class Work { + private: + explicit Work(napi_env env, + napi_async_execute_callback execute = nullptr, + napi_async_complete_callback complete = nullptr, + void* data = nullptr) + : _env(env), + _data(data), + _execute(execute), + _complete(complete) { + memset(&_request, 0, sizeof(_request)); + _request.data = this; + } + + ~Work() { } + + public: + static Work* New(napi_env env, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data) { + return new Work(env, execute, complete, data); + } + + static void Delete(Work* work) { + delete work; + } + + static void ExecuteCallback(uv_work_t* req) { + Work* work = static_cast(req->data); + work->_execute(work->_env, work->_data); + } + + static void CompleteCallback(uv_work_t* req, int status) { + Work* work = static_cast(req->data); + + if (work->_complete != nullptr) { + work->_complete(work->_env, ConvertUVErrorCode(status), work->_data); + } + } + + uv_work_t* Request() { + return &_request; + } + + private: + napi_env _env; + void* _data; + uv_work_t _request; + napi_async_execute_callback _execute; + napi_async_complete_callback _complete; + }; + +} // end of namespace uvimpl + +#define CALL_UV(condition) \ + do { \ + int result = (condition); \ + napi_status status = uvimpl::ConvertUVErrorCode(result); \ + if (status != napi_ok) { \ + return napi_set_last_error(status, result); \ + } \ + } while (0) + +napi_status napi_create_async_work(napi_env env, + napi_async_execute_callback execute, + napi_async_complete_callback complete, + void* data, + napi_async_work* result) { + CHECK_ARG(execute); + CHECK_ARG(result); + + uvimpl::Work* work = uvimpl::Work::New(env, execute, complete, data); + + *result = reinterpret_cast(work); + + return napi_ok; +} + +napi_status napi_delete_async_work(napi_env env, napi_async_work work) { + CHECK_ARG(work); + + uvimpl::Work::Delete(reinterpret_cast(work)); + + return napi_ok; +} + +napi_status napi_queue_async_work(napi_env env, napi_async_work work) { + CHECK_ARG(work); + + // Consider: Encapsulate the uv_loop_t into an opaque pointer parameter. + // Currently the environment event loop is the same as the UV default loop. + // Someday (if node ever supports multiple isolates), it may be better to get + // the loop from node::Environment::GetCurrent(env->isolate)->event_loop(); + uv_loop_t* event_loop = uv_default_loop(); + + uvimpl::Work* w = reinterpret_cast(work); + + CALL_UV(uv_queue_work(event_loop, + w->Request(), + uvimpl::Work::ExecuteCallback, + uvimpl::Work::CompleteCallback)); + + return napi_ok; +} + +napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { + CHECK_ARG(work); + + uvimpl::Work* w = reinterpret_cast(work); + + CALL_UV(uv_cancel(reinterpret_cast(w->Request()))); + + return napi_ok; +} diff --git a/src/node_api_types.h b/src/node_api_types.h new file mode 100644 index 0000000000..4bf1b82631 --- /dev/null +++ b/src/node_api_types.h @@ -0,0 +1,105 @@ +#ifndef SRC_NODE_API_TYPES_H_ +#define SRC_NODE_API_TYPES_H_ + +#include +#include + +#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) + typedef uint16_t char16_t; +#endif + +// JSVM API types are all opaque pointers for ABI stability +// typedef undefined structs instead of void* for compile time type safety +typedef struct napi_env__ *napi_env; +typedef struct napi_value__ *napi_value; +typedef struct napi_ref__ *napi_ref; +typedef struct napi_handle_scope__ *napi_handle_scope; +typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; +typedef struct napi_callback_info__ *napi_callback_info; +typedef struct napi_async_work__ *napi_async_work; + +typedef enum { + napi_default = 0, + napi_writable = 1 << 0, + napi_enumerable = 1 << 1, + napi_configurable = 1 << 2, + + // Used with napi_define_class to distinguish static properties + // from instance properties. Ignored by napi_define_properties. + napi_static = 1 << 10, +} napi_property_attributes; + +typedef enum { + // ES6 types (corresponds to typeof) + napi_undefined, + napi_null, + napi_boolean, + napi_number, + napi_string, + napi_symbol, + napi_object, + napi_function, + napi_external, +} napi_valuetype; + +typedef enum { + napi_int8_array, + napi_uint8_array, + napi_uint8_clamped_array, + napi_int16_array, + napi_uint16_array, + napi_int32_array, + napi_uint32_array, + napi_float32_array, + napi_float64_array, +} napi_typedarray_type; + +typedef enum { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_name_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_array_expected, + napi_generic_failure, + napi_pending_exception, + napi_cancelled, + napi_status_last +} napi_status; + +typedef napi_value (*napi_callback)(napi_env env, + napi_callback_info info); +typedef void (*napi_finalize)(napi_env env, + void* finalize_data, + void* finalize_hint); +typedef void (*napi_async_execute_callback)(napi_env env, + void* data); +typedef void (*napi_async_complete_callback)(napi_env env, + napi_status status, + void* data); + +typedef struct { + // One of utf8name or name should be NULL. + const char* utf8name; + napi_value name; + + napi_callback method; + napi_callback getter; + napi_callback setter; + napi_value value; + + napi_property_attributes attributes; + void* data; +} napi_property_descriptor; + +typedef struct { + const char* error_message; + void* engine_reserved; + uint32_t engine_error_code; + napi_status error_code; +} napi_extended_error_info; + +#endif // SRC_NODE_API_TYPES_H_ diff --git a/src/node_asyncapi.cc b/src/node_asyncapi.cc deleted file mode 100644 index 7e05ad86f6..0000000000 --- a/src/node_asyncapi.cc +++ /dev/null @@ -1,62 +0,0 @@ -#include "node_asyncapi_internal.h" - -napi_work napi_create_async_work() { - napi_work_impl* worker = (napi_work_impl*) malloc(sizeof(napi_work_impl)); - uv_work_t* req = (uv_work_t*) malloc(sizeof(uv_work_t)); - req->data = worker; - worker->work = req; - return reinterpret_cast(worker); -} - -void napi_delete_async_work(napi_work w) { - napi_work_impl* worker = reinterpret_cast(w); - if (worker != NULL) { - if (worker->work != NULL) { - free(reinterpret_cast(worker->work)); - } - free(worker); - worker = NULL; - } -} - - -void napi_async_set_data(napi_work w, void* data) { - napi_work_impl* worker = reinterpret_cast(w); - worker->data = data; -} - -void napi_async_set_execute(napi_work w, void (*execute)(void* data)) { - napi_work_impl* worker = reinterpret_cast(w); - worker->execute = execute; -} - -void napi_async_set_complete(napi_work w, void (*complete)(void* data)) { - napi_work_impl* worker = reinterpret_cast(w); - worker->complete = complete; -} - -void napi_async_set_destroy(napi_work w, void (*destroy)(void* data)) { - napi_work_impl* worker = reinterpret_cast(w); - worker->destroy = destroy; -} - -void napi_async_execute(uv_work_t* req) { - napi_work_impl* worker = static_cast(req->data); - worker->execute(worker->data); -} - -void napi_async_complete(uv_work_t* req) { - napi_work_impl* worker = static_cast(req->data); - worker->complete(worker->data); - worker->destroy(worker->data); -} - -void napi_async_queue_worker(napi_work w) { - napi_work_impl* worker = reinterpret_cast(w); - uv_queue_work( - uv_default_loop(), - worker->work, - napi_async_execute, - reinterpret_cast( - napi_async_complete)); -} diff --git a/src/node_asyncapi.h b/src/node_asyncapi.h deleted file mode 100644 index dffa194490..0000000000 --- a/src/node_asyncapi.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SRC_NODE_ASYNCAPI_H_ -#define SRC_NODE_ASYNCAPI_H_ - -#include "node_asyncapi_types.h" -#include - -#ifndef NODE_EXTERN -# ifdef _WIN32 -# ifndef BUILDING_NODE_EXTENSION -# define NODE_EXTERN __declspec(dllexport) -# else -# define NODE_EXTERN __declspec(dllimport) -# endif -# else -# define NODE_EXTERN /* nothing */ -# endif -#endif - -extern "C" { -NODE_EXTERN napi_work napi_create_async_work(); -NODE_EXTERN void napi_delete_async_work(napi_work w); -NODE_EXTERN void napi_async_set_data(napi_work w, void* data); -NODE_EXTERN void napi_async_set_execute(napi_work w, void (*execute)(void*)); -NODE_EXTERN void napi_async_set_complete(napi_work w, void (*complete)(void*)); -NODE_EXTERN void napi_async_set_destroy(napi_work w, void (*destroy)(void*)); -NODE_EXTERN void napi_async_queue_worker(napi_work w); -} // extern "C" - -#endif // SRC_NODE_ASYNCAPI_H_ diff --git a/src/node_asyncapi_internal.h b/src/node_asyncapi_internal.h deleted file mode 100644 index 8831488aef..0000000000 --- a/src/node_asyncapi_internal.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SRC_NODE_ASYNCAPI_INTERNAL_H_ -#define SRC_NODE_ASYNCAPI_INTERNAL_H_ - -#include "node_asyncapi.h" -#include "uv.h" - -typedef struct napi_work_impl__ { - uv_work_t* work; - void* data; - void (*execute)(void* data); - void (*complete)(void* data); - void (*destroy)(void* data); -} napi_work_impl; - - -void napi_async_execute(uv_work_t* req); -void napi_async_complete(uv_work_t* req); - - -#endif // SRC_NODE_ASYNCAPI_INTERNAL_H_ diff --git a/src/node_asyncapi_types.h b/src/node_asyncapi_types.h deleted file mode 100644 index ba91be190d..0000000000 --- a/src/node_asyncapi_types.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef SRC_NODE_ASYNCAPI_TYPES_H_ -#define SRC_NODE_ASYNCAPI_TYPES_H_ - -// LIBUV API types are all opaque pointers for ABI stability -// typedef undefined structs instead of void* for compile time type safety -typedef struct napi_uv_work_t__ *napi_work; - -#endif // SRC_NODE_ASYNCAPI_TYPES_H_ diff --git a/src/node_jsvmapi.cc b/src/node_jsvmapi.cc deleted file mode 100644 index c9e6a137ed..0000000000 --- a/src/node_jsvmapi.cc +++ /dev/null @@ -1,1299 +0,0 @@ -/******************************************************************************* - * Experimental prototype for demonstrating VM agnostic and ABI stable API - * for native modules to use instead of using Nan and V8 APIs directly. - * - * This is an rough proof of concept not intended for real world usage. - * It is currently far from a sufficiently completed work. - * - * - The API is not complete nor agreed upon. - * - The API is not yet analyzed for performance. - * - Performance is expected to suffer with the usage of opaque types currently - * requiring all operations to go across the DLL boundary, i.e. no inlining - * even for operations such as asking for the type of a value or retrieving a - * function 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. - * - ******************************************************************************/ - -#include "node_jsvmapi_internal.h" -#include -#include -#include -#include - -void napi_module_register(void* mod) { - node::node_module_register(mod); -} - -namespace v8impl { - -//=== Conversion between V8 Isolate and napi_env ========================== - - napi_env JsEnvFromV8Isolate(v8::Isolate* isolate) { - return reinterpret_cast(isolate); - } - - v8::Isolate* V8IsolateFromJsEnv(napi_env e) { - return reinterpret_cast(e); - } - - class HandleScopeWrapper { - public: - explicit HandleScopeWrapper( - v8::Isolate* isolate) : scope(isolate) { } - private: - v8::HandleScope scope; - }; - - // In node v0.10 version of v8, there is no EscapableHandleScope and the - // node v0.10 port use HandleScope::Close(Local v) to mimic the behavior - // of a EscapableHandleScope::Escape(Local v), but it is not the same - // semantics. This is an example of where the api abstraction fail to work - // across different versions. - class EscapableHandleScopeWrapper { - public: - explicit EscapableHandleScopeWrapper( - v8::Isolate* isolate) : scope(isolate) { } - template - v8::Local Escape(v8::Local handle) { - return scope.Escape(handle); - } - private: - v8::EscapableHandleScope scope; - }; - - napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) { - return reinterpret_cast(s); - } - - HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) { - return reinterpret_cast(s); - } - - napi_escapable_handle_scope - JsEscapableHandleScopeFromV8EscapableHandleScope( - EscapableHandleScopeWrapper* s) { - return reinterpret_cast(s); - } - - EscapableHandleScopeWrapper* - V8EscapableHandleScopeFromJsEscapableHandleScope( - napi_escapable_handle_scope s) { - return reinterpret_cast(s); - } - -//=== Conversion between V8 Handles and napi_value ======================== - - // This is assuming v8::Local<> will always be implemented with a single - // pointer field so that we can pass it around as a void* (maybe we should - // use intptr_t instead of void*) - - napi_value JsValueFromV8LocalValue(v8::Local local) { - // This is awkward, better way? memcpy but don't want that dependency? - union U { - napi_value v; - v8::Local l; - U(v8::Local _l) : l(_l) { } - } u(local); - assert(sizeof(u.v) == sizeof(u.l)); - return u.v; - } - - v8::Local V8LocalValueFromJsValue(napi_value v) { - // Likewise awkward - union U { - napi_value v; - v8::Local l; - U(napi_value _v) : v(_v) { } - } u(v); - assert(sizeof(u.v) == sizeof(u.l)); - return u.l; - } - - v8::Local V8LocalValueFromJsPropertyName(napi_propertyname pn) { - // Likewise awkward - union U { - napi_propertyname pn; - v8::Local l; - U(napi_propertyname _pn) : pn(_pn) { } - } u(pn); - assert(sizeof(u.pn) == sizeof(u.l)); - return u.l; - } - - static v8::Local V8LocalFunctionFromJsValue(napi_value v) { - // Likewise awkward - union U { - napi_value v; - v8::Local f; - U(napi_value _v) : v(_v) { } - } u(v); - assert(sizeof(u.v) == sizeof(u.f)); - return u.f; - } - - static napi_persistent JsPersistentFromV8PersistentValue( - v8::Persistent *per) { - return (napi_persistent) per; - } - - static v8::Persistent* V8PersistentValueFromJsPersistentValue( - napi_persistent per) { - return (v8::Persistent*) per; - } - - static napi_weakref JsWeakRefFromV8PersistentValue( - v8::Persistent *per) { - return (napi_weakref) per; - } - - static v8::Persistent* V8PersistentValueFromJsWeakRefValue( - napi_weakref per) { - return (v8::Persistent*) per; - } - - static void WeakRefCallback(const v8::WeakCallbackInfo& data) { - } - - class TryCatch: public v8::TryCatch { - public: - explicit TryCatch(v8::Isolate *isolate): - v8::TryCatch(isolate), _isolate(isolate) {} - - ~TryCatch() { - if (HasCaught()) { - lastException().Reset(_isolate, Exception()); - } - } - - static v8::Persistent & lastException() { - static v8::Persistent theException; - return theException; - } - private: - v8::Isolate *_isolate; - }; - -//=== Function napi_callback wrapper ================================= - - static const int kDataIndex = 0; - - static const int kFunctionIndex = 1; - static const int kFunctionFieldCount = 2; - - static const int kGetterIndex = 1; - static const int kSetterIndex = 2; - static const int kAccessorFieldCount = 3; - - // Interface implemented by classes that wrap V8 function and property - // callback info. - interface CallbackWrapper { - virtual napi_value This() = 0; - virtual napi_value Holder() = 0; - virtual bool IsConstructCall() = 0; - virtual void* Data() = 0; - virtual int ArgsLength() = 0; - virtual void Args(napi_value* buffer, size_t bufferlength) = 0; - virtual void SetReturnValue(napi_value v) = 0; - }; - - template - class CallbackWrapperBase : public CallbackWrapper { - public: - CallbackWrapperBase(const T& cbinfo) - : _cbinfo(cbinfo), - _cbdata(cbinfo.Data().As()) { - } - - virtual napi_value This() override { - return JsValueFromV8LocalValue(_cbinfo.This()); - } - - virtual napi_value Holder() override { - return JsValueFromV8LocalValue(_cbinfo.Holder()); - } - - virtual bool IsConstructCall() override { - return false; - } - - virtual void* Data() override { - return _cbdata->GetInternalField(kDataIndex).As()->Value(); - } - - protected: - void InvokeCallback() { - napi_callback_info cbinfo_wrapper = reinterpret_cast( - static_cast(this)); - napi_callback cb = reinterpret_cast( - _cbdata->GetInternalField(I).As()->Value()); - v8::Isolate* isolate = _cbinfo.GetIsolate(); - cb(v8impl::JsEnvFromV8Isolate(isolate), cbinfo_wrapper); - - if (!TryCatch::lastException().IsEmpty()) { - isolate->ThrowException(TryCatch::lastException().Get(isolate)); - TryCatch::lastException().Reset(); - } - } - - const T& _cbinfo; - const v8::Local _cbdata; - }; - - class FunctionCallbackWrapper : public CallbackWrapperBase< - v8::FunctionCallbackInfo, kFunctionIndex> { - public: - static void Invoke(const v8::FunctionCallbackInfo &info) { - FunctionCallbackWrapper cbwrapper(info); - cbwrapper.InvokeCallback(); - } - - FunctionCallbackWrapper( - const v8::FunctionCallbackInfo& cbinfo) - : CallbackWrapperBase(cbinfo) { - } - - virtual bool IsConstructCall() override { - return _cbinfo.IsConstructCall(); - } - - virtual int ArgsLength() override { - return _cbinfo.Length(); - } - - virtual void Args(napi_value* buffer, size_t bufferlength) override { - int i = 0; - // size_t appropriate for the buffer length parameter? - // Probably this API is not the way to go. - int min = - static_cast(bufferlength) < _cbinfo.Length() ? - static_cast(bufferlength) : _cbinfo.Length(); - - for (; i < min; i += 1) { - buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]); - } - - if (i < static_cast(bufferlength)) { - napi_value undefined = v8impl::JsValueFromV8LocalValue( - v8::Undefined(v8::Isolate::GetCurrent())); - for (; i < static_cast(bufferlength); i += 1) { - buffer[i] = undefined; - } - } - } - - virtual void SetReturnValue(napi_value v) override { - v8::Local val = v8impl::V8LocalValueFromJsValue(v); - _cbinfo.GetReturnValue().Set(val); - } - }; - - class GetterCallbackWrapper : public CallbackWrapperBase< - v8::PropertyCallbackInfo, kGetterIndex> { - public: - static void Invoke( - v8::Local property, - const v8::PropertyCallbackInfo& info) { - GetterCallbackWrapper cbwrapper(info); - cbwrapper.InvokeCallback(); - } - - GetterCallbackWrapper( - const v8::PropertyCallbackInfo& cbinfo) - : CallbackWrapperBase(cbinfo) { - } - - virtual int ArgsLength() override { - return 0; - } - - virtual void Args(napi_value* buffer, size_t bufferlength) override { - if (bufferlength > 0) { - napi_value undefined = v8impl::JsValueFromV8LocalValue( - v8::Undefined(v8::Isolate::GetCurrent())); - for (int i = 0; i < static_cast(bufferlength); i += 1) { - buffer[i] = undefined; - } - } - } - - virtual void SetReturnValue(napi_value v) override { - v8::Local val = v8impl::V8LocalValueFromJsValue(v); - _cbinfo.GetReturnValue().Set(val); - } - }; - - class SetterCallbackWrapper : public CallbackWrapperBase< - v8::PropertyCallbackInfo, kSetterIndex> { - public: - static void Invoke( - v8::Local property, - v8::Local v, - const v8::PropertyCallbackInfo& info) { - SetterCallbackWrapper cbwrapper(info, v); - cbwrapper.InvokeCallback(); - } - - SetterCallbackWrapper( - const v8::PropertyCallbackInfo& cbinfo, - const v8::Local& value) - : CallbackWrapperBase(cbinfo), - _value(value) { - } - - virtual int ArgsLength() override { - return 1; - } - - virtual void Args(napi_value* buffer, size_t bufferlength) override { - if (bufferlength > 0) { - buffer[0] = v8impl::JsValueFromV8LocalValue(_value); - - if (bufferlength > 1) { - napi_value undefined = v8impl::JsValueFromV8LocalValue( - v8::Undefined(v8::Isolate::GetCurrent())); - for (int i = 1; i < static_cast(bufferlength); i += 1) { - buffer[i] = undefined; - } - } - } - } - - virtual void SetReturnValue(napi_value v) override { - // Cannot set the return value of a setter. - assert(false); - } - - private: - const v8::Local& _value; - }; - - class ObjectWrapWrapper: public node::ObjectWrap { - public: - ObjectWrapWrapper(napi_value jsObject, void* nativeObj, - napi_destruct destructor) { - _destructor = destructor; - _nativeObj = nativeObj; - Wrap(V8LocalValueFromJsValue(jsObject)->ToObject()); - } - - void* getValue() { - return _nativeObj; - } - - static void* Unwrap(napi_value jsObject) { - ObjectWrapWrapper* wrapper = - ObjectWrap::Unwrap( - V8LocalValueFromJsValue(jsObject)->ToObject()); - return wrapper->getValue(); - } - - virtual ~ObjectWrapWrapper() { - if (_destructor != nullptr) { - _destructor(_nativeObj); - } - } - - private: - napi_destruct _destructor; - void* _nativeObj; - }; - - // Creates an object to be made available to the static function callback - // wrapper, used to retrieve the native callback function and data pointer. - v8::Local CreateFunctionCallbackData( - napi_env e, - napi_callback cb, - void* data) { - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local context = isolate->GetCurrentContext(); - - v8::Local otpl = v8::ObjectTemplate::New(isolate); - otpl->SetInternalFieldCount(v8impl::kFunctionFieldCount); - v8::Local cbdata = otpl->NewInstance(context).ToLocalChecked(); - - cbdata->SetInternalField( - v8impl::kFunctionIndex, - v8::External::New(isolate, reinterpret_cast(cb))); - - if (data) { - cbdata->SetInternalField( - v8impl::kDataIndex, - v8::External::New(isolate, reinterpret_cast(data))); - } - - return cbdata; - } - - // Creates an object to be made available to the static getter/setter - // 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) { - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local context = isolate->GetCurrentContext(); - - v8::Local otpl = v8::ObjectTemplate::New(isolate); - otpl->SetInternalFieldCount(v8impl::kAccessorFieldCount); - v8::Local cbdata = otpl->NewInstance(context).ToLocalChecked(); - - if (getter) { - cbdata->SetInternalField( - v8impl::kGetterIndex, - v8::External::New(isolate, reinterpret_cast(getter))); - } - - if (setter) { - cbdata->SetInternalField( - v8impl::kSetterIndex, - v8::External::New(isolate, reinterpret_cast(setter))); - } - - if (data) { - cbdata->SetInternalField( - v8impl::kDataIndex, - v8::External::New(isolate, reinterpret_cast(data))); - } - - return cbdata; - } - -} // 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))) - -napi_env napi_get_current_env() { - return v8impl::JsEnvFromV8Isolate(v8::Isolate::GetCurrent()); -} - -napi_value napi_create_function( - napi_env e, - napi_callback cb, - void* data) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local context = isolate->GetCurrentContext(); - v8::Local retval; - - v8::EscapableHandleScope scope(isolate); - - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(e, cb, data); - v8::Local tpl = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); - - retval = scope.Escape(tpl->GetFunction()); - return v8impl::JsValueFromV8LocalValue(retval); -} - -napi_value napi_create_constructor( - napi_env e, - char* utf8name, - napi_callback cb, - void* data, - int property_count, - napi_property_descriptor* properties) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local context = isolate->GetCurrentContext(); - v8::Local retval; - - v8::EscapableHandleScope scope(isolate); - v8::Local cbdata = - v8impl::CreateFunctionCallbackData(e, cb, data); - v8::Local tpl = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); - - // we need an internal field to stash the wrapped object - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - v8::Local namestring = - v8::String::NewFromUtf8(isolate, utf8name, - v8::NewStringType::kInternalized).ToLocalChecked(); - 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::PropertyAttribute attributes = - static_cast(p->attributes); - - // This code is similar to that in napi_define_property(); the - // difference is it applies to a template instead of an object. - if (p->method) { - v8::Local cbdata = v8impl::CreateFunctionCallbackData( - e, p->method, p->data); - - v8::Local t = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata, - v8::Signature::New(isolate, tpl)); - t->SetClassName(propertyname); - - tpl->PrototypeTemplate()->Set(propertyname, t, attributes); - } - else if (p->getter || p->setter) { - v8::Local cbdata = v8impl::CreateAccessorCallbackData( - e, p->getter, p->setter, p->data); - - tpl->PrototypeTemplate()->SetAccessor( - propertyname, - v8impl::GetterCallbackWrapper::Invoke, - p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, - cbdata, - v8::AccessControl::DEFAULT, - attributes); - } - else { - v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - tpl->PrototypeTemplate()->Set( - propertyname, - value, - attributes); - } - } - - retval = scope.Escape(tpl->GetFunction()); - return v8impl::JsValueFromV8LocalValue(retval); -} - -void 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()); -} - -void napi_set_return_value(napi_env e, - napi_callback_info cbinfo, napi_value v) { - NAPI_PREAMBLE(e); - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); - info->SetReturnValue(v); -} - -napi_propertyname napi_property_name(napi_env e, const char* utf8name) { - NAPI_PREAMBLE(e); - v8::Local namestring = - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), utf8name, - v8::NewStringType::kInternalized).ToLocalChecked(); - return reinterpret_cast( - v8impl::JsValueFromV8LocalValue(namestring)); -} - -napi_value napi_get_propertynames(napi_env e, napi_value o) { - NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local array = obj->GetPropertyNames(); - return v8impl::JsValueFromV8LocalValue(array); -} - -void 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); - - // 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) -} - -bool napi_has_property(napi_env e, napi_value o, napi_propertyname k) { - NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - v8::Local key = v8impl::V8LocalValueFromJsPropertyName(k); - - return obj->Has(key); -} - -napi_value napi_get_property(napi_env e, napi_value o, napi_propertyname k) { - NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - 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); -} - -void 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::Local val = v8impl::V8LocalValueFromJsValue(v); - - obj->Set(i, val); - - // 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) -} - -bool napi_has_element(napi_env e, napi_value o, uint32_t i) { - NAPI_PREAMBLE(e); - v8::Local obj = v8impl::V8LocalValueFromJsValue(o)->ToObject(); - - return obj->Has(i); -} - -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 - // 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); -} - -void napi_define_property(napi_env e, napi_value o, - napi_property_descriptor* p) { - 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(); - - v8::PropertyAttribute attributes = - static_cast(p->attributes); - - if (p->method) { - v8::Local cbdata = v8impl::CreateFunctionCallbackData( - e, p->method, p->data); - - v8::Local t = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); - - obj->DefineOwnProperty( - context, - name, - t->GetFunction(), - attributes); - } - else if (p->getter || p->setter) { - v8::Local cbdata = v8impl::CreateAccessorCallbackData( - e, p->getter, p->setter, p->data); - - obj->SetAccessor( - context, - name, - v8impl::GetterCallbackWrapper::Invoke, - p->setter ? v8impl::SetterCallbackWrapper::Invoke : nullptr, - cbdata, - v8::AccessControl::DEFAULT, - attributes); - } - else { - v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - obj->DefineOwnProperty( - context, - name, - value, - attributes); - } -} - -bool napi_is_array(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - v8::Local val = v8impl::V8LocalValueFromJsValue(v); - return val->IsArray(); -} - -uint32_t napi_get_array_length(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - v8::Local arr = - v8impl::V8LocalValueFromJsValue(v).As(); - return arr->Length(); -} - -bool napi_strict_equals(napi_env e, napi_value lhs, napi_value rhs) { - NAPI_PREAMBLE(e); - v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); - v8::Local b = v8impl::V8LocalValueFromJsValue(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_value napi_create_object(napi_env e) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Object::New(v8impl::V8IsolateFromJsEnv(e))); -} - -napi_value napi_create_array(napi_env e) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Array::New(v8impl::V8IsolateFromJsEnv(e))); -} - -napi_value napi_create_array_with_length(napi_env e, int length) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Array::New(v8impl::V8IsolateFromJsEnv(e), length)); -} - -napi_value napi_create_string(napi_env e, const char* s) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s)); -} - -napi_value napi_create_string_with_length(napi_env e, - const char* s, size_t length) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::String::NewFromUtf8(v8impl::V8IsolateFromJsEnv(e), s, - v8::NewStringType::kNormal, - static_cast(length)).ToLocalChecked()); -} - -napi_value napi_create_number(napi_env e, double v) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Number::New(v8impl::V8IsolateFromJsEnv(e), v)); -} - -napi_value napi_create_boolean(napi_env e, bool b) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Boolean::New(v8impl::V8IsolateFromJsEnv(e), b)); -} - -napi_value napi_create_symbol(napi_env e, const char* s) { - NAPI_PREAMBLE(e); - v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(e); - if (s == NULL) { - return v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); - } else { - v8::Local string = v8::String::NewFromUtf8(isolate, s); - return v8impl::JsValueFromV8LocalValue( - v8::Symbol::New(isolate, string)); - } -} - -napi_value napi_create_error(napi_env e, napi_value msg) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Exception::Error( - v8impl::V8LocalValueFromJsValue(msg).As())); -} - -napi_value napi_create_type_error(napi_env e, napi_value msg) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Exception::TypeError( - v8impl::V8LocalValueFromJsValue(msg).As())); -} - -napi_valuetype napi_get_type_of_value(napi_env e, napi_value vv) { - v8::Local v = v8impl::V8LocalValueFromJsValue(vv); - NAPI_PREAMBLE(e); - - if (v->IsNumber()) { - return napi_number; - } else if (v->IsString()) { - return napi_string; - } else if (v->IsFunction()) { - // This test has to come before IsObject because IsFunction - // implies IsObject - return napi_function; - } else if (v->IsObject()) { - return napi_object; - } else if (v->IsBoolean()) { - return napi_boolean; - } else if (v->IsUndefined()) { - return napi_undefined; - } else if (v->IsSymbol()) { - return napi_symbol; - } else if (v->IsNull()) { - return napi_null; - } else { - return napi_object; // Is this correct? - } -} - -napi_value napi_get_undefined_(napi_env e) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Undefined(v8impl::V8IsolateFromJsEnv(e))); -} - -napi_value napi_get_null(napi_env e) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::Null(v8impl::V8IsolateFromJsEnv(e))); -} - -napi_value napi_get_false(napi_env e) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::False(v8impl::V8IsolateFromJsEnv(e))); -} - -napi_value napi_get_true(napi_env e) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8::True(v8impl::V8IsolateFromJsEnv(e))); -} - -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(); -} - -// 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_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(); -} - -// 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_PREAMBLE(e); - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); - return info->Holder(); -} - -void* napi_get_cb_data(napi_env e, napi_callback_info cbinfo) { - v8impl::CallbackWrapper* info = - reinterpret_cast(cbinfo); - return info->Data(); -} - -napi_value napi_call_function(napi_env e, napi_value recv, - napi_value func, int argc, napi_value* argv) { - NAPI_PREAMBLE(e); - std::vector> args(argc); - - v8::Local v8func = v8impl::V8LocalFunctionFromJsValue(func); - v8::Handle v8recv = - v8impl::V8LocalValueFromJsValue(recv)->ToObject(); - for (int i = 0; i < argc; i++) { - args[i] = v8impl::V8LocalValueFromJsValue(argv[i]); - } - v8::Handle result = v8func->Call(v8recv, argc, args.data()); - return v8impl::JsValueFromV8LocalValue(result); -} - -napi_value napi_get_global(napi_env e) { - NAPI_PREAMBLE(e); - 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()); -} - -void 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 -} - -void napi_throw_error(napi_env e, const char* msg) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - - isolate->ThrowException( - v8::Exception::Error(v8::String::NewFromUtf8(isolate, msg))); - // any VM calls after this point and before returning - // to the javascript invoker will fail -} - -void napi_throw_type_error(napi_env e, const char* msg) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - - isolate->ThrowException( - v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, msg))); - // any VM calls after this point and before returning - // to the javascript invoker will fail -} - -double napi_get_number_from_value(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->NumberValue(); -} - -int napi_get_string_from_value(napi_env e, napi_value v, - char* buf, const int buf_size) { - NAPI_PREAMBLE(e); - if (napi_get_type_of_value(e, v) == napi_number) { - v8::String::Utf8Value str(v8impl::V8LocalValueFromJsValue(v)); - int len = str.length(); - if (buf_size > len) { - memcpy(buf, *str, len); - return 0; - } else { - memcpy(buf, *str, buf_size - 1); - return len - buf_size + 1; - } - } else { - int len = napi_get_string_utf8_length(e, v); - int copied = v8impl::V8LocalValueFromJsValue(v).As() - ->WriteUtf8( - buf, - buf_size, - 0, - v8::String::REPLACE_INVALID_UTF8 | - v8::String::PRESERVE_ONE_BYTE_NULL); - // add one for null ending - return len - copied + 1; - } -} - -int32_t napi_get_value_int32(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->Int32Value(); -} - -uint32_t napi_get_value_uint32(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->Uint32Value(); -} - -int64_t napi_get_value_int64(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->IntegerValue(); -} - -bool napi_get_value_bool(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v)->BooleanValue(); -} - -int napi_get_string_length(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v).As()->Length(); -} - -int napi_get_string_utf8_length(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v).As()->Utf8Length(); -} - -int napi_get_string_utf8(napi_env e, napi_value v, char* buf, int bufsize) { - NAPI_PREAMBLE(e); - return v8impl::V8LocalValueFromJsValue(v).As() - ->WriteUtf8( - buf, - bufsize, - 0, - v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8); -} - -napi_value napi_coerce_to_object(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8impl::V8LocalValueFromJsValue(v)->ToObject( - v8impl::V8IsolateFromJsEnv(e))); -} - -napi_value napi_coerce_to_string(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - v8impl::V8LocalValueFromJsValue(v)->ToString( - v8impl::V8IsolateFromJsEnv(e))); -} - -void 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())); - } -} - -void* napi_unwrap(napi_env e, napi_value jsObject) { - NAPI_PREAMBLE(e); - return v8impl::ObjectWrapWrapper::Unwrap(jsObject); -} - -napi_persistent napi_create_persistent(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Persistent *thePersistent = - new v8::Persistent( - isolate, v8impl::V8LocalValueFromJsValue(v)); - return v8impl::JsPersistentFromV8PersistentValue(thePersistent); -} - -void napi_release_persistent(napi_env e, napi_persistent p) { - NAPI_PREAMBLE(e); - v8::Persistent *thePersistent = - v8impl::V8PersistentValueFromJsPersistentValue(p); - thePersistent->Reset(); - delete thePersistent; -} - -napi_value napi_get_persistent_value(napi_env e, napi_persistent p) { - NAPI_PREAMBLE(e); - 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); -} - -napi_weakref napi_create_weakref(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Persistent *thePersistent = - new v8::Persistent( - isolate, v8impl::V8LocalValueFromJsValue(v)); - thePersistent->SetWeak(static_cast(nullptr), v8impl::WeakRefCallback, - v8::WeakCallbackType::kParameter); - // need to mark independent? - return v8impl::JsWeakRefFromV8PersistentValue(thePersistent); -} - -bool napi_get_weakref_value(napi_env e, napi_weakref w, napi_value* pv) { - NAPI_PREAMBLE(e); - 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; - } - *pv = v8impl::JsValueFromV8LocalValue(v); - return true; -} - -void napi_release_weakref(napi_env e, napi_weakref w) { - NAPI_PREAMBLE(e); - v8::Persistent *thePersistent = - v8impl::V8PersistentValueFromJsWeakRefValue(w); - thePersistent->Reset(); - delete thePersistent; -} - -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_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local v8cons = v8impl::V8LocalFunctionFromJsValue(cons); - 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); -} - -bool napi_instanceof(napi_env e, napi_value obj, napi_value cons) { - bool returnValue = false; - - v8::Local v8Obj = v8impl::V8LocalValueFromJsValue(obj); - v8::Local v8Cons = v8impl::V8LocalValueFromJsValue(cons); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - - if (!v8Cons->IsFunction()) { - napi_throw_type_error(e, "constructor must be a function"); - - // Error handling needs to be done here - return false; - } - - v8Cons = - v8Cons->ToObject()->Get(v8::String::NewFromUtf8(isolate, "prototype")); - - if (!v8Cons->IsObject()) { - napi_throw_type_error(e, "constructor prototype must be an object"); - - // Error handling needs to be done here - return false; - } - - if (!v8Obj->StrictEquals(v8Cons)) { - for (v8::Local originalObj = v8Obj; - !(v8Obj->IsNull() || v8Obj->IsUndefined()); - v8Obj = v8Obj->ToObject()->GetPrototype()) { - if (v8Obj->StrictEquals(v8Cons)) { - returnValue = - !(originalObj->IsNumber() || - originalObj->IsBoolean() || - originalObj->IsString()); - break; - } - } - } - - return returnValue; -} - -napi_value napi_make_external(napi_env e, napi_value v) { - - // v8impl::TryCatch doesn't make sense here since we're not calling into the - // engine at all. - return v; -} - -napi_value napi_make_callback(napi_env e, napi_value recv, - napi_value func, int argc, napi_value* argv) { - NAPI_PREAMBLE(e); - v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); - v8::Local v8recv = - v8impl::V8LocalValueFromJsValue(recv).As(); - v8::Local v8func = - v8impl::V8LocalValueFromJsValue(func).As(); - std::vector> args(argc); - for (int i = 0; i < argc; i++) { - args[i] = v8impl::V8LocalValueFromJsValue(argv[i]); - } - - v8::Handle result = - node::MakeCallback(isolate, v8recv, v8func, argc, args.data()); - return v8impl::JsValueFromV8LocalValue(result); -} - -// Methods to support catching exceptions -bool napi_is_exception_pending(napi_env) { - return !v8impl::TryCatch::lastException().IsEmpty(); -} - -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; - if (v8impl::TryCatch::lastException().IsEmpty()) { - returnValue = napi_get_undefined_(e); - } else { - returnValue = v8impl::JsValueFromV8LocalValue( - v8impl::TryCatch::lastException().Get(v8impl::V8IsolateFromJsEnv(e))); - v8impl::TryCatch::lastException().Reset(); - } - return returnValue; -} - -napi_value napi_buffer_new(napi_env e, char* data, uint32_t size) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - node::Buffer::New( - v8impl::V8IsolateFromJsEnv(e), data, size).ToLocalChecked()); -} - -napi_value napi_buffer_copy(napi_env e, const char* data, uint32_t size) { - NAPI_PREAMBLE(e); - return v8impl::JsValueFromV8LocalValue( - node::Buffer::Copy( - v8impl::V8IsolateFromJsEnv(e), data, size).ToLocalChecked()); -} - -bool napi_buffer_has_instance(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(v)); -} - -char* napi_buffer_data(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return node::Buffer::Data( - v8impl::V8LocalValueFromJsValue(v).As()); -} - -size_t napi_buffer_length(napi_env e, napi_value v) { - NAPI_PREAMBLE(e); - return node::Buffer::Length(v8impl::V8LocalValueFromJsValue(v)); -} diff --git a/src/node_jsvmapi.h b/src/node_jsvmapi.h deleted file mode 100644 index c4ae258235..0000000000 --- a/src/node_jsvmapi.h +++ /dev/null @@ -1,354 +0,0 @@ -/******************************************************************************* - * Experimental prototype for demonstrating VM agnostic and ABI stable API - * for native modules to use instead of using Nan and V8 APIs directly. - * - * This is a rough proof of concept not intended for real world usage. - * It is currently far from a sufficiently completed work. - * - * - The API is not complete nor agreed upon. - * - The API is not yet analyzed for performance. - * - Performance is expected to suffer with the usage of opaque types currently - * 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 only basic - * error handling or fault tolerance. - * - ******************************************************************************/ -#ifndef SRC_NODE_JSVMAPI_H_ -#define SRC_NODE_JSVMAPI_H_ - -#include -#include "node_jsvmapi_types.h" - -#ifndef NODE_EXTERN -# ifdef _WIN32 -# ifndef BUILDING_NODE_EXTENSION -# define NODE_EXTERN __declspec(dllexport) -# else -# define NODE_EXTERN __declspec(dllimport) -# endif -# else -# define NODE_EXTERN /* nothing */ -# endif -#endif - -namespace node { -NODE_EXTERN typedef void (*addon_abi_register_func)( - napi_env env, - napi_value exports, - napi_value module, - void* priv); -} // namespace node - -struct napi_module_struct { - int nm_version; - unsigned int nm_flags; - void* nm_dso_handle; - const char* nm_filename; - node::addon_abi_register_func nm_register_func; - void* nm_context_register_func; - const char* nm_modname; - void* nm_priv; - void* nm_link; -}; - -NODE_EXTERN void napi_module_register(void* mod); - -#ifndef NODE_MODULE_EXPORT -# ifdef _WIN32 -# define NODE_MODULE_EXPORT __declspec(dllexport) -# else -# define NODE_MODULE_EXPORT __attribute__((visibility("default"))) -# endif -#endif - -#if defined(_MSC_VER) -#pragma section(".CRT$XCU", read) -#define NODE_ABI_CTOR(fn) \ - static void __cdecl fn(void); \ - __declspec(dllexport, allocate(".CRT$XCU")) \ - void (__cdecl*fn ## _)(void) = fn; \ - static void __cdecl fn(void) -#else -#define NODE_ABI_CTOR(fn) \ - static void fn(void) __attribute__((constructor)); \ - static void fn(void) -#endif - -#define NODE_MODULE_ABI_X(modname, regfunc, priv, flags) \ - extern "C" { \ - static napi_module_struct _module = \ - { \ - -1, \ - flags, \ - NULL, \ - __FILE__, \ - (node::addon_abi_register_func)regfunc, \ - NULL, \ - #modname, \ - priv, \ - NULL \ - }; \ - NODE_ABI_CTOR(_register_ ## modname) { \ - napi_module_register(&_module); \ - } \ - } - -#define NODE_MODULE_ABI(modname, regfunc) \ - NODE_MODULE_ABI_X(modname, regfunc, NULL, 0) - - -// TODO(ianhall): We're using C linkage for the API but we're also using the -// bool type in these exports. Is that safe and stable? -extern "C" { - -NODE_EXTERN const napi_extended_error_info* napi_get_last_error_info(); - -// Environment -NODE_EXTERN napi_status napi_get_current_env(napi_env* e); - -// Getters for defined singletons -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(napi_env e, napi_value* result); - -// Methods to create Primitive types/Objects -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_status napi_create_number(napi_env e, double val, napi_value* result); -NODE_EXTERN napi_status napi_create_string_utf8(napi_env e, const char* s, - int length, napi_value* result); -NODE_EXTERN napi_status napi_create_string_utf16(napi_env e, const char16_t* s, - int length, napi_value* result); -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_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); -NODE_EXTERN napi_status napi_create_range_error(napi_env e, napi_value msg, napi_value* result); - -// Methods to get the the native napi_value from Primitive type -NODE_EXTERN napi_status napi_get_type_of_value(napi_env e, napi_value vv, napi_valuetype* result); -NODE_EXTERN napi_status napi_get_value_double(napi_env e, napi_value v, double* 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); - -// Gets the number of CHARACTERS in the string. -NODE_EXTERN napi_status napi_get_value_string_length(napi_env e, napi_value v, int* result); - -// Gets the number of BYTES in the UTF-8 encoded representation of the string. -NODE_EXTERN napi_status napi_get_value_string_utf8_length(napi_env e, napi_value v, - int* result); - -// Copies UTF-8 encoded bytes from a string into a buffer. -NODE_EXTERN napi_status napi_get_value_string_utf8(napi_env e, napi_value v, - char* buf, int bufsize, int* result); - -// Gets the number of 2-byte code units in the UTF-16 encoded representation of the string. -NODE_EXTERN napi_status napi_get_value_string_utf16_length(napi_env e, napi_value v, - int* result); - -// Copies UTF-16 encoded bytes from a string into a buffer. -NODE_EXTERN napi_status napi_get_value_string_utf16(napi_env e, napi_value v, - char16_t* buf, int bufsize, int* result); - -// Methods to coerce values -// These APIs may execute user script -NODE_EXTERN napi_status napi_coerce_to_bool(napi_env e, napi_value v, napi_value* result); -NODE_EXTERN napi_status napi_coerce_to_number(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_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_status napi_get_propertynames(napi_env e, napi_value object, - napi_value* result); -NODE_EXTERN napi_status napi_set_property(napi_env e, napi_value object, - napi_propertyname name, napi_value v); -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_status napi_set_element(napi_env e, napi_value object, - uint32_t i, napi_value v); -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_status napi_define_properties(napi_env e, - napi_value object, - int property_count, - const napi_property_descriptor* properties); - -// Methods to work with Arrays -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 napi_status napi_strict_equals(napi_env e, napi_value lhs, - napi_value rhs, bool* result); - -// Methods to work with Functions -NODE_EXTERN napi_status napi_set_function_name(napi_env e, napi_value func, - napi_propertyname napi_value); -NODE_EXTERN napi_status napi_call_function(napi_env e, - napi_value recv, - 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); - -// Napi version of node::MakeCallback(...) -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 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, int bufferlength); -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_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); - -NODE_EXTERN napi_status napi_define_class(napi_env e, - const char* utf8name, - napi_callback constructor, - void* data, - int property_count, - const napi_property_descriptor* properties, - napi_value* result); - -// Methods to work with external data objects -NODE_EXTERN napi_status napi_wrap(napi_env e, - napi_value jsObject, - void* nativeObj, - napi_finalize finalize_cb, - napi_ref* result); -NODE_EXTERN napi_status napi_unwrap(napi_env e, napi_value jsObject, void** result); - -NODE_EXTERN napi_status napi_create_external(napi_env e, - void* data, - napi_finalize finalize_cb, - napi_value* result); -NODE_EXTERN napi_status napi_get_value_external(napi_env e, napi_value v, void** result); - -// Methods to control object lifespan - -// Set initial_refcount to 0 for a weak reference, >0 for a strong reference. -NODE_EXTERN napi_status napi_create_reference(napi_env e, - napi_value value, - int initial_refcount, - napi_ref* result); - -// Deletes a reference. The referenced value is released, and may be GC'd unless there -// are other references to it. -NODE_EXTERN napi_status napi_delete_reference(napi_env e, napi_ref ref); - -// Increments the reference count, optionally returning the resulting count. After this call the -// reference will be a strong reference because its refcount is >0, and the referenced object is -// effectively "pinned". Calling this when the refcount is 0 and the object is unavailable -// results in an error. -NODE_EXTERN napi_status napi_reference_addref(napi_env e, napi_ref ref, int* result); - -// Decrements the reference count, optionally returning the resulting count. If the result is -// 0 the reference is now weak and the object may be GC'd at any time if there are no other -// references. Calling this when the refcount is already 0 results in an error. -NODE_EXTERN napi_status napi_reference_release(napi_env e, napi_ref ref, int* result); - -// Attempts to get a referenced value. If the reference is weak, the value might no longer be -// available, in that case the call is still successful but the result is NULL. -NODE_EXTERN napi_status napi_get_reference_value(napi_env e, napi_ref ref, napi_value* result); - -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 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); -NODE_EXTERN napi_status napi_throw_range_error(napi_env e, const char* msg); - -// Methods to support catching exceptions -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_create_buffer(napi_env e, - size_t size, - char** data, - napi_value* result); -NODE_EXTERN napi_status napi_create_external_buffer(napi_env e, - size_t size, - char* data, - napi_finalize finalize_cb, - napi_value* result); -NODE_EXTERN napi_status napi_create_buffer_copy(napi_env e, - const char* data, - size_t size, - napi_value* result); -NODE_EXTERN napi_status napi_is_buffer(napi_env e, napi_value v, bool* result); -NODE_EXTERN napi_status napi_get_buffer_info(napi_env e, napi_value v, char** data, size_t* length); - -// Methods to work with array buffers and typed arrays -NODE_EXTERN napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result); -NODE_EXTERN napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, void** data, - napi_value* result); -NODE_EXTERN napi_status napi_create_external_arraybuffer(napi_env env, void* external_data, - size_t byte_length, - napi_finalize finalize_cb, - napi_value* result); -NODE_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, - void** data, size_t* byte_length); -NODE_EXTERN napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result); -NODE_EXTERN napi_status napi_create_typedarray(napi_env env, napi_typedarray_type type, - size_t length, napi_value arraybuffer, - size_t byte_offset, napi_value* result); -NODE_EXTERN napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray, - napi_typedarray_type* type, size_t* length, - void** data, napi_value* arraybuffer, - size_t* byte_offset); -} // extern "C" - -#endif // SRC_NODE_JSVMAPI_H__ diff --git a/src/node_jsvmapi_internal.h b/src/node_jsvmapi_internal.h deleted file mode 100644 index 200b007877..0000000000 --- a/src/node_jsvmapi_internal.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef SRC_NODE_JSVMAPI_INTERNAL_H_ -#define SRC_NODE_JSVMAPI_INTERNAL_H_ - -#include "node_jsvmapi.h" -#include "node.h" - -namespace v8impl { - napi_env JsEnvFromV8Isolate(v8::Isolate* isolate); - v8::Isolate* V8IsolateFromJsEnv(napi_env e); - - napi_value JsValueFromV8LocalValue(v8::Local local); - v8::Local V8LocalValueFromJsValue(napi_value v); -} - - -#endif // SRC_NODE_JSVMAPI_INTERNAL_H_ diff --git a/src/node_jsvmapi_types.h b/src/node_jsvmapi_types.h deleted file mode 100644 index ce0e5695ce..0000000000 --- a/src/node_jsvmapi_types.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef SRC_NODE_JSVMAPI_TYPES_H_ -#define SRC_NODE_JSVMAPI_TYPES_H_ - -#include -#include - -// JSVM API types are all opaque pointers for ABI stability -// typedef undefined structs instead of void* for compile time type safety -typedef struct napi_env__ *napi_env; -typedef struct napi_value__ *napi_value; -typedef struct napi_ref__ *napi_ref; -typedef struct napi_handle_scope__ *napi_handle_scope; -typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; -typedef struct napi_propertyname__ *napi_propertyname; -typedef struct napi_callback_info__ *napi_callback_info; - -typedef void (*napi_callback)(napi_env, napi_callback_info); -typedef void (*napi_finalize)(void* v); - -enum napi_property_attributes { - napi_default = 0, - napi_read_only = 1 << 0, - napi_dont_enum = 1 << 1, - napi_dont_delete = 1 << 2, - - // Used with napi_define_class to distinguish static properties - // from instance properties. Ignored by napi_define_properties. - napi_static_property = 1 << 10, -}; - -struct napi_property_descriptor { - const char* utf8name; - - napi_callback method; - napi_callback getter; - napi_callback setter; - napi_value value; - - napi_property_attributes attributes; - void* data; -}; - -enum napi_valuetype { - // ES6 types (corresponds to typeof) - napi_undefined, - napi_null, - napi_boolean, - napi_number, - napi_string, - napi_symbol, - napi_object, - napi_function, - napi_external, -}; - -enum napi_typedarray_type { - napi_int8, - napi_uint8, - napi_uint8_clamped, - napi_int16, - napi_uint16, - napi_int32, - napi_uint32, - napi_float32, - napi_float64, -}; - -enum napi_status { - napi_ok, - napi_invalid_arg, - napi_object_expected, - napi_string_expected, - napi_function_expected, - napi_number_expected, - napi_boolean_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_status error_code; -}; - -#endif // SRC_NODE_JSVMAPI_TYPES_H_ diff --git a/test/addons-abi/1_hello_world/binding.cc b/test/addons-abi/1_hello_world/binding.cc deleted file mode 100644 index d4615d3d91..0000000000 --- a/test/addons-abi/1_hello_world/binding.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include - -void Method(napi_env env, napi_callback_info info) { - napi_status status; - napi_value world; - status = napi_create_string_utf8(env, "world", -1, &world); - if (status != napi_ok) return; - status = napi_set_return_value(env, info, world); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - napi_property_descriptor desc = { "hello", Method }; - status = napi_define_properties(env, exports, 1, &desc); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) - diff --git a/test/addons-abi/1_hello_world/test.js b/test/addons-abi/1_hello_world/test.js deleted file mode 100644 index d349dcfbd9..0000000000 --- a/test/addons-abi/1_hello_world/test.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -assert.equal(addon.hello(), 'world'); diff --git a/test/addons-abi/2_function_arguments/binding.cc b/test/addons-abi/2_function_arguments/binding.cc deleted file mode 100644 index d4afc59a71..0000000000 --- a/test/addons-abi/2_function_arguments/binding.cc +++ /dev/null @@ -1,56 +0,0 @@ -#include - -void Add(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 2) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - napi_valuetype valuetype0; - status = napi_get_type_of_value(env, args[0], &valuetype0); - if (status != napi_ok) return; - - napi_valuetype valuetype1; - status = napi_get_type_of_value(env, args[1], &valuetype1); - if (status != napi_ok) return; - - if (valuetype0 != napi_number || valuetype1 != napi_number) { - napi_throw_type_error(env, "Wrong arguments"); - return; - } - - double value0; - status = napi_get_value_double(env, args[0], &value0); - if (status != napi_ok) return; - - double value1; - status = napi_get_value_double(env, args[1], &value1); - if (status != napi_ok) return; - - napi_value sum; - status = napi_create_number(env, value0 + value1, &sum); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, sum); - if (status != napi_ok) return; -} - - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - napi_property_descriptor addDescriptor = { "add", Add }; - status = napi_define_properties(env, exports, 1, &addDescriptor); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/2_function_arguments/test.js b/test/addons-abi/2_function_arguments/test.js deleted file mode 100644 index dcdb365bcc..0000000000 --- a/test/addons-abi/2_function_arguments/test.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -assert.equal(addon.add(3, 5), 8); diff --git a/test/addons-abi/3_callbacks/binding.cc b/test/addons-abi/3_callbacks/binding.cc deleted file mode 100644 index 940193dd85..0000000000 --- a/test/addons-abi/3_callbacks/binding.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include - -void RunCallback(napi_env env, const napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_value cb = args[0]; - - napi_value argv[1]; - status = napi_create_string_utf8(env, "hello world", -1, argv); - if (status != napi_ok) return; - - napi_value global; - status = napi_get_global(env, &global); - if (status != napi_ok) return; - - status = napi_call_function(env, global, cb, 1, argv, nullptr); - if (status != napi_ok) return; -} - -void RunCallbackWithRecv(napi_env env, const napi_callback_info info) { - napi_status status; - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - napi_value cb = args[0]; - napi_value recv = args[1]; - - status = napi_call_function(env, recv, cb, 0, nullptr, nullptr); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - napi_property_descriptor desc[2] = { - { "RunCallback", RunCallback }, - { "RunCallbackWithRecv", RunCallbackWithRecv } - }; - status = napi_define_properties(env, exports, 2, desc); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/3_callbacks/test.js b/test/addons-abi/3_callbacks/test.js deleted file mode 100644 index 4e8f085413..0000000000 --- a/test/addons-abi/3_callbacks/test.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -addon.RunCallback(function(msg) { - assert.equal(msg, 'hello world'); -}); - -var global = ( function() { return this; } ).apply(); - -function testRecv(desiredRecv) { - addon.RunCallbackWithRecv(function() { - assert.equal(this, - ( desiredRecv === undefined || desiredRecv === null ) ? global : desiredRecv ); - }, desiredRecv); -} - -testRecv(undefined); -testRecv(null); -testRecv(5); -testRecv(true); -testRecv("Hello"); -testRecv([]); -testRecv({}); diff --git a/test/addons-abi/4_object_factory/binding.cc b/test/addons-abi/4_object_factory/binding.cc deleted file mode 100644 index c91c982f4e..0000000000 --- a/test/addons-abi/4_object_factory/binding.cc +++ /dev/null @@ -1,32 +0,0 @@ -#include - -void CreateObject(napi_env env, const napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_value obj; - status = napi_create_object(env, &obj); - if (status != napi_ok) return; - - napi_propertyname msgprop; - status = napi_property_name(env, "msg", &msgprop); - if (status != napi_ok) return; - - status = napi_set_property(env, obj, msgprop, args[0]); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, obj); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - napi_property_descriptor desc = { "exports", CreateObject }; - status = napi_define_properties(env, module, 1, &desc); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/4_object_factory/test.js b/test/addons-abi/4_object_factory/test.js deleted file mode 100644 index 5581dd12ce..0000000000 --- a/test/addons-abi/4_object_factory/test.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -var obj1 = addon('hello'); -var obj2 = addon('world'); -assert.equal(obj1.msg + ' ' + obj2.msg, 'hello world'); diff --git a/test/addons-abi/5_function_factory/binding.cc b/test/addons-abi/5_function_factory/binding.cc deleted file mode 100644 index 322cc8487d..0000000000 --- a/test/addons-abi/5_function_factory/binding.cc +++ /dev/null @@ -1,41 +0,0 @@ -#include - -void MyFunction(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value str; - status = napi_create_string_utf8(env, "hello world", -1, &str); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, str); - if (status != napi_ok) return; -} - -void CreateFunction(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value fn; - status = napi_create_function(env, MyFunction, nullptr, &fn); - if (status != napi_ok) return; - - napi_propertyname name; - status = napi_property_name(env, "theFunction", &name); - if (status != napi_ok) return; - - // omit this to make it anonymous - status = napi_set_function_name(env, fn, name); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, fn); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - napi_property_descriptor desc = { "exports", CreateFunction }; - status = napi_define_properties(env, module, 1, &desc); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) - diff --git a/test/addons-abi/5_function_factory/binding.gyp b/test/addons-abi/5_function_factory/binding.gyp deleted file mode 100644 index 2656c2651a..0000000000 --- a/test/addons-abi/5_function_factory/binding.gyp +++ /dev/null @@ -1,8 +0,0 @@ -{ - "targets": [ - { - "target_name": "binding", - "sources": [ "binding.cc" ] - } - ] -} diff --git a/test/addons-abi/5_function_factory/test.js b/test/addons-abi/5_function_factory/test.js deleted file mode 100644 index 954bc0ce02..0000000000 --- a/test/addons-abi/5_function_factory/test.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -var fn = addon(); -assert.equal(fn(), 'hello world'); // 'hello world' diff --git a/test/addons-abi/6_object_wrap/binding.cc b/test/addons-abi/6_object_wrap/binding.cc deleted file mode 100644 index 8e435da817..0000000000 --- a/test/addons-abi/6_object_wrap/binding.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include "myobject.h" - -void Init(napi_env env, napi_value exports, napi_value module) { - MyObject::Init(env, exports); -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/6_object_wrap/myobject.cc b/test/addons-abi/6_object_wrap/myobject.cc deleted file mode 100644 index c97b79ed77..0000000000 --- a/test/addons-abi/6_object_wrap/myobject.cc +++ /dev/null @@ -1,228 +0,0 @@ -#include "myobject.h" - -napi_ref MyObject::constructor; - -MyObject::MyObject(double value) : value_(value), env_(nullptr), wrapper_(nullptr) { -} - -MyObject::~MyObject() { - napi_delete_reference(env_, wrapper_); -} - -void MyObject::Destructor(void* nativeObject) { - reinterpret_cast(nativeObject)->~MyObject(); -} - -void MyObject::Init(napi_env env, napi_value exports) { - napi_status status; - napi_property_descriptor properties[] = { - { "value", nullptr, GetValue, SetValue }, - { "plusOne", PlusOne }, - { "multiply", Multiply }, - { "isInstance", IsInstance }, - }; - - napi_value cons; - status = napi_define_class( - env, "MyObject", New, nullptr, sizeof (properties) / sizeof (*properties), properties, &cons); - if (status != napi_ok) return; - - status = napi_create_reference(env, cons, 1, &constructor); - if (status != napi_ok) return; - - napi_propertyname name; - status = napi_property_name(env, "MyObject", &name); - if (status != napi_ok) return; - - status = napi_set_property(env, exports, name, cons); - if (status != napi_ok) return; -} - -void MyObject::New(napi_env env, napi_callback_info info) { - napi_status status; - - bool is_constructor; - status = napi_is_construct_call(env, info, &is_constructor); - if (status != napi_ok) return; - - if (is_constructor) { - // Invoked as constructor: `new MyObject(...)` - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - double value = 0; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_undefined) { - status = napi_get_value_double(env, args[0], &value); - if (status != napi_ok) return; - } - - MyObject* obj = new MyObject(value); - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - obj->env_ = env; - status = napi_wrap(env, jsthis, reinterpret_cast(obj), - MyObject::Destructor, &obj->wrapper_); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, jsthis); - if (status != napi_ok) return; - } else { - // Invoked as plain function `MyObject(...)`, turn into construct call. - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - const int argc = 1; - napi_value argv[argc] = { args[0] }; - - napi_value cons; - status = napi_get_reference_value(env, constructor, &cons); - if (status != napi_ok) return; - - napi_value instance; - status = napi_new_instance(env, cons, argc, argv, &instance); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, instance); - if (status != napi_ok) return; - } -} - -void MyObject::GetValue(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - MyObject* obj; - status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); - if (status != napi_ok) return; - - napi_value num; - status = napi_create_number(env, obj->value_, &num); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, num); - if (status != napi_ok) return; -} - -void MyObject::SetValue(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value value; - status = napi_get_cb_args(env, info, &value, 1); - if (status != napi_ok) return; - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - MyObject* obj; - status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); - if (status != napi_ok) return; - - status = napi_get_value_double(env, value, &obj->value_); - if (status != napi_ok) return; -} - -void MyObject::PlusOne(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - MyObject* obj; - status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); - if (status != napi_ok) return; - - obj->value_ += 1; - - napi_value num; - status = napi_create_number(env, obj->value_, &num); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, num); - if (status != napi_ok) return; -} - -void MyObject::Multiply(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - double multiple = 1; - if (valuetype != napi_undefined) { - status = napi_get_value_double(env, args[0], &multiple); - if (status != napi_ok) return; - } - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - MyObject* obj; - status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); - if (status != napi_ok) return; - - napi_value cons; - status = napi_get_reference_value(env, constructor, &cons); - if (status != napi_ok) return; - - const int argc = 1; - napi_value argv[argc]; - status = napi_create_number(env, obj->value_ * multiple, argv); - if (status != napi_ok) return; - - napi_value instance; - status = napi_new_instance(env, cons, argc, argv, &instance); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, instance); - if (status != napi_ok) return; -} - -void MyObject::IsInstance(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, jsthis, &valuetype); - if (status != napi_ok) return; - - bool result = false; - if (valuetype == napi_object) { - napi_value constructorFunction; - status = napi_get_reference_value(env, constructor, &constructorFunction); - if (status != napi_ok) return; - - status = napi_instanceof(env, jsthis, constructorFunction, &result); - if (status != napi_ok) return; - } - - napi_value resultValue; - status = napi_create_boolean(env, result, &resultValue); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, resultValue); - if (status != napi_ok) return; -} diff --git a/test/addons-abi/6_object_wrap/myobject.h b/test/addons-abi/6_object_wrap/myobject.h deleted file mode 100644 index b085307700..0000000000 --- a/test/addons-abi/6_object_wrap/myobject.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEST_ADDONS_ABI_6_OBJECT_WRAP_MYOBJECT_H_ -#define TEST_ADDONS_ABI_6_OBJECT_WRAP_MYOBJECT_H_ - -#include - -class MyObject { - public: - static void Init(napi_env env, napi_value exports); - static void Destructor(void* nativeObject); - - private: - explicit MyObject(double value_ = 0); - ~MyObject(); - - static void New(napi_env env, napi_callback_info info); - static void GetValue(napi_env env, napi_callback_info info); - static void SetValue(napi_env env, napi_callback_info info); - static void PlusOne(napi_env env, napi_callback_info info); - static void Multiply(napi_env env, napi_callback_info info); - static void IsInstance(napi_env env, napi_callback_info info); - static napi_ref constructor; - double value_; - napi_env env_; - napi_ref wrapper_; -}; - -#endif // TEST_ADDONS_ABI_6_OBJECT_WRAP_MYOBJECT_H_ diff --git a/test/addons-abi/6_object_wrap/test.js b/test/addons-abi/6_object_wrap/test.js deleted file mode 100644 index 1e519d8f06..0000000000 --- a/test/addons-abi/6_object_wrap/test.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -var obj = new addon.MyObject(9); -assert.ok(obj instanceof addon.MyObject); -assert.equal(obj.value, 9); -obj.value = 10; -assert.equal(obj.value, 10); -assert.equal(obj.plusOne(), 11); -assert.equal(obj.plusOne(), 12); -assert.equal(obj.plusOne(), 13); - -assert.equal(obj.multiply().value, 13); -assert.equal(obj.multiply(10).value, 130); - -var newobj = obj.multiply(-1); -assert.equal(newobj.value, -13); -assert(obj !== newobj); - -assert.ok(obj.isInstance()); diff --git a/test/addons-abi/7_factory_wrap/binding.cc b/test/addons-abi/7_factory_wrap/binding.cc deleted file mode 100644 index 6042eaa829..0000000000 --- a/test/addons-abi/7_factory_wrap/binding.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "myobject.h" - -void CreateObject(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_value instance; - status = MyObject::NewInstance(env, args[0], &instance); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, instance); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - status = MyObject::Init(env); - if (status != napi_ok) return; - - napi_property_descriptor desc = { "exports", CreateObject }; - status = napi_define_properties(env, module, 1, &desc); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/7_factory_wrap/myobject.cc b/test/addons-abi/7_factory_wrap/myobject.cc deleted file mode 100644 index 0202737ab6..0000000000 --- a/test/addons-abi/7_factory_wrap/myobject.cc +++ /dev/null @@ -1,101 +0,0 @@ -#include "myobject.h" - -MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} - -MyObject::~MyObject() { - napi_delete_reference(env_, wrapper_); -} - -void MyObject::Destructor(void* nativeObject) { - reinterpret_cast(nativeObject)->~MyObject(); -} - -napi_ref MyObject::constructor; - -napi_status MyObject::Init(napi_env env) { - napi_status status; - napi_property_descriptor properties[] = { - { "plusOne", PlusOne }, - }; - - napi_value cons; - status = napi_define_class(env, "MyObject", New, nullptr, 1, properties, &cons); - if (status != napi_ok) return status; - - status = napi_create_reference(env, cons, 1, &constructor); - if (status != napi_ok) return status; - - return napi_ok; -} - -void MyObject::New(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - MyObject* obj = new MyObject(); - - if (valuetype == napi_undefined) { - obj->counter_ = 0; - } - else { - status = napi_get_value_double(env, args[0], &obj->counter_); - if (status != napi_ok) return; - } - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - obj->env_ = env; - status = napi_wrap(env, jsthis, reinterpret_cast(obj), - MyObject::Destructor, &obj->wrapper_); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, jsthis); - if (status != napi_ok) return; -} - - -napi_status MyObject::NewInstance(napi_env env, napi_value arg, napi_value* instance) { - napi_status status; - - const int argc = 1; - napi_value argv[argc] = { arg }; - - napi_value cons; - status = napi_get_reference_value(env, constructor, &cons); - if (status != napi_ok) return status; - - status = napi_new_instance(env, cons, argc, argv, instance); - if (status != napi_ok) return status; - - return napi_ok; -} - -void MyObject::PlusOne(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - MyObject* obj; - status = napi_unwrap(env, jsthis, reinterpret_cast(&obj)); - if (status != napi_ok) return; - - obj->counter_ += 1; - - napi_value num; - status = napi_create_number(env, obj->counter_, &num); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, num); - if (status != napi_ok) return; -} diff --git a/test/addons-abi/7_factory_wrap/myobject.h b/test/addons-abi/7_factory_wrap/myobject.h deleted file mode 100644 index 7867948d9f..0000000000 --- a/test/addons-abi/7_factory_wrap/myobject.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEST_ADDONS_ABI_7_FACTORY_WRAP_MYOBJECT_H_ -#define TEST_ADDONS_ABI_7_FACTORY_WRAP_MYOBJECT_H_ - -#include - -class MyObject { - public: - static napi_status Init(napi_env env); - static void Destructor(void* nativeObject); - static napi_status NewInstance(napi_env env, napi_value arg, napi_value* instance); - - private: - MyObject(); - ~MyObject(); - - static napi_ref constructor; - static void New(napi_env env, napi_callback_info info); - static void PlusOne(napi_env env, napi_callback_info info); - double counter_; - napi_env env_; - napi_ref wrapper_; -}; - -#endif // TEST_ADDONS_ABI_7_FACTORY_WRAP_MYOBJECT_H_ diff --git a/test/addons-abi/7_factory_wrap/test.js b/test/addons-abi/7_factory_wrap/test.js deleted file mode 100644 index e69b03ce9a..0000000000 --- a/test/addons-abi/7_factory_wrap/test.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var createObject = require(`./build/${common.buildType}/binding`); - -var obj = createObject(10); -assert.equal(obj.plusOne(), 11); -assert.equal(obj.plusOne(), 12); -assert.equal(obj.plusOne(), 13); - -var obj2 = createObject(20); -assert.equal(obj2.plusOne(), 21); -assert.equal(obj2.plusOne(), 22); -assert.equal(obj2.plusOne(), 23); diff --git a/test/addons-abi/8_passing_wrapped/binding.cc b/test/addons-abi/8_passing_wrapped/binding.cc deleted file mode 100644 index b727079b81..0000000000 --- a/test/addons-abi/8_passing_wrapped/binding.cc +++ /dev/null @@ -1,54 +0,0 @@ -#include "myobject.h" - -void CreateObject(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_value instance; - status = MyObject::NewInstance(env, args[0], &instance); - - status = napi_set_return_value(env, info, instance); - if (status != napi_ok) return; -} - -void Add(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - MyObject* obj1; - status = napi_unwrap(env, args[0], reinterpret_cast(&obj1)); - if (status != napi_ok) return; - - MyObject* obj2; - status = napi_unwrap(env, args[1], reinterpret_cast(&obj2)); - if (status != napi_ok) return; - - napi_value sum; - status = napi_create_number(env, obj1->Val() + obj2->Val(), &sum); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, sum); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - MyObject::Init(env); - - napi_property_descriptor desc[] = { - { "createObject", CreateObject }, - { "add", Add }, - }; - status = napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc); - if (status != napi_ok) return; -} - - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/8_passing_wrapped/myobject.cc b/test/addons-abi/8_passing_wrapped/myobject.cc deleted file mode 100644 index 06a69b7e15..0000000000 --- a/test/addons-abi/8_passing_wrapped/myobject.cc +++ /dev/null @@ -1,77 +0,0 @@ -#include "myobject.h" -#include - -MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} - -MyObject::~MyObject() { - napi_delete_reference(env_, wrapper_); -} - -void MyObject::Destructor(void* nativeObject) { - reinterpret_cast(nativeObject)->~MyObject(); -} - -napi_ref MyObject::constructor; - -napi_status MyObject::Init(napi_env env) { - napi_status status; - - napi_value cons; - status = napi_define_class(env, "MyObject", New, nullptr, 0, nullptr, &cons); - if (status != napi_ok) return status; - - status = napi_create_reference(env, cons, 1, &constructor); - if (status != napi_ok) return status; - - return napi_ok; -} - -void MyObject::New(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - MyObject* obj = new MyObject(); - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype == napi_undefined) { - obj->val_ = 0; - } - else { - status = napi_get_value_double(env, args[0], &obj->val_); - if (status != napi_ok) return; - } - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - obj->env_ = env; - status = napi_wrap(env, jsthis, reinterpret_cast(obj), - MyObject::Destructor, &obj->wrapper_); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, jsthis); - if (status != napi_ok) return; -} - -napi_status MyObject::NewInstance(napi_env env, napi_value arg, napi_value* instance) { - napi_status status; - - const int argc = 1; - napi_value argv[argc] = { arg }; - - napi_value cons; - status = napi_get_reference_value(env, constructor, &cons); - if (status != napi_ok) return status; - - status = napi_new_instance(env, cons, argc, argv, instance); - if (status != napi_ok) return status; - - return napi_ok; -} diff --git a/test/addons-abi/8_passing_wrapped/myobject.h b/test/addons-abi/8_passing_wrapped/myobject.h deleted file mode 100644 index 98c0f52a2a..0000000000 --- a/test/addons-abi/8_passing_wrapped/myobject.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEST_ADDONS_ABI_8_PASSING_WRAPPED_MYOBJECT_H_ -#define TEST_ADDONS_ABI_8_PASSING_WRAPPED_MYOBJECT_H_ - -#include - -class MyObject { - public: - static napi_status Init(napi_env env); - static void Destructor(void* nativeObject); - static napi_status NewInstance(napi_env env, napi_value arg, napi_value* instance); - double Val() const { return val_; } - - private: - MyObject(); - ~MyObject(); - - static napi_ref constructor; - static void New(napi_env env, napi_callback_info info); - double val_; - napi_env env_; - napi_ref wrapper_; -}; - -#endif // TEST_ADDONS_ABI_8_PASSING_WRAPPED_MYOBJECT_H_ diff --git a/test/addons-abi/8_passing_wrapped/test.js b/test/addons-abi/8_passing_wrapped/test.js deleted file mode 100644 index ee42f50182..0000000000 --- a/test/addons-abi/8_passing_wrapped/test.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/binding`); - -var obj1 = addon.createObject(10); -var obj2 = addon.createObject(20); -var result = addon.add(obj1, obj2); -assert.equal(result, 30); diff --git a/test/addons-abi/test_array/test.js b/test/addons-abi/test_array/test.js deleted file mode 100644 index 7168786993..0000000000 --- a/test/addons-abi/test_array/test.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// Testing api calls for arrays -var test_array = require(`./build/${common.buildType}/test_array`); - -var array = [ - 1, - 9, - 48, - 13493, - 9459324, - { name: 'hello' }, - [ - 'world', - 'node', - 'abi' - ] -]; - -assert.equal(test_array.Test(array, array.length + 1), 'Index out of bound!'); - -try { - test_array.Test(array, -2); -} -catch (err) { - assert.equal(err.message, 'Invalid index. Expects a positive integer.'); -} - -array.forEach(function(element, index) { - assert.equal(test_array.Test(array, index), element); -}); - - -assert.deepStrictEqual(test_array.New(array), array); diff --git a/test/addons-abi/test_array/test_array.cc b/test/addons-abi/test_array/test_array.cc deleted file mode 100644 index 1bd2a7cce6..0000000000 --- a/test/addons-abi/test_array/test_array.cc +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include - -void Test(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 2) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - napi_valuetype valuetype0; - status = napi_get_type_of_value(env, args[0], &valuetype0); - if (status != napi_ok) return; - - if (valuetype0 != napi_object) { - napi_throw_type_error(env, - "Wrong type of argments. Expects an array as first argument."); - return; - } - - napi_valuetype valuetype1; - status = napi_get_type_of_value(env, args[1], &valuetype1); - if (status != napi_ok) return; - - if (valuetype1 != napi_number) { - napi_throw_type_error(env, - "Wrong type of argments. Expects an integer as second argument."); - return; - } - - napi_value array = args[0]; - int index; - status = napi_get_value_int32(env, args[1], &index); - if (status != napi_ok) return; - - bool isarray; - status = napi_is_array(env, array, &isarray); - if (status != napi_ok) return; - - if (isarray) { - uint32_t size; - status = napi_get_array_length(env, array, &size); - if (status != napi_ok) return; - - if (index >= static_cast(size)) { - napi_value str; - status = napi_create_string_utf8(env, "Index out of bound!", -1, &str); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, str); - if (status != napi_ok) return; - } - else if (index < 0) { - napi_throw_type_error(env, "Invalid index. Expects a positive integer."); - } - else { - napi_value ret; - status = napi_get_element(env, array, index, &ret); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, ret); - if (status != napi_ok) return; - } - } -} - -void New(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_object) { - napi_throw_type_error(env, - "Wrong type of argments. Expects an array as first argument."); - return; - } - - napi_value ret; - status = napi_create_array(env, &ret); - if (status != napi_ok) return; - - uint32_t length; - status = napi_get_array_length(env, args[0], &length); - if (status != napi_ok) return; - - for (uint32_t i = 0; i < length; i++) { - napi_value e; - status = napi_get_element(env, args[0], i, &e); - if (status != napi_ok) return; - - status = napi_set_element(env, ret, i, e); - if (status != napi_ok) return; - } - - status = napi_set_return_value(env, info, ret); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor descriptors[] = { - { "Test", Test }, - { "New", New }, - }; - - status = napi_define_properties( - env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_buffer/test.js b/test/addons-abi/test_buffer/test.js deleted file mode 100644 index a16875db29..0000000000 --- a/test/addons-abi/test_buffer/test.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; -// Flags: --expose-gc - -var common = require('../../common'); -var binding = require(`./build/${common.buildType}/test_buffer`); -var assert = require( "assert" ); - -assert( binding.newBuffer().toString() === binding.theText, - "buffer returned by newBuffer() has wrong contents" ); -assert( binding.newExternalBuffer().toString() === binding.theText, - "buffer returned by newExternalBuffer() has wrong contents" ); - -setImmediate(() => { - global.gc(); - - assert( binding.getDeleterCallCount() == 1, "deleter was not called" ); - assert( binding.copyBuffer().toString() === binding.theText, - "buffer returned by copyBuffer() has wrong contents" ); - - var buffer = binding.staticBuffer(); - assert( binding.bufferHasInstance( buffer ), "buffer type checking fails" ); - assert( binding.bufferInfo( buffer ), "buffer data is accurate" ); - - setImmediate(() => { - global.gc(); - - assert( binding.getDeleterCallCount() == 2, "deleter was not called" ); - }); -}); diff --git a/test/addons-abi/test_buffer/test_buffer.cc b/test/addons-abi/test_buffer/test_buffer.cc deleted file mode 100644 index 0ca455e6ae..0000000000 --- a/test/addons-abi/test_buffer/test_buffer.cc +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include - -#define JS_ASSERT(env, assertion, message) \ - if (!(assertion)) { \ - napi_throw_error((env), (std::string("assertion (" #assertion ") failed: ") + message).c_str()); \ - return; \ - } - -#define NAPI_CALL(env, theCall) \ - if (theCall != napi_ok) { \ - const char *errorMessage = napi_get_last_error_info()->error_message; \ - errorMessage = errorMessage ? errorMessage: "empty error message"; \ - napi_throw_error((env), errorMessage); \ - return; \ - } - -static const char theText[] = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; - -static int deleterCallCount = 0; -static void deleteTheText(void *data) { - delete (char *)data; - deleterCallCount++; -} - -static void noopDeleter(void *data) { - deleterCallCount++; -} - -NAPI_METHOD(newBuffer) { - napi_value theBuffer; - char *theCopy; - NAPI_CALL(env, napi_create_buffer(env, sizeof(theText), &theCopy, - &theBuffer)); - JS_ASSERT(env, theCopy, "Failed to copy static text for newBuffer"); - strcpy(theCopy, theText); - NAPI_CALL(env, napi_set_return_value(env, info, theBuffer)); -} - -NAPI_METHOD(newExternalBuffer) { - napi_value theBuffer; - char *theCopy = strdup(theText); - JS_ASSERT(env, theCopy, - "Failed to copy static text for newExternalBuffer"); - NAPI_CALL(env, napi_create_external_buffer(env, sizeof(theText), theCopy, - deleteTheText, &theBuffer)); - NAPI_CALL(env, napi_set_return_value(env, info, theBuffer)); -} - -NAPI_METHOD(getDeleterCallCount) { - napi_value callCount; - NAPI_CALL(env, napi_create_number(env, deleterCallCount, &callCount)); - NAPI_CALL(env, napi_set_return_value(env, info, callCount)); -} - -NAPI_METHOD(copyBuffer) { - napi_value theBuffer; - NAPI_CALL(env, napi_create_buffer_copy(env, theText, sizeof(theText), - &theBuffer)); - NAPI_CALL(env, napi_set_return_value(env, info, theBuffer)); -} - -NAPI_METHOD(bufferHasInstance) { - int argc; - NAPI_CALL(env, napi_get_cb_args_length(env, info, &argc)); - JS_ASSERT(env, argc == 1, "Wrong number of arguments"); - napi_value theBuffer; - NAPI_CALL(env, napi_get_cb_args(env, info, &theBuffer, 1)); - bool hasInstance; - napi_valuetype theType; - NAPI_CALL(env, napi_get_type_of_value(env, theBuffer, &theType)); - JS_ASSERT(env, theType == napi_object, - "bufferHasInstance: instance is not an object"); - NAPI_CALL(env, napi_is_buffer(env, theBuffer, &hasInstance)); - JS_ASSERT(env, hasInstance, "bufferHasInstance: instance is not a buffer"); - napi_value returnValue; - NAPI_CALL(env, napi_create_boolean(env, hasInstance, &returnValue)); - NAPI_CALL(env, napi_set_return_value(env, info, returnValue)); -} - -NAPI_METHOD(bufferInfo) { - int argc; - NAPI_CALL(env, napi_get_cb_args_length(env, info, &argc)); - JS_ASSERT(env, argc == 1, "Wrong number of arguments"); - napi_value theBuffer, returnValue; - NAPI_CALL(env, napi_get_cb_args(env, info, &theBuffer, 1)); - char *bufferData; - size_t bufferLength; - NAPI_CALL(env, napi_get_buffer_info(env, theBuffer, &bufferData, - &bufferLength)); - NAPI_CALL(env, napi_create_boolean(env, - !strcmp(bufferData, theText) && bufferLength == sizeof(theText), - &returnValue)); - NAPI_CALL(env, napi_set_return_value(env, info, returnValue)); -} - -NAPI_METHOD(staticBuffer) { - napi_value theBuffer; - NAPI_CALL(env, napi_create_external_buffer(env, sizeof(theText), - (char *)theText, noopDeleter, &theBuffer)); - NAPI_CALL(env, napi_set_return_value(env, info, theBuffer)); -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_propertyname propName; - napi_value theValue; - - NAPI_CALL(env, napi_property_name(env, "theText", &propName)); - NAPI_CALL(env, napi_create_string_utf8(env, theText, sizeof(theText), &theValue)); - NAPI_CALL(env, napi_set_property(env, exports, propName, theValue)); - - napi_property_descriptor methods[] = { - { "newBuffer", newBuffer }, - { "newExternalBuffer", newExternalBuffer }, - { "getDeleterCallCount", getDeleterCallCount }, - { "copyBuffer", copyBuffer }, - { "bufferHasInstance", bufferHasInstance }, - { "bufferInfo", bufferInfo }, - { "staticBuffer", staticBuffer } - }; - - NAPI_CALL(env, napi_define_properties(env, exports, - sizeof(methods)/sizeof(methods[0]), methods)); -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_constructor/test.js b/test/addons-abi/test_constructor/test.js deleted file mode 100644 index 25733573a9..0000000000 --- a/test/addons-abi/test_constructor/test.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// Testing api calls for a constructor that defines properties -var TestConstructor = require(`./build/${common.buildType}/test_constructor`); -var test_object = new TestConstructor(); - -assert.equal(test_object.echo('hello'), 'hello'); - -test_object.readwriteValue = 1; -assert.equal(test_object.readwriteValue, 1); -test_object.readwriteValue = 2; -assert.equal(test_object.readwriteValue, 2); - -assert.throws(() => { test_object.readonlyValue = 3; }); - -assert.ok(test_object.hiddenValue); - -// All properties except 'hiddenValue' should be enumerable. -var propertyNames = []; -for (var name in test_object) { - propertyNames.push(name); -} -assert.ok(propertyNames.indexOf('echo') >= 0); -assert.ok(propertyNames.indexOf('readwriteValue') >= 0); -assert.ok(propertyNames.indexOf('readonlyValue') >= 0); -assert.ok(propertyNames.indexOf('hiddenValue') < 0); diff --git a/test/addons-abi/test_constructor/test_constructor.cc b/test/addons-abi/test_constructor/test_constructor.cc deleted file mode 100644 index b8688fed90..0000000000 --- a/test/addons-abi/test_constructor/test_constructor.cc +++ /dev/null @@ -1,109 +0,0 @@ -#include - -static double value_ = 1; -napi_ref constructor_; - -void GetValue(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 0) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value number; - status = napi_create_number(env, value_, &number); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, number); - if (status != napi_ok) return; -} - -void SetValue(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value arg; - status = napi_get_cb_args(env, info, &arg, 1); - if (status != napi_ok) return; - - status = napi_get_value_double(env, arg, &value_); - if (status != napi_ok) return; -} - -void Echo(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value arg; - status = napi_get_cb_args(env, info, &arg, 1); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, arg); - if (status != napi_ok) return; -} - -void New(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value jsthis; - status = napi_get_cb_this(env, info, &jsthis); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, jsthis); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_value number; - status = napi_create_number(env, value_, &number); - if (status != napi_ok) return; - - napi_property_descriptor properties[] = { - { "echo", Echo }, - { "accessorValue", nullptr, GetValue, SetValue }, - { "readwriteValue", nullptr, nullptr, nullptr, number }, - { "readonlyValue", nullptr, nullptr, nullptr, number, napi_read_only }, - { "hiddenValue", nullptr, nullptr, nullptr, number, - static_cast(napi_read_only | napi_dont_enum) }, - }; - - napi_value cons; - status = napi_define_class(env, "MyObject", New, - nullptr, sizeof(properties)/sizeof(*properties), properties, &cons); - if (status != napi_ok) return; - - napi_propertyname name; - status = napi_property_name(env, "exports", &name); - if (status != napi_ok) return; - - status = napi_set_property(env, module, name, cons); - if (status != napi_ok) return; - - status = napi_create_reference(env, cons, 1, &constructor_); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_exception/test.js b/test/addons-abi/test_exception/test.js deleted file mode 100644 index 02ebabd696..0000000000 --- a/test/addons-abi/test_exception/test.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -var common = require('../../common'); -var test_exception = require(`./build/${common.buildType}/test_exception`); -var assert = require( "assert" ); -var theError = new Error( "Some error" ); -var throwTheError = function() { - throw theError; -}; -var caughtError; - -var throwNoError = function() {}; - -// Test that the native side successfully captures the exception -var returnedError = test_exception.returnException( throwTheError ); -assert.strictEqual( theError, returnedError, - "Returned error is strictly equal to the thrown error" ); - -// Test that the native side passes the exception through -try { - test_exception.allowException( throwTheError ); -} catch ( anError ) { - caughtError = anError; -} -assert.strictEqual( caughtError, theError, - "Thrown exception was allowed to pass through unhindered" ); -caughtError = undefined; - -// Test that the exception thrown above was marked as pending before it was handled on the JS side -assert.strictEqual( test_exception.wasPending(), true, - "VM was marked as having an exception pending when it was allowed through" ); - -// Test that the native side does not capture a non-existing exception -returnedError = test_exception.returnException( throwNoError ); -assert.strictEqual( undefined, returnedError, - "Returned error is undefined when no exception is thrown" ); - -// Test that no exception appears that was not thrown by us -try { - test_exception.allowException( throwNoError ); -} catch( anError ) { - caughtError = anError; -} -assert.strictEqual( undefined, caughtError, - "No exception originated on the native side" ); - -// Test that the exception state remains clear when no exception is thrown -assert.strictEqual( test_exception.wasPending(), false, - "VM was not marked as having an exception pending when none was allowed through" ); diff --git a/test/addons-abi/test_exception/test_exception.cc b/test/addons-abi/test_exception/test_exception.cc deleted file mode 100644 index 698a246a86..0000000000 --- a/test/addons-abi/test_exception/test_exception.cc +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include - -static bool exceptionWasPending = false; - -NAPI_METHOD(returnException) { - napi_status status; - napi_value jsFunction; - - status = napi_get_cb_args(env, info, &jsFunction, 1); - if (status != napi_ok) return; - - napi_value global; - status = napi_get_global(env, &global); - if (status != napi_ok) return; - - napi_value result; - status = napi_call_function(env, global, jsFunction, 0, 0, &result); - if (status == napi_pending_exception) { - napi_value ex; - status = napi_get_and_clear_last_exception(env, &ex); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, ex); - if (status != napi_ok) return; - } -} - -NAPI_METHOD(allowException) { - napi_status status; - napi_value jsFunction; - - status = napi_get_cb_args(env, info, &jsFunction, 1); - if (status != napi_ok) return; - - napi_value global; - status = napi_get_global(env, &global); - if (status != napi_ok) return; - - napi_value result; - status = napi_call_function(env, global, jsFunction, 0, 0, &result); - // Ignore status and check napi_is_exception_pending() instead. - - status = napi_is_exception_pending(env, &exceptionWasPending); - if (status != napi_ok) return; -} - -NAPI_METHOD(wasPending) { - napi_status status; - - napi_value result; - status = napi_create_boolean(env, exceptionWasPending, &result); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, result); - if (status != napi_ok) return; -} - -NAPI_MODULE_INIT(Init) { - napi_status status; - - napi_property_descriptor descriptors[] = { - { "returnException", returnException }, - { "allowException", allowException }, - { "wasPending", wasPending }, - }; - - status = napi_define_properties( - env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_function/test.js b/test/addons-abi/test_function/test.js deleted file mode 100644 index 7351519a20..0000000000 --- a/test/addons-abi/test_function/test.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// testing api calls for function -var test_function = require(`./build/${common.buildType}/test_function`); - - -function func1() { - return 1; -} -assert.equal(test_function.Test(func1), 1); - -function func2() { - console.log('hello world!'); -} -assert.equal(test_function.Test(func2), null); - -function func3(input) { - return input + 1; -} -assert.equal(test_function.Test(func3, 1), 2); - -function func4(input) { - return func3(input); -} -assert.equal(test_function.Test(func4, 1), 2); diff --git a/test/addons-abi/test_function/test_function.cc b/test/addons-abi/test_function/test_function.cc deleted file mode 100644 index 08004ad39c..0000000000 --- a/test/addons-abi/test_function/test_function.cc +++ /dev/null @@ -1,59 +0,0 @@ -#include - -void Test(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[10]; - status = napi_get_cb_args(env, info, args, 10); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_function) { - napi_throw_type_error(env, "Wrong type of argments. Expects a function."); - return; - } - - napi_value function = args[0]; - napi_value* argv = args + 1; - argc = argc - 1; - - napi_value global; - status = napi_get_global(env, &global); - if (status != napi_ok) return; - - napi_value result; - status = napi_call_function(env, global, function, argc, argv, &result); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, result); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_propertyname name; - status = napi_property_name(env, "Test", &name); - if (status != napi_ok) return; - - napi_value fn; - status = napi_create_function(env, Test, nullptr, &fn); - if (status != napi_ok) return; - - status = napi_set_property(env, exports, name, fn); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_instanceof/test.js b/test/addons-abi/test_instanceof/test.js deleted file mode 100644 index 1a9b126a7f..0000000000 --- a/test/addons-abi/test_instanceof/test.js +++ /dev/null @@ -1,37 +0,0 @@ -var fs = require( 'fs' ); - -var common = require('../../common'); -var assert = require('assert'); -var addon = require(`./build/${common.buildType}/test_instanceof`); -var path = require( 'path' ); - -function assertTrue(assertion) { - return assert.strictEqual(true, assertion); -} - -function assertFalse(assertion) { - assert.strictEqual(false, assertion); -} - -function assertEquals(leftHandSide, rightHandSide) { - assert.equal(leftHandSide, rightHandSide); -} - -function assertThrows(statement) { - assert.throws(function() { - eval(statement); - }, Error); -} - -function testFile( fileName ) { - eval( fs.readFileSync( fileName, { encoding: 'utf8' } ) - .replace( /[(]([^\s(]+)\s+instanceof\s+([^)]+)[)]/g, - '( addon.doInstanceOf( $1, $2 ) )' ) ); -} - -testFile( - path.join( path.resolve( __dirname, '..', '..', '..', 'deps', 'v8', 'test', 'mjsunit' ), - 'instanceof.js' ) ); -testFile( - path.join( path.resolve( __dirname, '..', '..', '..', 'deps', 'v8', 'test', 'mjsunit' ), - 'instanceof-2.js' ) ); diff --git a/test/addons-abi/test_instanceof/test_instanceof.cc b/test/addons-abi/test_instanceof/test_instanceof.cc deleted file mode 100644 index af13e25eaa..0000000000 --- a/test/addons-abi/test_instanceof/test_instanceof.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include - -void doInstanceOf(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value arguments[2]; - - status = napi_get_cb_args(env, info, arguments, 2); - if (status != napi_ok) return; - - bool instanceof; - status = napi_instanceof(env, arguments[0], arguments[1], &instanceof); - if (status != napi_ok) return; - - napi_value result; - status = napi_create_boolean(env, instanceof, &result); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, result); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor descriptors[] = { - { "doInstanceOf", doInstanceOf }, - }; - - status = napi_define_properties( - env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_number/test.js b/test/addons-abi/test_number/test.js deleted file mode 100644 index 76bff04ce1..0000000000 --- a/test/addons-abi/test_number/test.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); -var test_number = require(`./build/${common.buildType}/test_number`); - - -// testing api calls for number -assert.equal(0, test_number.Test(0)); -assert.equal(1, test_number.Test(1)); -assert.equal(-1, test_number.Test(-1)); -assert.equal(100, test_number.Test(100)); -assert.equal(2121, test_number.Test(2121)); -assert.equal(-1233, test_number.Test(-1233)); -assert.equal(986583, test_number.Test(986583)); -assert.equal(-976675, test_number.Test(-976675)); - -var num1 = 98765432213456789876546896323445679887645323232436587988766545658; -assert.equal(num1, test_number.Test(num1)); - -var num2 = -4350987086545760976737453646576078997096876957864353245245769809; -assert.equal(num2, test_number.Test(num2)); - -var num3 = Number.MAX_SAFE_INTEGER; -assert.equal(num3, test_number.Test(num3)); - -var num4 = Number.MAX_SAFE_INTEGER + 10; -assert.equal(num4, test_number.Test(num4)); - -var num5 = Number.MAX_VALUE; -assert.equal(num5, test_number.Test(num5)); - -var num6 = Number.MAX_VALUE + 10; -assert.equal(num6, test_number.Test(num6)); - -var num7 = Number.POSITIVE_INFINITY; -assert.equal(num7, test_number.Test(num7)); - -var num8 = Number.NEGATIVE_INFINITY; -assert.equal(num8, test_number.Test(num8)); diff --git a/test/addons-abi/test_number/test_number.cc b/test/addons-abi/test_number/test_number.cc deleted file mode 100644 index 68872f3652..0000000000 --- a/test/addons-abi/test_number/test_number.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include - -void Test(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_number) { - napi_throw_type_error(env, "Wrong type of argments. Expects a number."); - return; - } - - double input; - status = napi_get_value_double(env, args[0], &input); - if (status != napi_ok) return; - - napi_value output; - status = napi_create_number(env, input, &output); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor descriptors[] = { - { "Test", Test }, - }; - - status = napi_define_properties( - env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_object/test.js b/test/addons-abi/test_object/test.js deleted file mode 100644 index 4a40c0961f..0000000000 --- a/test/addons-abi/test_object/test.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// Testing api calls for objects -var test_object = require(`./build/${common.buildType}/test_object`); - - -var object = { - hello: 'world', - array: [ - 1, 94, 'str', 12.321, { test: 'obj in arr' } - ], - newObject: { - test: 'obj in obj' - } -}; - -assert.equal(test_object.Get(object, 'hello'), 'world'); -assert.deepStrictEqual(test_object.Get(object, 'array'), - [ 1, 94, 'str', 12.321, { test: 'obj in arr' } ]); -assert.deepStrictEqual(test_object.Get(object, 'newObject'), - { test: 'obj in obj' }); - -assert(test_object.Has(object, 'hello')); -assert(test_object.Has(object, 'array')); -assert(test_object.Has(object, 'newObject')); - -var newObject = test_object.New(); -assert(test_object.Has(newObject, 'test_number')); -assert.equal(newObject.test_number, 987654321); -assert.equal(newObject.test_string, 'test string'); - -// test_object.Inflate increases all properties by 1 -var cube = { - x: 10, - y: 10, - z: 10 -}; - -assert.deepStrictEqual(test_object.Inflate(cube), {x: 11, y: 11, z: 11}); -assert.deepStrictEqual(test_object.Inflate(cube), {x: 12, y: 12, z: 12}); -assert.deepStrictEqual(test_object.Inflate(cube), {x: 13, y: 13, z: 13}); -cube.t = 13; -assert.deepStrictEqual(test_object.Inflate(cube), {x: 14, y: 14, z: 14, t: 14}); - diff --git a/test/addons-abi/test_object/test_object.cc b/test/addons-abi/test_object/test_object.cc deleted file mode 100644 index f6c3653a2d..0000000000 --- a/test/addons-abi/test_object/test_object.cc +++ /dev/null @@ -1,232 +0,0 @@ -#include - -void Get(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 2) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - napi_valuetype valuetype0; - status = napi_get_type_of_value(env, args[0], &valuetype0); - if (status != napi_ok) return; - - if (valuetype0 != napi_object) { - napi_throw_type_error(env, - "Wrong type of argments. Expects an object as first argument."); - return; - } - - napi_valuetype valuetype1; - status = napi_get_type_of_value(env, args[1], &valuetype1); - if (status != napi_ok) return; - - if (valuetype1 != napi_string) { - napi_throw_type_error(env, - "Wrong type of argments. Expects a string as second argument."); - return; - } - - napi_value object = args[0]; - char buffer[128]; - int buffer_size = 128; - - status = napi_get_value_string_utf8(env, args[1], buffer, buffer_size, nullptr); - if (status != napi_ok) return; - - napi_propertyname property_name; - status = napi_property_name(env, buffer, &property_name); - if (status != napi_ok) return; - - napi_value output; - status = napi_get_property(env, object, property_name, &output); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output); - if (status != napi_ok) return; -} - -void Has(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 2) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - napi_valuetype valuetype0; - status = napi_get_type_of_value(env, args[0], &valuetype0); - if (status != napi_ok) return; - - if (valuetype0 != napi_object) { - napi_throw_type_error(env, - "Wrong type of argments. Expects an object as first argument."); - return; - } - - napi_valuetype valuetype1; - status = napi_get_type_of_value(env, args[1], &valuetype1); - if (status != napi_ok) return; - - if (valuetype1 != napi_string) { - napi_throw_type_error(env, - "Wrong type of argments. Expects a string as second argument."); - return; - } - - napi_value obj = args[0]; - const int buffer_size = 128; - char buffer[buffer_size]; - - status = napi_get_value_string_utf8(env, args[1], buffer, buffer_size, nullptr); - if (status != napi_ok) return; - - napi_propertyname property_name; - status = napi_property_name(env, buffer, &property_name); - if (status != napi_ok) return; - - bool has_property; - status = napi_has_property(env, obj, property_name, &has_property); - if (status != napi_ok) return; - - napi_value ret; - status = napi_create_boolean(env, has_property, &ret); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, ret); - if (status != napi_ok) return; -} - -void New(napi_env env, napi_callback_info info) { - napi_status status; - - napi_value ret; - status = napi_create_object(env, &ret); - - napi_propertyname test_number; - status = napi_property_name(env, "test_number", &test_number); - if (status != napi_ok) return; - - napi_value num; - status = napi_create_number(env, 987654321, &num); - if (status != napi_ok) return; - - status = napi_set_property(env, ret, test_number, num); - if (status != napi_ok) return; - - napi_propertyname test_string; - status = napi_property_name(env, "test_string", &test_string); - if (status != napi_ok) return; - - napi_value str; - status = napi_create_string_utf8(env, "test string", -1, &str); - if (status != napi_ok) return; - - status = napi_set_property(env, ret, test_string, str); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, ret); - if (status != napi_ok) return; -} - -void Inflate(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_object) { - napi_throw_type_error(env, - "Wrong type of argments. Expects an object as first argument."); - return; - } - - napi_value obj = args[0]; - - napi_value propertynames; - status = napi_get_propertynames(env, obj, &propertynames); - if (status != napi_ok) return; - - uint32_t length; - status = napi_get_array_length(env, propertynames, &length); - if (status != napi_ok) return; - - for (uint32_t i = 0; i < length; i++) { - napi_value property_str; - status = napi_get_element(env, propertynames, i, &property_str); - if (status != napi_ok) return; - - const int buffer_size = 128; - char buffer[buffer_size]; - - status = napi_get_value_string_utf8(env, property_str, buffer, buffer_size, nullptr); - if (status != napi_ok) return; - - napi_propertyname propertyname; - status = napi_property_name(env, buffer, &propertyname); - if (status != napi_ok) return; - - napi_value value; - status = napi_get_property(env, obj, propertyname, &value); - if (status != napi_ok) return; - - double double_val; - status = napi_get_value_double(env, value, &double_val); - if (status != napi_ok) return; - - status = napi_create_number(env, double_val + 1, &value); - if (status != napi_ok) return; - - status = napi_set_property(env, obj, propertyname, value); - if (status != napi_ok) return; - } - status = napi_set_return_value(env, info, obj); -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor descriptors[] = { - { "Get", Get }, - { "Has", Has }, - { "New", New }, - { "Inflate", Inflate }, - }; - - status = napi_define_properties( - env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_properties/test.js b/test/addons-abi/test_properties/test.js deleted file mode 100644 index a32dc7bc32..0000000000 --- a/test/addons-abi/test_properties/test.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// Testing api calls for defining properties -var test_object = require(`./build/${common.buildType}/test_properties`); - -assert.equal(test_object.echo('hello'), 'hello'); - -test_object.readwriteValue = 1; -assert.equal(test_object.readwriteValue, 1); -test_object.readwriteValue = 2; -assert.equal(test_object.readwriteValue, 2); - -assert.throws(() => { test_object.readonlyValue = 3; }); - -assert.ok(test_object.hiddenValue); - -// All properties except 'hiddenValue' should be enumerable. -var propertyNames = []; -for (var name in test_object) { - propertyNames.push(name); -} -assert.ok(propertyNames.indexOf('echo') >= 0); -assert.ok(propertyNames.indexOf('readwriteValue') >= 0); -assert.ok(propertyNames.indexOf('readonlyValue') >= 0); -assert.ok(propertyNames.indexOf('hiddenValue') < 0); diff --git a/test/addons-abi/test_properties/test_properties.cc b/test/addons-abi/test_properties/test_properties.cc deleted file mode 100644 index d4773b10bd..0000000000 --- a/test/addons-abi/test_properties/test_properties.cc +++ /dev/null @@ -1,86 +0,0 @@ -#include - -static double value_ = 1; - -void GetValue(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 0) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value number; - status = napi_create_number(env, value_, &number); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, number); - if (status != napi_ok) return; -} - -void SetValue(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value arg; - status = napi_get_cb_args(env, info, &arg, 1); - if (status != napi_ok) return; - - status = napi_get_value_double(env, arg, &value_); - if (status != napi_ok) return; -} - -void Echo(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value arg; - status = napi_get_cb_args(env, info, &arg, 1); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, arg); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_value number; - status = napi_create_number(env, value_, &number); - if (status != napi_ok) return; - - napi_property_descriptor properties[] = { - { "echo", Echo }, - { "accessorValue", nullptr, GetValue, SetValue }, - { "readwriteValue", nullptr, nullptr, nullptr, number }, - { "readonlyValue", nullptr, nullptr, nullptr, number, napi_read_only }, - { "hiddenValue", nullptr, nullptr, nullptr, number, - static_cast(napi_read_only | napi_dont_enum) }, - }; - - status = napi_define_properties( - env, exports, sizeof(properties) / sizeof(*properties), properties); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_string/test.js b/test/addons-abi/test_string/test.js deleted file mode 100644 index b489ad3d37..0000000000 --- a/test/addons-abi/test_string/test.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// testing api calls for string -var test_string = require(`./build/${common.buildType}/test_string`); - -var str1 = 'hello world'; -assert.equal(test_string.Copy(str1), str1); -assert.equal(test_string.Length(str1), 11); -assert.equal(test_string.Utf8Length(str1), 11); - -var str2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; -assert.equal(test_string.Copy(str2), str2); -assert.equal(test_string.Length(str2), 62); -assert.equal(test_string.Utf8Length(str2), 62); - -var str3 = '?!@#$%^&*()_+-=[]{}/.,<>\'\"\\'; -assert.equal(test_string.Copy(str3), str3); -assert.equal(test_string.Length(str3), 27); -assert.equal(test_string.Utf8Length(str3), 27); - -var str4 = '\u{2003}\u{2101}\u{2001}'; -assert.equal(test_string.Copy(str4), str4); -assert.equal(test_string.Length(str4), 3); -assert.equal(test_string.Utf8Length(str4), 9); diff --git a/test/addons-abi/test_string/test_string.cc b/test/addons-abi/test_string/test_string.cc deleted file mode 100644 index 3c0d0812db..0000000000 --- a/test/addons-abi/test_string/test_string.cc +++ /dev/null @@ -1,130 +0,0 @@ -#include - -void Copy(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_string) { - napi_throw_type_error(env, "Wrong type of argments. Expects a string."); - return; - } - - char buffer[128]; - int buffer_size = 128; - - status = napi_get_value_string_utf8(env, args[0], buffer, buffer_size, nullptr); - if (status != napi_ok) return; - - napi_value output; - status = napi_create_string_utf8(env, buffer, -1, &output); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output); - if (status != napi_ok) return; -} - -void Length(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_string) { - napi_throw_type_error(env, "Wrong type of argments. Expects a string."); - return; - } - - int length; - status = napi_get_value_string_length(env, args[0], &length); - if (status != napi_ok) return; - - napi_value output; - status = napi_create_number(env, length, &output); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output); - if (status != napi_ok) return; -} - -void Utf8Length(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_string) { - napi_throw_type_error(env, "Wrong type of argments. Expects a string."); - return; - } - - int length; - status = napi_get_value_string_utf8_length(env, args[0], &length); - if (status != napi_ok) return; - - napi_value output; - status = napi_create_number(env, length, &output); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor properties[] = { - { "Copy", Copy }, - { "Length", Length }, - { "Utf8Length", Utf8Length }, - }; - - status = napi_define_properties( - env, exports, sizeof(properties) / sizeof(*properties), properties); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_symbol/test1.js b/test/addons-abi/test_symbol/test1.js deleted file mode 100644 index 77e2698335..0000000000 --- a/test/addons-abi/test_symbol/test1.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// testing api calls for symbol -var test_symbol = require(`./build/${common.buildType}/test_symbol`); - -var sym = test_symbol.New('test'); -assert.equal(sym.toString(), 'Symbol(test)'); - - -var myObj = {}; -var fooSym = test_symbol.New('foo'); -var otherSym = test_symbol.New('bar'); -myObj['foo'] = 'bar'; -myObj[fooSym] = 'baz'; -myObj[otherSym] = 'bing'; -assert(myObj.foo === 'bar'); -assert(myObj[fooSym] === 'baz'); -assert(myObj[otherSym] === 'bing'); diff --git a/test/addons-abi/test_symbol/test2.js b/test/addons-abi/test_symbol/test2.js deleted file mode 100644 index c786584a76..0000000000 --- a/test/addons-abi/test_symbol/test2.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// testing api calls for symbol -var test_symbol = require(`./build/${common.buildType}/test_symbol`); - -var fooSym = test_symbol.New('foo'); -var myObj = {}; -myObj['foo'] = 'bar'; -myObj[fooSym] = 'baz'; -Object.keys(myObj); // -> [ 'foo' ] -Object.getOwnPropertyNames(myObj); // -> [ 'foo' ] -Object.getOwnPropertySymbols(myObj); // -> [ Symbol(foo) ] -assert(Object.getOwnPropertySymbols(myObj)[0] === fooSym); diff --git a/test/addons-abi/test_symbol/test3.js b/test/addons-abi/test_symbol/test3.js deleted file mode 100644 index de76c0b87c..0000000000 --- a/test/addons-abi/test_symbol/test3.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// testing api calls for symbol -var test_symbol = require(`./build/${common.buildType}/test_symbol`); - -assert.notEqual(test_symbol.New(), test_symbol.New()); -assert.notEqual(test_symbol.New('foo'), test_symbol.New('foo')); -assert.notEqual(test_symbol.New('foo'), test_symbol.New('bar')); - -var foo1 = test_symbol.New('foo'); -var foo2 = test_symbol.New('foo'); -var object = { - [foo1]: 1, - [foo2]: 2, -}; -assert(object[foo1] === 1); -assert(object[foo2] === 2); diff --git a/test/addons-abi/test_symbol/test_symbol.cc b/test/addons-abi/test_symbol/test_symbol.cc deleted file mode 100644 index ee0b402726..0000000000 --- a/test/addons-abi/test_symbol/test_symbol.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include - -void Test(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc < 1) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[1]; - status = napi_get_cb_args(env, info, args, 1); - if (status != napi_ok) return; - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_symbol) { - napi_throw_type_error(env, "Wrong type of argments. Expects a symbol."); - return; - } - - char buffer[128]; - int buffer_size = 128; - - status = napi_get_value_string_utf8(env, args[0], buffer, buffer_size, nullptr); - if (status != napi_ok) return; - - napi_value output; - status = napi_create_string_utf8(env, buffer, -1, &output); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output); - if (status != napi_ok) return; -} - -void New(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc >= 1) { - napi_value args[1]; - napi_get_cb_args(env, info, args, 1); - - napi_valuetype valuetype; - status = napi_get_type_of_value(env, args[0], &valuetype); - if (status != napi_ok) return; - - if (valuetype != napi_string) { - napi_throw_type_error(env, "Wrong type of argments. Expects a string."); - return; - } - - char buffer[128]; - int buffer_size = 128; - status = napi_get_value_string_utf8(env, args[0], buffer, buffer_size, nullptr); - if (status != napi_ok) return; - - napi_value symbol; - status = napi_create_symbol(env, buffer, &symbol); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, symbol); - if (status != napi_ok) return; - } else { - napi_value symbol; - status = napi_create_symbol(env, NULL, &symbol); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, symbol); - if (status != napi_ok) return; - } -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor properties[] = { - { "New", New }, - }; - - status = napi_define_properties( - env, exports, sizeof(properties) / sizeof(*properties), properties); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/test_typedarray/test.js b/test/addons-abi/test_typedarray/test.js deleted file mode 100644 index d5748da5aa..0000000000 --- a/test/addons-abi/test_typedarray/test.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; -var common = require('../../common'); -var assert = require('assert'); - -// Testing api calls for arrays -var test_typedarray = require(`./build/${common.buildType}/test_typedarray`); - -var byteArray = new Uint8Array(3); -byteArray[0] = 0; -byteArray[1] = 1; -byteArray[2] = 2; -assert.equal(byteArray.length, 3); - -var doubleArray = new Float64Array(3); -doubleArray[0] = 0.0; -doubleArray[1] = 1.1; -doubleArray[2] = 2.2; -assert.equal(doubleArray.length, 3); - -var byteResult = test_typedarray.Multiply(byteArray, 3); -assert.ok(byteResult instanceof Uint8Array); -assert.equal(byteResult.length, 3); -assert.equal(byteResult[0], 0); -assert.equal(byteResult[1], 3); -assert.equal(byteResult[2], 6); - -var doubleResult = test_typedarray.Multiply(doubleArray, -3); -assert.ok(doubleResult instanceof Float64Array); -assert.equal(doubleResult.length, 3); -assert.equal(doubleResult[0], 0); -assert.equal(Math.round(10 * doubleResult[1]) / 10, -3.3); -assert.equal(Math.round(10 * doubleResult[2]) / 10, -6.6); - -var externalResult = test_typedarray.External(); -assert.ok(externalResult instanceof Int8Array); -assert.equal(externalResult.length, 3); -assert.equal(externalResult[0], 0); -assert.equal(externalResult[1], 1); -assert.equal(externalResult[2], 2); diff --git a/test/addons-abi/test_typedarray/test_typedarray.cc b/test/addons-abi/test_typedarray/test_typedarray.cc deleted file mode 100644 index 9e6531f361..0000000000 --- a/test/addons-abi/test_typedarray/test_typedarray.cc +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include - -void Multiply(napi_env env, napi_callback_info info) { - napi_status status; - - int argc; - status = napi_get_cb_args_length(env, info, &argc); - if (status != napi_ok) return; - - if (argc != 2) { - napi_throw_type_error(env, "Wrong number of arguments"); - return; - } - - napi_value args[2]; - status = napi_get_cb_args(env, info, args, 2); - if (status != napi_ok) return; - - napi_valuetype valuetype0; - status = napi_get_type_of_value(env, args[0], &valuetype0); - if (status != napi_ok) return; - - if (valuetype0 != napi_object) { - napi_throw_type_error(env, - "Wrong type of argments. Expects a typed array as first argument."); - return; - } - - napi_value input_array = args[0]; - bool istypedarray; - status = napi_is_typedarray(env, input_array, &istypedarray); - if (status != napi_ok) return; - - if (!istypedarray) { - napi_throw_type_error(env, - "Wrong type of argments. Expects a typed array as first argument."); - return; - } - - napi_valuetype valuetype1; - status = napi_get_type_of_value(env, args[1], &valuetype1); - if (status != napi_ok) return; - - if (valuetype1 != napi_number) { - napi_throw_type_error(env, - "Wrong type of argments. Expects a number as second argument."); - return; - } - - double multiplier; - status = napi_get_value_double(env, args[1], &multiplier); - if (status != napi_ok) return; - - napi_typedarray_type type; - napi_value input_buffer; - size_t byte_offset; - size_t length; - status = napi_get_typedarray_info( - env, input_array, &type, &length, nullptr, &input_buffer, &byte_offset); - if (status != napi_ok) return; - - void* data; - size_t byte_length; - status = napi_get_arraybuffer_info(env, input_buffer, &data, &byte_length); - if (status != napi_ok) return; - - napi_value output_buffer; - void* output_ptr = nullptr; - status = napi_create_arraybuffer(env, byte_length, &output_ptr, &output_buffer); - if (status != napi_ok) return; - - napi_value output_array; - status = napi_create_typedarray(env, type, length, output_buffer, byte_offset, &output_array); - if (status != napi_ok) return; - - if (type == napi_uint8) { - uint8_t* input_bytes = reinterpret_cast(data) + byte_offset; - uint8_t* output_bytes = reinterpret_cast(output_ptr); - for (size_t i = 0; i < length; i++) { - output_bytes[i] = static_cast(input_bytes[i] * multiplier); - } - } - else if (type == napi_float64) { - double* input_doubles = reinterpret_cast( - reinterpret_cast(data) + byte_offset); - double* output_doubles = reinterpret_cast(output_ptr); - for (size_t i = 0; i < length; i++) { - output_doubles[i] = input_doubles[i] * multiplier; - } - } - else { - napi_throw_error(env, "Typed array was of a type not expected by test."); - return; - } - - status = napi_set_return_value(env, info, output_array); - if (status != napi_ok) return; -} - -void External(napi_env env, napi_callback_info info) { - static int8_t externalData[] = { 0, 1, 2 }; - - napi_value output_buffer; - napi_status status = napi_create_external_arraybuffer( - env, externalData, sizeof(externalData), nullptr, &output_buffer); - if (status != napi_ok) return; - - napi_value output_array; - status = napi_create_typedarray( - env, napi_int8, sizeof(externalData) / sizeof(uint8_t), output_buffer, 0, &output_array); - if (status != napi_ok) return; - - status = napi_set_return_value(env, info, output_array); - if (status != napi_ok) return; -} - -void Init(napi_env env, napi_value exports, napi_value module) { - napi_status status; - - napi_property_descriptor descriptors[] = { - { "Multiply", Multiply }, - { "External", External }, - }; - - status = napi_define_properties( - env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors); - if (status != napi_ok) return; -} - -NODE_MODULE_ABI(addon, Init) diff --git a/test/addons-abi/.gitignore b/test/addons-napi/.gitignore similarity index 100% rename from test/addons-abi/.gitignore rename to test/addons-napi/.gitignore diff --git a/test/addons-napi/1_hello_world/binding.c b/test/addons-napi/1_hello_world/binding.c new file mode 100644 index 0000000000..57bac37fa0 --- /dev/null +++ b/test/addons-napi/1_hello_world/binding.c @@ -0,0 +1,18 @@ +#include +#include "../common.h" +#include + +napi_value Method(napi_env env, napi_callback_info info) { + napi_value world; + const char* str = "world"; + size_t str_len = strlen(str); + NAPI_CALL(env, napi_create_string_utf8(env, str, str_len, &world)); + return world; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method); + NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 1, &desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/1_hello_world/binding.gyp b/test/addons-napi/1_hello_world/binding.gyp similarity index 66% rename from test/addons-abi/1_hello_world/binding.gyp rename to test/addons-napi/1_hello_world/binding.gyp index 2656c2651a..62381d5e54 100644 --- a/test/addons-abi/1_hello_world/binding.gyp +++ b/test/addons-napi/1_hello_world/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "binding", - "sources": [ "binding.cc" ] + "sources": [ "binding.c" ] } ] } diff --git a/test/addons-napi/1_hello_world/test.js b/test/addons-napi/1_hello_world/test.js new file mode 100644 index 0000000000..c975c48a73 --- /dev/null +++ b/test/addons-napi/1_hello_world/test.js @@ -0,0 +1,6 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +assert.strictEqual(addon.hello(), 'world'); diff --git a/test/addons-napi/2_function_arguments/binding.c b/test/addons-napi/2_function_arguments/binding.c new file mode 100644 index 0000000000..a5ccac7b61 --- /dev/null +++ b/test/addons-napi/2_function_arguments/binding.c @@ -0,0 +1,37 @@ +#include +#include "../common.h" + +napi_value Add(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number, + "Wrong argument type. Numbers expected."); + + double value0; + NAPI_CALL(env, napi_get_value_double(env, args[0], &value0)); + + double value1; + NAPI_CALL(env, napi_get_value_double(env, args[1], &value1)); + + napi_value sum; + NAPI_CALL(env, napi_create_number(env, value0 + value1, &sum)); + + return sum; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("add", Add); + NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 1, &desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/2_function_arguments/binding.gyp b/test/addons-napi/2_function_arguments/binding.gyp similarity index 66% rename from test/addons-abi/2_function_arguments/binding.gyp rename to test/addons-napi/2_function_arguments/binding.gyp index 2656c2651a..62381d5e54 100644 --- a/test/addons-abi/2_function_arguments/binding.gyp +++ b/test/addons-napi/2_function_arguments/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "binding", - "sources": [ "binding.cc" ] + "sources": [ "binding.c" ] } ] } diff --git a/test/addons-napi/2_function_arguments/test.js b/test/addons-napi/2_function_arguments/test.js new file mode 100644 index 0000000000..e70f76b718 --- /dev/null +++ b/test/addons-napi/2_function_arguments/test.js @@ -0,0 +1,6 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +assert.strictEqual(addon.add(3, 5), 8); diff --git a/test/addons-napi/3_callbacks/binding.c b/test/addons-napi/3_callbacks/binding.c new file mode 100644 index 0000000000..47360bd979 --- /dev/null +++ b/test/addons-napi/3_callbacks/binding.c @@ -0,0 +1,43 @@ +#include +#include "../common.h" +#include + +napi_value RunCallback(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value argv[1]; + const char* str = "hello world"; + size_t str_len = strlen(str); + NAPI_CALL(env, napi_create_string_utf8(env, str, str_len, argv)); + + napi_value global; + NAPI_CALL(env, napi_get_global(env, &global)); + + napi_value cb = args[0]; + NAPI_CALL(env, napi_call_function(env, global, cb, 1, argv, NULL)); + + return NULL; +} + +napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value cb = args[0]; + napi_value recv = args[1]; + NAPI_CALL(env, napi_call_function(env, recv, cb, 0, NULL, NULL)); + return NULL; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor desc[2] = { + DECLARE_NAPI_PROPERTY("RunCallback", RunCallback), + DECLARE_NAPI_PROPERTY("RunCallbackWithRecv", RunCallbackWithRecv), + }; + NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 2, desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/3_callbacks/binding.gyp b/test/addons-napi/3_callbacks/binding.gyp similarity index 66% rename from test/addons-abi/3_callbacks/binding.gyp rename to test/addons-napi/3_callbacks/binding.gyp index 2656c2651a..62381d5e54 100644 --- a/test/addons-abi/3_callbacks/binding.gyp +++ b/test/addons-napi/3_callbacks/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "binding", - "sources": [ "binding.cc" ] + "sources": [ "binding.c" ] } ] } diff --git a/test/addons-napi/3_callbacks/test.js b/test/addons-napi/3_callbacks/test.js new file mode 100644 index 0000000000..25e070d974 --- /dev/null +++ b/test/addons-napi/3_callbacks/test.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +addon.RunCallback(function(msg) { + assert.strictEqual(msg, 'hello world'); +}); + +function testRecv(desiredRecv) { + addon.RunCallbackWithRecv(function() { + assert.strictEqual(this, desiredRecv); + }, desiredRecv); +} + +testRecv(undefined); +testRecv(null); +testRecv(5); +testRecv(true); +testRecv('Hello'); +testRecv([]); +testRecv({}); diff --git a/test/addons-napi/4_object_factory/binding.c b/test/addons-napi/4_object_factory/binding.c new file mode 100644 index 0000000000..d76f30b847 --- /dev/null +++ b/test/addons-napi/4_object_factory/binding.c @@ -0,0 +1,23 @@ +#include +#include "../common.h" + +napi_value CreateObject(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value obj; + NAPI_CALL(env, napi_create_object(env, &obj)); + + NAPI_CALL(env, napi_set_named_property(env, obj, "msg", args[0])); + + return obj; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor desc = + DECLARE_NAPI_PROPERTY("exports", CreateObject); + NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/4_object_factory/binding.gyp b/test/addons-napi/4_object_factory/binding.gyp similarity index 66% rename from test/addons-abi/4_object_factory/binding.gyp rename to test/addons-napi/4_object_factory/binding.gyp index 2656c2651a..62381d5e54 100644 --- a/test/addons-abi/4_object_factory/binding.gyp +++ b/test/addons-napi/4_object_factory/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "binding", - "sources": [ "binding.cc" ] + "sources": [ "binding.c" ] } ] } diff --git a/test/addons-napi/4_object_factory/test.js b/test/addons-napi/4_object_factory/test.js new file mode 100644 index 0000000000..15313c1547 --- /dev/null +++ b/test/addons-napi/4_object_factory/test.js @@ -0,0 +1,8 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +const obj1 = addon('hello'); +const obj2 = addon('world'); +assert.strictEqual(`${obj1.msg} ${obj2.msg}`, 'hello world'); diff --git a/test/addons-napi/5_function_factory/binding.c b/test/addons-napi/5_function_factory/binding.c new file mode 100644 index 0000000000..4c20190f50 --- /dev/null +++ b/test/addons-napi/5_function_factory/binding.c @@ -0,0 +1,25 @@ +#include +#include "../common.h" + +napi_value MyFunction(napi_env env, napi_callback_info info) { + napi_value str; + NAPI_CALL(env, napi_create_string_utf8(env, "hello world", -1, &str)); + + return str; +} + +napi_value CreateFunction(napi_env env, napi_callback_info info) { + napi_value fn; + NAPI_CALL(env, + napi_create_function(env, "theFunction", MyFunction, NULL, &fn)); + + return fn; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor desc = + DECLARE_NAPI_PROPERTY("exports", CreateFunction); + NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-napi/5_function_factory/binding.gyp b/test/addons-napi/5_function_factory/binding.gyp new file mode 100644 index 0000000000..62381d5e54 --- /dev/null +++ b/test/addons-napi/5_function_factory/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "binding", + "sources": [ "binding.c" ] + } + ] +} diff --git a/test/addons-napi/5_function_factory/test.js b/test/addons-napi/5_function_factory/test.js new file mode 100644 index 0000000000..7521824e1e --- /dev/null +++ b/test/addons-napi/5_function_factory/test.js @@ -0,0 +1,7 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +const fn = addon(); +assert.strictEqual(fn(), 'hello world'); // 'hello world' diff --git a/test/addons-napi/6_object_wrap/binding.cc b/test/addons-napi/6_object_wrap/binding.cc new file mode 100644 index 0000000000..99d8339bd0 --- /dev/null +++ b/test/addons-napi/6_object_wrap/binding.cc @@ -0,0 +1,7 @@ +#include "myobject.h" + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + MyObject::Init(env, exports); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/6_object_wrap/binding.gyp b/test/addons-napi/6_object_wrap/binding.gyp similarity index 100% rename from test/addons-abi/6_object_wrap/binding.gyp rename to test/addons-napi/6_object_wrap/binding.gyp diff --git a/test/addons-napi/6_object_wrap/myobject.cc b/test/addons-napi/6_object_wrap/myobject.cc new file mode 100644 index 0000000000..c2557facc0 --- /dev/null +++ b/test/addons-napi/6_object_wrap/myobject.cc @@ -0,0 +1,149 @@ +#include "myobject.h" +#include "../common.h" + +napi_ref MyObject::constructor; + +MyObject::MyObject(double value) + : value_(value), env_(nullptr), wrapper_(nullptr) {} + +MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); } + +void MyObject::Destructor( + napi_env env, void* nativeObject, void* /*finalize_hint*/) { + MyObject* obj = static_cast(nativeObject); + delete obj; +} + +void MyObject::Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + { "value", nullptr, nullptr, GetValue, SetValue, 0, napi_default, 0 }, + DECLARE_NAPI_PROPERTY("plusOne", PlusOne), + DECLARE_NAPI_PROPERTY("multiply", Multiply), + }; + + napi_value cons; + NAPI_CALL_RETURN_VOID(env, + napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons)); + + NAPI_CALL_RETURN_VOID(env, napi_create_reference(env, cons, 1, &constructor)); + + NAPI_CALL_RETURN_VOID(env, + napi_set_named_property(env, exports, "MyObject", cons)); +} + +napi_value MyObject::New(napi_env env, napi_callback_info info) { + bool is_constructor; + NAPI_CALL(env, napi_is_construct_call(env, info, &is_constructor)); + + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + if (is_constructor) { + // Invoked as constructor: `new MyObject(...)` + double value = 0; + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + if (valuetype != napi_undefined) { + NAPI_CALL(env, napi_get_value_double(env, args[0], &value)); + } + + MyObject* obj = new MyObject(value); + + obj->env_ = env; + NAPI_CALL(env, napi_wrap(env, + _this, + obj, + MyObject::Destructor, + nullptr, // finalize_hint + &obj->wrapper_)); + + return _this; + } + + // Invoked as plain function `MyObject(...)`, turn into construct call. + argc = 1; + napi_value argv[1] = {args[0]}; + + napi_value cons; + NAPI_CALL(env, napi_get_reference_value(env, constructor, &cons)); + + napi_value instance; + NAPI_CALL(env, napi_new_instance(env, cons, argc, argv, &instance)); + + return instance; +} + +napi_value MyObject::GetValue(napi_env env, napi_callback_info info) { + napi_value _this; + NAPI_CALL(env, + napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + MyObject* obj; + NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast(&obj))); + + napi_value num; + NAPI_CALL(env, napi_create_number(env, obj->value_, &num)); + + return num; +} + +napi_value MyObject::SetValue(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + MyObject* obj; + NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast(&obj))); + + NAPI_CALL(env, napi_get_value_double(env, args[0], &obj->value_)); + + return nullptr; +} + +napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) { + napi_value _this; + NAPI_CALL(env, + napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + MyObject* obj; + NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast(&obj))); + + obj->value_ += 1; + + napi_value num; + NAPI_CALL(env, napi_create_number(env, obj->value_, &num)); + + return num; +} + +napi_value MyObject::Multiply(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + double multiple = 1; + if (argc >= 1) { + NAPI_CALL(env, napi_get_value_double(env, args[0], &multiple)); + } + + MyObject* obj; + NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast(&obj))); + + napi_value cons; + NAPI_CALL(env, napi_get_reference_value(env, constructor, &cons)); + + const int kArgCount = 1; + napi_value argv[kArgCount]; + NAPI_CALL(env, napi_create_number(env, obj->value_ * multiple, argv)); + + napi_value instance; + NAPI_CALL(env, napi_new_instance(env, cons, kArgCount, argv, &instance)); + + return instance; +} diff --git a/test/addons-napi/6_object_wrap/myobject.h b/test/addons-napi/6_object_wrap/myobject.h new file mode 100644 index 0000000000..b7f425951d --- /dev/null +++ b/test/addons-napi/6_object_wrap/myobject.h @@ -0,0 +1,26 @@ +#ifndef TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ +#define TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ + +#include + +class MyObject { + public: + static void Init(napi_env env, napi_value exports); + static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); + + private: + explicit MyObject(double value_ = 0); + ~MyObject(); + + static napi_value New(napi_env env, napi_callback_info info); + static napi_value GetValue(napi_env env, napi_callback_info info); + static napi_value SetValue(napi_env env, napi_callback_info info); + static napi_value PlusOne(napi_env env, napi_callback_info info); + static napi_value Multiply(napi_env env, napi_callback_info info); + static napi_ref constructor; + double value_; + napi_env env_; + napi_ref wrapper_; +}; + +#endif // TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ diff --git a/test/addons-napi/6_object_wrap/test.js b/test/addons-napi/6_object_wrap/test.js new file mode 100644 index 0000000000..4d89da6a43 --- /dev/null +++ b/test/addons-napi/6_object_wrap/test.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +const obj = new addon.MyObject(9); +assert.strictEqual(obj.value, 9); +obj.value = 10; +assert.strictEqual(obj.value, 10); +assert.strictEqual(obj.plusOne(), 11); +assert.strictEqual(obj.plusOne(), 12); +assert.strictEqual(obj.plusOne(), 13); + +assert.strictEqual(obj.multiply().value, 13); +assert.strictEqual(obj.multiply(10).value, 130); + +const newobj = obj.multiply(-1); +assert.strictEqual(newobj.value, -13); +assert.notStrictEqual(obj, newobj); diff --git a/test/addons-napi/7_factory_wrap/binding.cc b/test/addons-napi/7_factory_wrap/binding.cc new file mode 100644 index 0000000000..c61e409478 --- /dev/null +++ b/test/addons-napi/7_factory_wrap/binding.cc @@ -0,0 +1,23 @@ +#include "myobject.h" +#include "../common.h" + +napi_value CreateObject(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + napi_value instance; + NAPI_CALL(env, MyObject::NewInstance(env, args[0], &instance)); + + return instance; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + NAPI_CALL_RETURN_VOID(env, MyObject::Init(env)); + + napi_property_descriptor desc = + DECLARE_NAPI_PROPERTY("exports", CreateObject); + NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/7_factory_wrap/binding.gyp b/test/addons-napi/7_factory_wrap/binding.gyp similarity index 100% rename from test/addons-abi/7_factory_wrap/binding.gyp rename to test/addons-napi/7_factory_wrap/binding.gyp diff --git a/test/addons-napi/7_factory_wrap/myobject.cc b/test/addons-napi/7_factory_wrap/myobject.cc new file mode 100644 index 0000000000..d6b374d7bb --- /dev/null +++ b/test/addons-napi/7_factory_wrap/myobject.cc @@ -0,0 +1,94 @@ +#include "myobject.h" +#include "../common.h" + +MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} + +MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); } + +void MyObject::Destructor(napi_env env, + void* nativeObject, + void* /*finalize_hint*/) { + MyObject* obj = static_cast(nativeObject); + delete obj; +} + +napi_ref MyObject::constructor; + +napi_status MyObject::Init(napi_env env) { + napi_status status; + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("plusOne", PlusOne), + }; + + napi_value cons; + status = + napi_define_class(env, "MyObject", New, nullptr, 1, properties, &cons); + if (status != napi_ok) return status; + + status = napi_create_reference(env, cons, 1, &constructor); + if (status != napi_ok) return status; + + return napi_ok; +} + +napi_value MyObject::New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + MyObject* obj = new MyObject(); + + if (valuetype == napi_undefined) { + obj->counter_ = 0; + } else { + NAPI_CALL(env, napi_get_value_double(env, args[0], &obj->counter_)); + } + + obj->env_ = env; + NAPI_CALL(env, napi_wrap(env, + _this, + obj, + MyObject::Destructor, + nullptr, /* finalize_hint */ + &obj->wrapper_)); + + return _this; +} + +napi_status MyObject::NewInstance(napi_env env, + napi_value arg, + napi_value* instance) { + napi_status status; + + const int argc = 1; + napi_value argv[argc] = {arg}; + + napi_value cons; + status = napi_get_reference_value(env, constructor, &cons); + if (status != napi_ok) return status; + + status = napi_new_instance(env, cons, argc, argv, instance); + if (status != napi_ok) return status; + + return napi_ok; +} + +napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) { + napi_value _this; + NAPI_CALL(env, + napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + MyObject* obj; + NAPI_CALL(env, napi_unwrap(env, _this, reinterpret_cast(&obj))); + + obj->counter_ += 1; + + napi_value num; + NAPI_CALL(env, napi_create_number(env, obj->counter_, &num)); + + return num; +} diff --git a/test/addons-napi/7_factory_wrap/myobject.h b/test/addons-napi/7_factory_wrap/myobject.h new file mode 100644 index 0000000000..c0b8522c60 --- /dev/null +++ b/test/addons-napi/7_factory_wrap/myobject.h @@ -0,0 +1,26 @@ +#ifndef TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ +#define TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ + +#include + +class MyObject { + public: + static napi_status Init(napi_env env); + static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); + static napi_status NewInstance(napi_env env, + napi_value arg, + napi_value* instance); + + private: + MyObject(); + ~MyObject(); + + static napi_ref constructor; + static napi_value New(napi_env env, napi_callback_info info); + static napi_value PlusOne(napi_env env, napi_callback_info info); + double counter_; + napi_env env_; + napi_ref wrapper_; +}; + +#endif // TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ diff --git a/test/addons-napi/7_factory_wrap/test.js b/test/addons-napi/7_factory_wrap/test.js new file mode 100644 index 0000000000..20108e14b6 --- /dev/null +++ b/test/addons-napi/7_factory_wrap/test.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const createObject = require(`./build/${common.buildType}/binding`); + +const obj = createObject(10); +assert.strictEqual(obj.plusOne(), 11); +assert.strictEqual(obj.plusOne(), 12); +assert.strictEqual(obj.plusOne(), 13); + +const obj2 = createObject(20); +assert.strictEqual(obj2.plusOne(), 21); +assert.strictEqual(obj2.plusOne(), 22); +assert.strictEqual(obj2.plusOne(), 23); diff --git a/test/addons-napi/8_passing_wrapped/binding.cc b/test/addons-napi/8_passing_wrapped/binding.cc new file mode 100644 index 0000000000..581638bdfa --- /dev/null +++ b/test/addons-napi/8_passing_wrapped/binding.cc @@ -0,0 +1,44 @@ +#include "myobject.h" +#include "../common.h" + +napi_value CreateObject(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + napi_value instance; + NAPI_CALL(env, MyObject::NewInstance(env, args[0], &instance)); + + return instance; +} + +napi_value Add(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + MyObject* obj1; + NAPI_CALL(env, napi_unwrap(env, args[0], reinterpret_cast(&obj1))); + + MyObject* obj2; + NAPI_CALL(env, napi_unwrap(env, args[1], reinterpret_cast(&obj2))); + + napi_value sum; + NAPI_CALL(env, napi_create_number(env, obj1->Val() + obj2->Val(), &sum)); + + return sum; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + MyObject::Init(env); + + napi_property_descriptor desc[] = { + DECLARE_NAPI_PROPERTY("createObject", CreateObject), + DECLARE_NAPI_PROPERTY("add", Add), + }; + + NAPI_CALL_RETURN_VOID(env, + napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/8_passing_wrapped/binding.gyp b/test/addons-napi/8_passing_wrapped/binding.gyp similarity index 100% rename from test/addons-abi/8_passing_wrapped/binding.gyp rename to test/addons-napi/8_passing_wrapped/binding.gyp diff --git a/test/addons-napi/8_passing_wrapped/myobject.cc b/test/addons-napi/8_passing_wrapped/myobject.cc new file mode 100644 index 0000000000..0c24d7696e --- /dev/null +++ b/test/addons-napi/8_passing_wrapped/myobject.cc @@ -0,0 +1,73 @@ +#include "myobject.h" +#include "../common.h" + +MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} + +MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); } + +void MyObject::Destructor( + napi_env env, void* nativeObject, void* /*finalize_hint*/) { + MyObject* obj = static_cast(nativeObject); + delete obj; +} + +napi_ref MyObject::constructor; + +napi_status MyObject::Init(napi_env env) { + napi_status status; + + napi_value cons; + status = napi_define_class(env, "MyObject", New, nullptr, 0, nullptr, &cons); + if (status != napi_ok) return status; + + status = napi_create_reference(env, cons, 1, &constructor); + if (status != napi_ok) return status; + + return napi_ok; +} + +napi_value MyObject::New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + MyObject* obj = new MyObject(); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + if (valuetype == napi_undefined) { + obj->val_ = 0; + } else { + NAPI_CALL(env, napi_get_value_double(env, args[0], &obj->val_)); + } + + obj->env_ = env; + NAPI_CALL(env, napi_wrap(env, + _this, + obj, + MyObject::Destructor, + nullptr, // finalize_hint + &obj->wrapper_)); + + return _this; +} + +napi_status MyObject::NewInstance(napi_env env, + napi_value arg, + napi_value* instance) { + napi_status status; + + const int argc = 1; + napi_value argv[argc] = {arg}; + + napi_value cons; + status = napi_get_reference_value(env, constructor, &cons); + if (status != napi_ok) return status; + + status = napi_new_instance(env, cons, argc, argv, instance); + if (status != napi_ok) return status; + + return napi_ok; +} diff --git a/test/addons-napi/8_passing_wrapped/myobject.h b/test/addons-napi/8_passing_wrapped/myobject.h new file mode 100644 index 0000000000..7c6a35aa85 --- /dev/null +++ b/test/addons-napi/8_passing_wrapped/myobject.h @@ -0,0 +1,26 @@ +#ifndef TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ +#define TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ + +#include + +class MyObject { + public: + static napi_status Init(napi_env env); + static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); + static napi_status NewInstance(napi_env env, + napi_value arg, + napi_value* instance); + double Val() const { return val_; } + + private: + MyObject(); + ~MyObject(); + + static napi_ref constructor; + static napi_value New(napi_env env, napi_callback_info info); + double val_; + napi_env env_; + napi_ref wrapper_; +}; + +#endif // TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ diff --git a/test/addons-napi/8_passing_wrapped/test.js b/test/addons-napi/8_passing_wrapped/test.js new file mode 100644 index 0000000000..3d24fa5d9f --- /dev/null +++ b/test/addons-napi/8_passing_wrapped/test.js @@ -0,0 +1,9 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const addon = require(`./build/${common.buildType}/binding`); + +const obj1 = addon.createObject(10); +const obj2 = addon.createObject(20); +const result = addon.add(obj1, obj2); +assert.strictEqual(result, 30); diff --git a/test/addons-napi/common.h b/test/addons-napi/common.h new file mode 100644 index 0000000000..b06ece6e39 --- /dev/null +++ b/test/addons-napi/common.h @@ -0,0 +1,52 @@ +// Empty value so that macros here are able to return NULL or void +#define NAPI_RETVAL_NOTHING // Intentionally blank #define + +#define GET_AND_THROW_LAST_ERROR(env) \ + do { \ + bool is_pending; \ + napi_is_exception_pending((env), &is_pending); \ + /* If an exception is already pending, don't rethrow it */ \ + if (!is_pending) { \ + const napi_extended_error_info* error_info; \ + napi_get_last_error_info((env), &error_info); \ + const char* error_message = error_info->error_message != NULL ? \ + error_info->error_message : \ + "empty error message"; \ + napi_throw_error((env), error_message); \ + } \ + } while(0); + +#define NAPI_ASSERT_BASE(env, assertion, message, ret_val) \ + if (!(assertion)) { \ + napi_throw_error( \ + (env), \ + "assertion (" #assertion ") failed: " message); \ + return ret_val; \ + } + +// Returns NULL on failed assertion. +// This is meant to be used inside napi_callback methods. +#define NAPI_ASSERT(env, assertion, message) \ + NAPI_ASSERT_BASE(env, assertion, message, NULL) + +// Returns empty on failed assertion. +// This is meant to be used inside functions with void return type. +#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) \ + NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING) + +#define NAPI_CALL_BASE(env, the_call, ret_val) \ + if ((the_call) != napi_ok) { \ + GET_AND_THROW_LAST_ERROR((env)); \ + return ret_val; \ + } + +// Returns NULL if the_call doesn't return napi_ok. +#define NAPI_CALL(env, the_call) \ + NAPI_CALL_BASE(env, the_call, NULL) + +// Returns empty if the_call doesn't return napi_ok. +#define NAPI_CALL_RETURN_VOID(env, the_call) \ + NAPI_CALL_BASE(env, the_call, NAPI_RETVAL_NOTHING) + +#define DECLARE_NAPI_PROPERTY(name, func) \ + { (name), 0, (func), 0, 0, 0, napi_default, 0 } diff --git a/test/addons-abi/test_array/binding.gyp b/test/addons-napi/test_array/binding.gyp similarity index 65% rename from test/addons-abi/test_array/binding.gyp rename to test/addons-napi/test_array/binding.gyp index f277e2893d..44920de098 100644 --- a/test/addons-abi/test_array/binding.gyp +++ b/test/addons-napi/test_array/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_array", - "sources": [ "test_array.cc" ] + "sources": [ "test_array.c" ] } ] } diff --git a/test/addons-napi/test_array/test.js b/test/addons-napi/test_array/test.js new file mode 100644 index 0000000000..c2759b0072 --- /dev/null +++ b/test/addons-napi/test_array/test.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// Testing api calls for arrays +const test_array = require(`./build/${common.buildType}/test_array`); + +const array = [ + 1, + 9, + 48, + 13493, + 9459324, + { name: 'hello' }, + [ + 'world', + 'node', + 'abi' + ] +]; + +assert.strictEqual(test_array.Test(array, array.length + 1), + 'Index out of bound!'); + +assert.throws( + () => { + test_array.Test(array, -2); + }, + /Invalid index\. Expects a positive integer\./ +); + +array.forEach(function(element, index) { + assert.strictEqual(test_array.Test(array, index), element); +}); + + +assert.deepStrictEqual(test_array.New(array), array); diff --git a/test/addons-napi/test_array/test_array.c b/test/addons-napi/test_array/test_array.c new file mode 100644 index 0000000000..3634a85e3c --- /dev/null +++ b/test/addons-napi/test_array/test_array.c @@ -0,0 +1,93 @@ +#include +#include +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an array as first argument."); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NAPI_ASSERT(env, valuetype1 == napi_number, + "Wrong type of arguments. Expects an integer as second argument."); + + napi_value array = args[0]; + int32_t index; + NAPI_CALL(env, napi_get_value_int32(env, args[1], &index)); + + NAPI_ASSERT(env, index >= 0, "Invalid index. Expects a positive integer."); + + bool isarray; + NAPI_CALL(env, napi_is_array(env, array, &isarray)); + + if (!isarray) { + return NULL; + } + + uint32_t length; + NAPI_CALL(env, napi_get_array_length(env, array, &length)); + + if ((uint32_t)index >= length) { + napi_value str; + const char* str_val = "Index out of bound!"; + size_t str_len = strlen(str_val); + NAPI_CALL(env, napi_create_string_utf8(env, str_val, str_len, &str)); + + return str; + } + + napi_value ret; + NAPI_CALL(env, napi_get_element(env, array, index, &ret)); + + return ret; +} + +napi_value New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an array as first argument."); + + napi_value ret; + NAPI_CALL(env, napi_create_array(env, &ret)); + + uint32_t i, length; + NAPI_CALL(env, napi_get_array_length(env, args[0], &length)); + + for (i = 0; i < length; i++) { + napi_value e; + NAPI_CALL(env, napi_get_element(env, args[0], i, &e)); + NAPI_CALL(env, napi_set_element(env, ret, i, e)); + } + + return ret; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("Test", Test), + DECLARE_NAPI_PROPERTY("New", New), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-napi/test_async/binding.gyp b/test/addons-napi/test_async/binding.gyp new file mode 100644 index 0000000000..cf8beb70c6 --- /dev/null +++ b/test/addons-napi/test_async/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_async", + "sources": [ "test_async.cc" ] + } + ] +} diff --git a/test/addons-napi/test_async/test.js b/test/addons-napi/test_async/test.js new file mode 100644 index 0000000000..0bfd955ac7 --- /dev/null +++ b/test/addons-napi/test_async/test.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test_async = require(`./build/${common.buildType}/test_async`); + +test_async(5, common.mustCall(function(err, val) { + assert.strictEqual(err, null); + assert.strictEqual(val, 10); + process.nextTick(common.mustCall(function() {})); +})); diff --git a/test/addons-napi/test_async/test_async.cc b/test/addons-napi/test_async/test_async.cc new file mode 100644 index 0000000000..bc0af66d26 --- /dev/null +++ b/test/addons-napi/test_async/test_async.cc @@ -0,0 +1,122 @@ +#include +#include "../common.h" + +#if defined _WIN32 +#include +#else +#include +#endif + +typedef struct { + int32_t _input; + int32_t _output; + napi_ref _callback; + napi_async_work _request; +} carrier; + +carrier the_carrier; + +struct AutoHandleScope { + explicit AutoHandleScope(napi_env env) + : _env(env), + _scope(nullptr) { + napi_open_handle_scope(_env, &_scope); + } + ~AutoHandleScope() { + napi_close_handle_scope(_env, _scope); + } + private: + AutoHandleScope() { } + + napi_env _env; + napi_handle_scope _scope; +}; + +void Execute(napi_env env, void* data) { +#if defined _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + carrier* c = static_cast(data); + + if (c != &the_carrier) { + napi_throw_type_error(env, "Wrong data parameter to Execute."); + return; + } + + c->_output = c->_input * 2; +} + +void Complete(napi_env env, napi_status status, void* data) { + AutoHandleScope scope(env); + carrier* c = static_cast(data); + + if (c != &the_carrier) { + napi_throw_type_error(env, "Wrong data parameter to Complete."); + return; + } + + if (status != napi_ok) { + napi_throw_type_error(env, "Execute callback failed."); + return; + } + + napi_value argv[2]; + + NAPI_CALL_RETURN_VOID(env, napi_get_null(env, &argv[0])); + NAPI_CALL_RETURN_VOID(env, napi_create_number(env, c->_output, &argv[1])); + napi_value callback; + NAPI_CALL_RETURN_VOID(env, + napi_get_reference_value(env, c->_callback, &callback)); + napi_value global; + NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global)); + + napi_value result; + NAPI_CALL_RETURN_VOID(env, + napi_call_function(env, global, callback, 2, argv, &result)); + + NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, c->_callback)); + NAPI_CALL_RETURN_VOID(env, napi_delete_async_work(env, c->_request)); +} + +napi_value Test(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value argv[2]; + napi_value _this; + void* data; + NAPI_CALL(env, + napi_get_cb_info(env, info, &argc, argv, &_this, &data)); + NAPI_ASSERT(env, argc >= 2, "Not enough arguments, expected 2."); + + napi_valuetype t; + NAPI_CALL(env, napi_typeof(env, argv[0], &t)); + NAPI_ASSERT(env, t == napi_number, + "Wrong first argument, integer expected."); + NAPI_CALL(env, napi_typeof(env, argv[1], &t)); + NAPI_ASSERT(env, t == napi_function, + "Wrong second argument, function expected."); + + the_carrier._output = 0; + + NAPI_CALL(env, + napi_get_value_int32(env, argv[0], &the_carrier._input)); + NAPI_CALL(env, + napi_create_reference(env, argv[1], 1, &the_carrier._callback)); + NAPI_CALL(env, napi_create_async_work( + env, Execute, Complete, &the_carrier, &the_carrier._request)); + NAPI_CALL(env, + napi_queue_async_work(env, the_carrier._request)); + + return nullptr; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_value test; + NAPI_CALL_RETURN_VOID(env, + napi_create_function(env, "Test", Test, nullptr, &test)); + NAPI_CALL_RETURN_VOID(env, + napi_set_named_property(env, module, "exports", test)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_buffer/binding.gyp b/test/addons-napi/test_buffer/binding.gyp similarity index 65% rename from test/addons-abi/test_buffer/binding.gyp rename to test/addons-napi/test_buffer/binding.gyp index 711edd4f79..e41a3993cd 100644 --- a/test/addons-abi/test_buffer/binding.gyp +++ b/test/addons-napi/test_buffer/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_buffer", - "sources": [ "test_buffer.cc" ] + "sources": [ "test_buffer.c" ] } ] } diff --git a/test/addons-napi/test_buffer/test.js b/test/addons-napi/test_buffer/test.js new file mode 100644 index 0000000000..6fb80b0205 --- /dev/null +++ b/test/addons-napi/test_buffer/test.js @@ -0,0 +1,25 @@ +'use strict'; +// Flags: --expose-gc + +const common = require('../../common'); +const binding = require(`./build/${common.buildType}/test_buffer`); +const assert = require('assert'); + +assert.strictEqual(binding.newBuffer().toString(), binding.theText, + 'buffer returned by newBuffer() has wrong contents'); +assert.strictEqual(binding.newExternalBuffer().toString(), binding.theText, + 'buffer returned by newExternalBuffer() has wrong contents'); +console.log('gc1'); +global.gc(); +assert.strictEqual(binding.getDeleterCallCount(), 1, 'deleter was not called'); +assert.strictEqual(binding.copyBuffer().toString(), binding.theText, + 'buffer returned by copyBuffer() has wrong contents'); + +let buffer = binding.staticBuffer(); +assert.strictEqual(binding.bufferHasInstance(buffer), true, + 'buffer type checking fails'); +assert.strictEqual(binding.bufferInfo(buffer), true, 'buffer data is accurate'); +buffer = null; +global.gc(); +console.log('gc2'); +assert.strictEqual(binding.getDeleterCallCount(), 2, 'deleter was not called'); diff --git a/test/addons-napi/test_buffer/test_buffer.c b/test/addons-napi/test_buffer/test_buffer.c new file mode 100644 index 0000000000..880149e1ff --- /dev/null +++ b/test/addons-napi/test_buffer/test_buffer.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include "../common.h" + +static const char theText[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + +static int deleterCallCount = 0; +static void deleteTheText(napi_env env, void* data, void* finalize_hint) { + NAPI_ASSERT_RETURN_VOID(env, data != NULL && strcmp(data, theText) == 0, "invalid data"); + (void)finalize_hint; + free(data); + deleterCallCount++; +} + +static void noopDeleter(napi_env env, void* data, void* finalize_hint) { + NAPI_ASSERT_RETURN_VOID(env, data != NULL && strcmp(data, theText) == 0, "invalid data"); + (void)finalize_hint; + deleterCallCount++; +} + +napi_value newBuffer(napi_env env, napi_callback_info info) { + napi_value theBuffer; + char* theCopy; + const unsigned int kBufferSize = sizeof(theText); + + NAPI_CALL(env, + napi_create_buffer( + env, + sizeof(theText), + (void**)(&theCopy), + &theBuffer)); + NAPI_ASSERT(env, theCopy, "Failed to copy static text for newBuffer"); + memcpy(theCopy, theText, kBufferSize); + + return theBuffer; +} + +napi_value newExternalBuffer(napi_env env, napi_callback_info info) { + napi_value theBuffer; + char* theCopy = strdup(theText); + NAPI_ASSERT(env, theCopy, "Failed to copy static text for newExternalBuffer"); + NAPI_CALL(env, + napi_create_external_buffer( + env, + sizeof(theText), + theCopy, + deleteTheText, + NULL, // finalize_hint + &theBuffer)); + + return theBuffer; +} + +napi_value getDeleterCallCount(napi_env env, napi_callback_info info) { + napi_value callCount; + NAPI_CALL(env, napi_create_number(env, deleterCallCount, &callCount)); + return callCount; +} + +napi_value copyBuffer(napi_env env, napi_callback_info info) { + napi_value theBuffer; + NAPI_CALL(env, napi_create_buffer_copy( + env, sizeof(theText), theText, NULL, &theBuffer)); + return theBuffer; +} + +napi_value bufferHasInstance(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + napi_value theBuffer = args[0]; + bool hasInstance; + napi_valuetype theType; + NAPI_CALL(env, napi_typeof(env, theBuffer, &theType)); + NAPI_ASSERT(env, + theType == napi_object, + "bufferHasInstance: instance is not an object"); + NAPI_CALL(env, napi_is_buffer(env, theBuffer, &hasInstance)); + NAPI_ASSERT(env, hasInstance, "bufferHasInstance: instance is not a buffer"); + napi_value returnValue; + NAPI_CALL(env, napi_get_boolean(env, hasInstance, &returnValue)); + return returnValue; +} + +napi_value bufferInfo(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + napi_value theBuffer = args[0]; + char *bufferData; + napi_value returnValue; + size_t bufferLength; + NAPI_CALL(env, + napi_get_buffer_info( + env, + theBuffer, + (void**)(&bufferData), + &bufferLength)); + NAPI_CALL(env, napi_get_boolean(env, + !strcmp(bufferData, theText) && bufferLength == sizeof(theText), + &returnValue)); + return returnValue; +} + +napi_value staticBuffer(napi_env env, napi_callback_info info) { + napi_value theBuffer; + NAPI_CALL( + env, + napi_create_external_buffer(env, + sizeof(theText), + (void*)theText, + noopDeleter, + NULL, // finalize_hint + &theBuffer)); + return theBuffer; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_value theValue; + + NAPI_CALL_RETURN_VOID(env, + napi_create_string_utf8(env, theText, sizeof(theText), &theValue)); + NAPI_CALL_RETURN_VOID(env, + napi_set_named_property(env, exports, "theText", theValue)); + + napi_property_descriptor methods[] = { + DECLARE_NAPI_PROPERTY("newBuffer", newBuffer), + DECLARE_NAPI_PROPERTY("newExternalBuffer", newExternalBuffer), + DECLARE_NAPI_PROPERTY("getDeleterCallCount", getDeleterCallCount), + DECLARE_NAPI_PROPERTY("copyBuffer", copyBuffer), + DECLARE_NAPI_PROPERTY("bufferHasInstance", bufferHasInstance), + DECLARE_NAPI_PROPERTY("bufferInfo", bufferInfo), + DECLARE_NAPI_PROPERTY("staticBuffer", staticBuffer), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(methods) / sizeof(methods[0]), methods)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_constructor/binding.gyp b/test/addons-napi/test_constructor/binding.gyp similarity index 63% rename from test/addons-abi/test_constructor/binding.gyp rename to test/addons-napi/test_constructor/binding.gyp index 0ea8f87738..55140e7c37 100644 --- a/test/addons-abi/test_constructor/binding.gyp +++ b/test/addons-napi/test_constructor/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_constructor", - "sources": [ "test_constructor.cc" ] + "sources": [ "test_constructor.c" ] } ] } diff --git a/test/addons-napi/test_constructor/test.js b/test/addons-napi/test_constructor/test.js new file mode 100644 index 0000000000..92440bf49e --- /dev/null +++ b/test/addons-napi/test_constructor/test.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// Testing api calls for a constructor that defines properties +const TestConstructor = require(`./build/${common.buildType}/test_constructor`); +const test_object = new TestConstructor(); + +assert.strictEqual(test_object.echo('hello'), 'hello'); + +test_object.readwriteValue = 1; +assert.strictEqual(test_object.readwriteValue, 1); +test_object.readwriteValue = 2; +assert.strictEqual(test_object.readwriteValue, 2); + +assert.throws(() => { test_object.readonlyValue = 3; }, TypeError); + +assert.ok(test_object.hiddenValue); + +// Properties with napi_enumerable attribute should be enumerable. +const propertyNames = []; +for (const name in test_object) { + propertyNames.push(name); +} +assert.ok(propertyNames.indexOf('echo') >= 0); +assert.ok(propertyNames.indexOf('readwriteValue') >= 0); +assert.ok(propertyNames.indexOf('readonlyValue') >= 0); +assert.ok(propertyNames.indexOf('hiddenValue') < 0); +assert.ok(propertyNames.indexOf('readwriteAccessor1') < 0); +assert.ok(propertyNames.indexOf('readwriteAccessor2') < 0); +assert.ok(propertyNames.indexOf('readonlyAccessor1') < 0); +assert.ok(propertyNames.indexOf('readonlyAccessor2') < 0); + +// The napi_writable attribute should be ignored for accessors. +test_object.readwriteAccessor1 = 1; +assert.strictEqual(test_object.readwriteAccessor1, 1); +assert.strictEqual(test_object.readonlyAccessor1, 1); +assert.throws(() => { test_object.readonlyAccessor1 = 3; }, TypeError); +test_object.readwriteAccessor2 = 2; +assert.strictEqual(test_object.readwriteAccessor2, 2); +assert.strictEqual(test_object.readonlyAccessor2, 2); +assert.throws(() => { test_object.readonlyAccessor2 = 3; }, TypeError); diff --git a/test/addons-napi/test_constructor/test_constructor.c b/test/addons-napi/test_constructor/test_constructor.c new file mode 100644 index 0000000000..0a73010d72 --- /dev/null +++ b/test/addons-napi/test_constructor/test_constructor.c @@ -0,0 +1,74 @@ +#include +#include "../common.h" + +static double value_ = 1; +napi_ref constructor_; + +napi_value GetValue(napi_env env, napi_callback_info info) { + size_t argc = 0; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); + + NAPI_ASSERT(env, argc == 0, "Wrong number of arguments"); + + napi_value number; + NAPI_CALL(env, napi_create_number(env, value_, &number)); + + return number; +} + +napi_value SetValue(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + + NAPI_CALL(env, napi_get_value_double(env, args[0], &value_)); + + return NULL; +} + +napi_value Echo(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + + return args[0]; +} + +napi_value New(napi_env env, napi_callback_info info) { + napi_value _this; + NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL)); + + return _this; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_value number; + NAPI_CALL_RETURN_VOID(env, napi_create_number(env, value_, &number)); + + napi_property_descriptor properties[] = { + { "echo", 0, Echo, 0, 0, 0, napi_enumerable, 0 }, + { "readwriteValue", 0, 0, 0, 0, number, napi_enumerable | napi_writable, 0 }, + { "readonlyValue", 0, 0, 0, 0, number, napi_enumerable, 0}, + { "hiddenValue", 0, 0, 0, 0, number, napi_default, 0}, + { "readwriteAccessor1", 0, 0, GetValue, SetValue, 0, napi_default, 0}, + { "readwriteAccessor2", 0, 0, GetValue, SetValue, 0, napi_writable, 0}, + { "readonlyAccessor1", 0, 0, GetValue, NULL, 0, napi_default, 0}, + { "readonlyAccessor2", 0, 0, GetValue, NULL, 0, napi_writable, 0}, + }; + + napi_value cons; + NAPI_CALL_RETURN_VOID(env, napi_define_class(env, "MyObject", New, + NULL, sizeof(properties)/sizeof(*properties), properties, &cons)); + + NAPI_CALL_RETURN_VOID(env, + napi_set_named_property(env, module, "exports", cons)); + + NAPI_CALL_RETURN_VOID(env, + napi_create_reference(env, cons, 1, &constructor_)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-napi/test_conversions/binding.gyp b/test/addons-napi/test_conversions/binding.gyp new file mode 100644 index 0000000000..8d8d6fc012 --- /dev/null +++ b/test/addons-napi/test_conversions/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_conversions", + "sources": [ "test_conversions.c" ] + } + ] +} diff --git a/test/addons-napi/test_conversions/test.js b/test/addons-napi/test_conversions/test.js new file mode 100644 index 0000000000..73d2c3314f --- /dev/null +++ b/test/addons-napi/test_conversions/test.js @@ -0,0 +1,140 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test = require(`./build/${common.buildType}/test_conversions`); + +const boolExpected = /boolean was expected/; +const numberExpected = /number was expected/; +const stringExpected = /string was expected/; + +const testSym = Symbol('test'); + +assert.strictEqual(false, test.asBool(false)); +assert.strictEqual(true, test.asBool(true)); +assert.throws(() => test.asBool(undefined), boolExpected); +assert.throws(() => test.asBool(null), boolExpected); +assert.throws(() => test.asBool(Number.NaN), boolExpected); +assert.throws(() => test.asBool(0), boolExpected); +assert.throws(() => test.asBool(''), boolExpected); +assert.throws(() => test.asBool('0'), boolExpected); +assert.throws(() => test.asBool(1), boolExpected); +assert.throws(() => test.asBool('1'), boolExpected); +assert.throws(() => test.asBool('true'), boolExpected); +assert.throws(() => test.asBool({}), boolExpected); +assert.throws(() => test.asBool([]), boolExpected); +assert.throws(() => test.asBool(testSym), boolExpected); + +[test.asInt32, test.asUInt32, test.asInt64].forEach((asInt) => { + assert.strictEqual(0, asInt(0)); + assert.strictEqual(1, asInt(1)); + assert.strictEqual(1, asInt(1.0)); + assert.strictEqual(1, asInt(1.1)); + assert.strictEqual(1, asInt(1.9)); + assert.strictEqual(0, asInt(0.9)); + assert.strictEqual(999, asInt(999.9)); + assert.strictEqual(0, asInt(Number.NaN)); + assert.throws(() => asInt(undefined), numberExpected); + assert.throws(() => asInt(null), numberExpected); + assert.throws(() => asInt(false), numberExpected); + assert.throws(() => asInt(''), numberExpected); + assert.throws(() => asInt('1'), numberExpected); + assert.throws(() => asInt({}), numberExpected); + assert.throws(() => asInt([]), numberExpected); + assert.throws(() => asInt(testSym), numberExpected); +}); + +assert.strictEqual(-1, test.asInt32(-1)); +assert.strictEqual(-1, test.asInt64(-1)); +assert.strictEqual(Math.pow(2, 32) - 1, test.asUInt32(-1)); + +assert.strictEqual(0, test.asDouble(0)); +assert.strictEqual(1, test.asDouble(1)); +assert.strictEqual(1.0, test.asDouble(1.0)); +assert.strictEqual(1.1, test.asDouble(1.1)); +assert.strictEqual(1.9, test.asDouble(1.9)); +assert.strictEqual(0.9, test.asDouble(0.9)); +assert.strictEqual(999.9, test.asDouble(999.9)); +assert.strictEqual(-1, test.asDouble(-1)); +assert.ok(Number.isNaN(test.asDouble(Number.NaN))); +assert.throws(() => test.asDouble(undefined), numberExpected); +assert.throws(() => test.asDouble(null), numberExpected); +assert.throws(() => test.asDouble(false), numberExpected); +assert.throws(() => test.asDouble(''), numberExpected); +assert.throws(() => test.asDouble('1'), numberExpected); +assert.throws(() => test.asDouble({}), numberExpected); +assert.throws(() => test.asDouble([]), numberExpected); +assert.throws(() => test.asDouble(testSym), numberExpected); + +assert.strictEqual('', test.asString('')); +assert.strictEqual('test', test.asString('test')); +assert.throws(() => test.asString(undefined), stringExpected); +assert.throws(() => test.asString(null), stringExpected); +assert.throws(() => test.asString(false), stringExpected); +assert.throws(() => test.asString(1), stringExpected); +assert.throws(() => test.asString(1.1), stringExpected); +assert.throws(() => test.asString(Number.NaN), stringExpected); +assert.throws(() => test.asString({}), stringExpected); +assert.throws(() => test.asString([]), stringExpected); +assert.throws(() => test.asString(testSym), stringExpected); + +assert.strictEqual(true, test.toBool(true)); +assert.strictEqual(true, test.toBool(1)); +assert.strictEqual(true, test.toBool(-1)); +assert.strictEqual(true, test.toBool('true')); +assert.strictEqual(true, test.toBool('false')); +assert.strictEqual(true, test.toBool({})); +assert.strictEqual(true, test.toBool([])); +assert.strictEqual(true, test.toBool(testSym)); +assert.strictEqual(false, test.toBool(false)); +assert.strictEqual(false, test.toBool(undefined)); +assert.strictEqual(false, test.toBool(null)); +assert.strictEqual(false, test.toBool(0)); +assert.strictEqual(false, test.toBool(Number.NaN)); +assert.strictEqual(false, test.toBool('')); + +assert.strictEqual(0, test.toNumber(0)); +assert.strictEqual(1, test.toNumber(1)); +assert.strictEqual(1.1, test.toNumber(1.1)); +assert.strictEqual(-1, test.toNumber(-1)); +assert.strictEqual(0, test.toNumber('0')); +assert.strictEqual(1, test.toNumber('1')); +assert.strictEqual(1.1, test.toNumber('1.1')); +assert.strictEqual(0, test.toNumber([])); +assert.strictEqual(0, test.toNumber(false)); +assert.strictEqual(0, test.toNumber(null)); +assert.strictEqual(0, test.toNumber('')); +assert.ok(Number.isNaN(test.toNumber(Number.NaN))); +assert.ok(Number.isNaN(test.toNumber({}))); +assert.ok(Number.isNaN(test.toNumber(undefined))); +assert.throws(() => test.toNumber(testSym), TypeError); + +assert.deepStrictEqual({}, test.toObject({})); +assert.deepStrictEqual({ 'test': 1 }, test.toObject({ 'test': 1 })); +assert.deepStrictEqual([], test.toObject([])); +assert.deepStrictEqual([ 1, 2, 3 ], test.toObject([ 1, 2, 3 ])); +assert.deepStrictEqual(new Boolean(false), test.toObject(false)); +assert.deepStrictEqual(new Boolean(true), test.toObject(true)); +assert.deepStrictEqual(new String(''), test.toObject('')); +assert.deepStrictEqual(new Number(0), test.toObject(0)); +assert.deepStrictEqual(new Number(Number.NaN), test.toObject(Number.NaN)); +assert.deepStrictEqual(new Object(testSym), test.toObject(testSym)); +assert.notDeepStrictEqual(false, test.toObject(false)); +assert.notDeepStrictEqual(true, test.toObject(true)); +assert.notDeepStrictEqual('', test.toObject('')); +assert.notDeepStrictEqual(0, test.toObject(0)); +assert.ok(!Number.isNaN(test.toObject(Number.NaN))); + +assert.strictEqual('', test.toString('')); +assert.strictEqual('test', test.toString('test')); +assert.strictEqual('undefined', test.toString(undefined)); +assert.strictEqual('null', test.toString(null)); +assert.strictEqual('false', test.toString(false)); +assert.strictEqual('true', test.toString(true)); +assert.strictEqual('0', test.toString(0)); +assert.strictEqual('1.1', test.toString(1.1)); +assert.strictEqual('NaN', test.toString(Number.NaN)); +assert.strictEqual('[object Object]', test.toString({})); +assert.strictEqual('test', test.toString({ toString: () => 'test' })); +assert.strictEqual('', test.toString([])); +assert.strictEqual('1,2,3', test.toString([ 1, 2, 3 ])); +assert.throws(() => test.toString(testSym), TypeError); diff --git a/test/addons-napi/test_conversions/test_conversions.c b/test/addons-napi/test_conversions/test_conversions.c new file mode 100644 index 0000000000..637cff43b8 --- /dev/null +++ b/test/addons-napi/test_conversions/test_conversions.c @@ -0,0 +1,151 @@ +#include +#include "../common.h" + +napi_value AsBool(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + bool value; + NAPI_CALL(env, napi_get_value_bool(env, args[0], &value)); + + napi_value output; + NAPI_CALL(env, napi_get_boolean(env, value, &output)); + + return output; +} + +napi_value AsInt32(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + int32_t value; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &value)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, value, &output)); + + return output; +} + +napi_value AsUInt32(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + uint32_t value; + NAPI_CALL(env, napi_get_value_uint32(env, args[0], &value)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, value, &output)); + + return output; +} + +napi_value AsInt64(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + int64_t value; + NAPI_CALL(env, napi_get_value_int64(env, args[0], &value)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, (double)value, &output)); + + return output; +} + +napi_value AsDouble(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + double value; + NAPI_CALL(env, napi_get_value_double(env, args[0], &value)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, value, &output)); + + return output; +} + +napi_value AsString(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + char value[100]; + NAPI_CALL(env, + napi_get_value_string_utf8(env, args[0], value, sizeof(value), NULL)); + + napi_value output; + NAPI_CALL(env, napi_create_string_utf8(env, value, -1, &output)); + + return output; +} + +napi_value ToBool(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value output; + NAPI_CALL(env, napi_coerce_to_bool(env, args[0], &output)); + + return output; +} + +napi_value ToNumber(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value output; + NAPI_CALL(env, napi_coerce_to_number(env, args[0], &output)); + + return output; +} + +napi_value ToObject(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value output; + NAPI_CALL(env, napi_coerce_to_object(env, args[0], &output)); + + return output; +} + +napi_value ToString(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value output; + NAPI_CALL(env, napi_coerce_to_string(env, args[0], &output)); + + return output; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("asBool", AsBool), + DECLARE_NAPI_PROPERTY("asInt32", AsInt32), + DECLARE_NAPI_PROPERTY("asUInt32", AsUInt32), + DECLARE_NAPI_PROPERTY("asInt64", AsInt64), + DECLARE_NAPI_PROPERTY("asDouble", AsDouble), + DECLARE_NAPI_PROPERTY("asString", AsString), + DECLARE_NAPI_PROPERTY("toBool", ToBool), + DECLARE_NAPI_PROPERTY("toNumber", ToNumber), + DECLARE_NAPI_PROPERTY("toObject", ToObject), + DECLARE_NAPI_PROPERTY("toString", ToString), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-napi/test_error/binding.gyp b/test/addons-napi/test_error/binding.gyp new file mode 100644 index 0000000000..c2defd9551 --- /dev/null +++ b/test/addons-napi/test_error/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_error", + "sources": [ "test_error.cc" ] + } + ] +} diff --git a/test/addons-napi/test_error/test.js b/test/addons-napi/test_error/test.js new file mode 100644 index 0000000000..521c29250d --- /dev/null +++ b/test/addons-napi/test_error/test.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../../common'); +const test_error = require(`./build/${common.buildType}/test_error`); +const assert = require('assert'); +const theError = new Error('Some error'); +const theTypeError = new TypeError('Some type error'); +const theSyntaxError = new SyntaxError('Some syntax error'); +const theRangeError = new RangeError('Some type error'); +const theReferenceError = new ReferenceError('Some reference error'); +const theURIError = new URIError('Some URI error'); +const theEvalError = new EvalError('Some eval error'); + +class MyError extends Error { } +const myError = new MyError('Some MyError'); + +// Test that native error object is correctly classed +assert.strictEqual(test_error.checkError(theError), true, + 'Error object correctly classed by napi_is_error'); + +// Test that native type error object is correctly classed +assert.strictEqual(test_error.checkError(theTypeError), true, + 'Type error object correctly classed by napi_is_error'); + +// Test that native syntax error object is correctly classed +assert.strictEqual(test_error.checkError(theSyntaxError), true, + 'Syntax error object correctly classed by napi_is_error'); + +// Test that native range error object is correctly classed +assert.strictEqual(test_error.checkError(theRangeError), true, + 'Range error object correctly classed by napi_is_error'); + +// Test that native reference error object is correctly classed +assert.strictEqual(test_error.checkError(theReferenceError), true, + 'Reference error object correctly classed by' + + ' napi_is_error'); + +// Test that native URI error object is correctly classed +assert.strictEqual(test_error.checkError(theURIError), true, + 'URI error object correctly classed by napi_is_error'); + +// Test that native eval error object is correctly classed +assert.strictEqual(test_error.checkError(theEvalError), true, + 'Eval error object correctly classed by napi_is_error'); + +// Test that class derived from native error is correctly classed +assert.strictEqual(test_error.checkError(myError), true, + 'Class derived from native error correctly classed by' + + ' napi_is_error'); + +// Test that non-error object is correctly classed +assert.strictEqual(test_error.checkError({}), false, + 'Non-error object correctly classed by napi_is_error'); + +// Test that non-error primitive is correctly classed +assert.strictEqual(test_error.checkError('non-object'), false, + 'Non-error primitive correctly classed by napi_is_error'); diff --git a/test/addons-napi/test_error/test_error.cc b/test/addons-napi/test_error/test_error.cc new file mode 100644 index 0000000000..eb616cac37 --- /dev/null +++ b/test/addons-napi/test_error/test_error.cc @@ -0,0 +1,27 @@ +#include +#include "../common.h" + +napi_value checkError(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + bool r; + NAPI_CALL(env, napi_is_error(env, args[0], &r)); + + napi_value result; + NAPI_CALL(env, napi_get_boolean(env, r, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("checkError", checkError), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_exception/binding.gyp b/test/addons-napi/test_exception/binding.gyp similarity index 64% rename from test/addons-abi/test_exception/binding.gyp rename to test/addons-napi/test_exception/binding.gyp index 3024b2bff9..d2e4586e46 100644 --- a/test/addons-abi/test_exception/binding.gyp +++ b/test/addons-napi/test_exception/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_exception", - "sources": [ "test_exception.cc" ] + "sources": [ "test_exception.c" ] } ] } diff --git a/test/addons-napi/test_exception/test.js b/test/addons-napi/test_exception/test.js new file mode 100644 index 0000000000..83d2b5000e --- /dev/null +++ b/test/addons-napi/test_exception/test.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../../common'); +const test_exception = require(`./build/${common.buildType}/test_exception`); +const assert = require('assert'); +const theError = new Error('Some error'); +const throwTheError = function() { + throw theError; +}; +let caughtError; + +const throwNoError = function() {}; + +// Test that the native side successfully captures the exception +let returnedError = test_exception.returnException(throwTheError); +assert.strictEqual(theError, returnedError, + 'Returned error is strictly equal to the thrown error'); + +// Test that the native side passes the exception through +assert.throws( + () => { + test_exception.allowException(throwTheError); + }, + function(err) { + return err === theError; + }, + 'Thrown exception was allowed to pass through unhindered' +); + +// Test that the exception thrown above was marked as pending +// before it was handled on the JS side +assert.strictEqual(test_exception.wasPending(), true, + 'VM was marked as having an exception pending' + + ' when it was allowed through'); + +// Test that the native side does not capture a non-existing exception +returnedError = test_exception.returnException(throwNoError); +assert.strictEqual(undefined, returnedError, + 'Returned error is undefined when no exception is thrown'); + +// Test that no exception appears that was not thrown by us +try { + test_exception.allowException(throwNoError); +} catch (anError) { + caughtError = anError; +} +assert.strictEqual(undefined, caughtError, + 'No exception originated on the native side'); + +// Test that the exception state remains clear when no exception is thrown +assert.strictEqual(test_exception.wasPending(), false, + 'VM was not marked as having an exception pending' + + ' when none was allowed through'); diff --git a/test/addons-napi/test_exception/test_exception.c b/test/addons-napi/test_exception/test_exception.c new file mode 100644 index 0000000000..191ac3f8ba --- /dev/null +++ b/test/addons-napi/test_exception/test_exception.c @@ -0,0 +1,59 @@ +#include +#include "../common.h" + +static bool exceptionWasPending = false; + +napi_value returnException(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value global; + NAPI_CALL(env, napi_get_global(env, &global)); + + napi_value result; + napi_status status = napi_call_function(env, global, args[0], 0, 0, &result); + if (status == napi_pending_exception) { + napi_value ex; + NAPI_CALL(env, napi_get_and_clear_last_exception(env, &ex)); + return ex; + } + + return NULL; +} + +napi_value allowException(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value global; + NAPI_CALL(env, napi_get_global(env, &global)); + + napi_value result; + napi_call_function(env, global, args[0], 0, 0, &result); + // Ignore status and check napi_is_exception_pending() instead. + + NAPI_CALL(env, napi_is_exception_pending(env, &exceptionWasPending)); + return NULL; +} + +napi_value wasPending(napi_env env, napi_callback_info info) { + napi_value result; + NAPI_CALL(env, napi_get_boolean(env, exceptionWasPending, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("returnException", returnException), + DECLARE_NAPI_PROPERTY("allowException", allowException), + DECLARE_NAPI_PROPERTY("wasPending", wasPending), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_function/binding.gyp b/test/addons-napi/test_function/binding.gyp similarity index 64% rename from test/addons-abi/test_function/binding.gyp rename to test/addons-napi/test_function/binding.gyp index e58e6e0985..2b015bddd7 100644 --- a/test/addons-abi/test_function/binding.gyp +++ b/test/addons-napi/test_function/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_function", - "sources": [ "test_function.cc" ] + "sources": [ "test_function.c" ] } ] } diff --git a/test/addons-napi/test_function/test.js b/test/addons-napi/test_function/test.js new file mode 100644 index 0000000000..bdb9133adf --- /dev/null +++ b/test/addons-napi/test_function/test.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// testing api calls for function +const test_function = require(`./build/${common.buildType}/test_function`); + + +function func1() { + return 1; +} +assert.strictEqual(test_function.Test(func1), 1); + +function func2() { + console.log('hello world!'); + return null; +} +assert.strictEqual(test_function.Test(func2), null); + +function func3(input) { + return input + 1; +} +assert.strictEqual(test_function.Test(func3, 1), 2); + +function func4(input) { + return func3(input); +} +assert.strictEqual(test_function.Test(func4, 1), 2); diff --git a/test/addons-napi/test_function/test_function.c b/test/addons-napi/test_function/test_function.c new file mode 100644 index 0000000000..928f99c184 --- /dev/null +++ b/test/addons-napi/test_function/test_function.c @@ -0,0 +1,35 @@ +#include +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + size_t argc = 10; + napi_value args[10]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc > 0, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_function, + "Wrong type of arguments. Expects a number as first argument."); + + napi_value* argv = args + 1; + argc = argc - 1; + + napi_value global; + NAPI_CALL(env, napi_get_global(env, &global)); + + napi_value result; + NAPI_CALL(env, napi_call_function(env, global, args[0], argc, argv, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_value fn; + NAPI_CALL_RETURN_VOID(env, napi_create_function(env, NULL, Test, NULL, &fn)); + NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, exports, "Test", fn)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-napi/test_handle_scope/binding.gyp b/test/addons-napi/test_handle_scope/binding.gyp new file mode 100644 index 0000000000..daa96681a5 --- /dev/null +++ b/test/addons-napi/test_handle_scope/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_handle_scope", + "sources": [ "test_handle_scope.c" ] + } + ] +} diff --git a/test/addons-napi/test_handle_scope/test.js b/test/addons-napi/test_handle_scope/test.js new file mode 100644 index 0000000000..ed52410f80 --- /dev/null +++ b/test/addons-napi/test_handle_scope/test.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// testing handle scope api calls +const testHandleScope = + require(`./build/${common.buildType}/test_handle_scope`); + +testHandleScope.NewScope(); +assert.ok(testHandleScope.NewScopeEscape() instanceof Object); diff --git a/test/addons-napi/test_handle_scope/test_handle_scope.c b/test/addons-napi/test_handle_scope/test_handle_scope.c new file mode 100644 index 0000000000..f88c04705e --- /dev/null +++ b/test/addons-napi/test_handle_scope/test_handle_scope.c @@ -0,0 +1,42 @@ +#include +#include "../common.h" +#include + +// these tests validate the handle scope functions in the normal +// flow. Forcing gc behaviour to fully validate they are doing +// the right right thing would be quite hard so we keep it +// simple for now. + +napi_value NewScope(napi_env env, napi_callback_info info) { + napi_handle_scope scope; + napi_value output = NULL; + + NAPI_CALL(env, napi_open_handle_scope(env, &scope)); + NAPI_CALL(env, napi_create_object(env, &output)); + NAPI_CALL(env, napi_close_handle_scope(env, scope)); + return NULL; +} + +napi_value NewScopeEscape(napi_env env, napi_callback_info info) { + napi_escapable_handle_scope scope; + napi_value output = NULL; + napi_value escapee = NULL; + + NAPI_CALL(env, napi_open_escapable_handle_scope(env, &scope)); + NAPI_CALL(env, napi_create_object(env, &output)); + NAPI_CALL(env, napi_escape_handle(env, scope, output, &escapee)); + NAPI_CALL(env, napi_close_escapable_handle_scope(env, scope)); + return escapee; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("NewScope", NewScope), + DECLARE_NAPI_PROPERTY("NewScopeEscape", NewScopeEscape), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_instanceof/binding.gyp b/test/addons-napi/test_instanceof/binding.gyp similarity index 64% rename from test/addons-abi/test_instanceof/binding.gyp rename to test/addons-napi/test_instanceof/binding.gyp index 0519df52e9..7fca7e0736 100644 --- a/test/addons-abi/test_instanceof/binding.gyp +++ b/test/addons-napi/test_instanceof/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_instanceof", - "sources": [ "test_instanceof.cc" ] + "sources": [ "test_instanceof.c" ] } ] } diff --git a/test/addons-napi/test_instanceof/test.js b/test/addons-napi/test_instanceof/test.js new file mode 100644 index 0000000000..38d17031e9 --- /dev/null +++ b/test/addons-napi/test_instanceof/test.js @@ -0,0 +1,87 @@ +'use strict'; +const fs = require('fs'); + +const common = require('../../common'); +const assert = require('assert'); + +// addon is referenced through the eval expression in testFile +// eslint-disable-next-line no-unused-vars +const addon = require(`./build/${common.buildType}/test_instanceof`); +const path = require('path'); + +// The following assert functions are referenced by v8's unit tests +// See for instance deps/v8/test/mjsunit/instanceof.js +// eslint-disable-next-line no-unused-vars +function assertTrue(assertion) { + return assert.strictEqual(true, assertion); +} + +// eslint-disable-next-line no-unused-vars +function assertFalse(assertion) { + assert.strictEqual(false, assertion); +} + +// eslint-disable-next-line no-unused-vars +function assertEquals(leftHandSide, rightHandSide) { + assert.strictEqual(leftHandSide, rightHandSide); +} + +// eslint-disable-next-line no-unused-vars +function assertThrows(statement) { + assert.throws(function() { + eval(statement); + }, Error); +} + +function testFile(fileName) { + const contents = fs.readFileSync(fileName, { encoding: 'utf8' }); + eval(contents.replace(/[(]([^\s(]+)\s+instanceof\s+([^)]+)[)]/g, + '(addon.doInstanceOf($1, $2))')); +} + +testFile( + path.join(path.resolve(__dirname, '..', '..', '..', + 'deps', 'v8', 'test', 'mjsunit'), + 'instanceof.js')); +testFile( + path.join(path.resolve(__dirname, '..', '..', '..', + 'deps', 'v8', 'test', 'mjsunit'), + 'instanceof-2.js')); + +// We can only perform this test if we have a working Symbol.hasInstance +if (typeof Symbol !== 'undefined' && 'hasInstance' in Symbol && + typeof Symbol.hasInstance === 'symbol') { + + function compareToNative(theObject, theConstructor) { + assert.strictEqual(addon.doInstanceOf(theObject, theConstructor), + (theObject instanceof theConstructor)); + } + + const MyClass = function MyClass() {}; + Object.defineProperty(MyClass, Symbol.hasInstance, { + value: function(candidate) { + return 'mark' in candidate; + } + }); + + const MySubClass = function MySubClass() {}; + MySubClass.prototype = new MyClass(); + + let x = new MySubClass(); + let y = new MySubClass(); + x.mark = true; + + compareToNative(x, MySubClass); + compareToNative(y, MySubClass); + compareToNative(x, MyClass); + compareToNative(y, MyClass); + + x = new MyClass(); + y = new MyClass(); + x.mark = true; + + compareToNative(x, MySubClass); + compareToNative(y, MySubClass); + compareToNative(x, MyClass); + compareToNative(y, MyClass); +} diff --git a/test/addons-napi/test_instanceof/test_instanceof.c b/test/addons-napi/test_instanceof/test_instanceof.c new file mode 100644 index 0000000000..76a6542830 --- /dev/null +++ b/test/addons-napi/test_instanceof/test_instanceof.c @@ -0,0 +1,28 @@ +#include +#include +#include "../common.h" + +napi_value doInstanceOf(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + bool instanceof; + NAPI_CALL(env, napi_instanceof(env, args[0], args[1], &instanceof)); + + napi_value result; + NAPI_CALL(env, napi_get_boolean(env, instanceof, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("doInstanceOf", doInstanceOf), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-napi/test_napi_status/binding.gyp b/test/addons-napi/test_napi_status/binding.gyp new file mode 100644 index 0000000000..01ace540a9 --- /dev/null +++ b/test/addons-napi/test_napi_status/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_napi_status", + "sources": [ "test_napi_status.cc" ] + } + ] +} diff --git a/test/addons-napi/test_napi_status/test.js b/test/addons-napi/test_napi_status/test.js new file mode 100644 index 0000000000..60c4f693b2 --- /dev/null +++ b/test/addons-napi/test_napi_status/test.js @@ -0,0 +1,8 @@ +'use strict'; + +const common = require('../../common'); +const addon = require(`./build/${common.buildType}/test_napi_status`); +const assert = require('assert'); + +addon.createNapiError(); +assert(addon.testNapiErrorCleanup(), 'napi_status cleaned up for second call'); diff --git a/test/addons-napi/test_napi_status/test_napi_status.cc b/test/addons-napi/test_napi_status/test_napi_status.cc new file mode 100644 index 0000000000..9046feffd4 --- /dev/null +++ b/test/addons-napi/test_napi_status/test_napi_status.cc @@ -0,0 +1,37 @@ +#include +#include "../common.h" + +napi_value createNapiError(napi_env env, napi_callback_info info) { + napi_value value; + NAPI_CALL(env, napi_create_string_utf8(env, "xyz", 3, &value)); + + double double_value; + napi_status status = napi_get_value_double(env, value, &double_value); + + NAPI_ASSERT(env, status != napi_ok, "Failed to produce error condition"); + + return nullptr; +} + +napi_value testNapiErrorCleanup(napi_env env, napi_callback_info info) { + const napi_extended_error_info *error_info = 0; + NAPI_CALL(env, napi_get_last_error_info(env, &error_info)); + + napi_value result; + bool is_ok = error_info->error_code == napi_ok; + NAPI_CALL(env, napi_get_boolean(env, is_ok, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("createNapiError", createNapiError), + DECLARE_NAPI_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_number/binding.gyp b/test/addons-napi/test_number/binding.gyp similarity index 65% rename from test/addons-abi/test_number/binding.gyp rename to test/addons-napi/test_number/binding.gyp index b04e027b5d..c934d5ef03 100644 --- a/test/addons-abi/test_number/binding.gyp +++ b/test/addons-napi/test_number/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_number", - "sources": [ "test_number.cc" ] + "sources": [ "test_number.c" ] } ] } diff --git a/test/addons-napi/test_number/test.js b/test/addons-napi/test_number/test.js new file mode 100644 index 0000000000..885b9b599d --- /dev/null +++ b/test/addons-napi/test_number/test.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test_number = require(`./build/${common.buildType}/test_number`); + + +// testing api calls for number +assert.strictEqual(0, test_number.Test(0)); +assert.strictEqual(1, test_number.Test(1)); +assert.strictEqual(-1, test_number.Test(-1)); +assert.strictEqual(100, test_number.Test(100)); +assert.strictEqual(2121, test_number.Test(2121)); +assert.strictEqual(-1233, test_number.Test(-1233)); +assert.strictEqual(986583, test_number.Test(986583)); +assert.strictEqual(-976675, test_number.Test(-976675)); + +const num1 = 98765432213456789876546896323445679887645323232436587988766545658; +assert.strictEqual(num1, test_number.Test(num1)); + +const num2 = -4350987086545760976737453646576078997096876957864353245245769809; +assert.strictEqual(num2, test_number.Test(num2)); + +const num3 = Number.MAX_SAFE_INTEGER; +assert.strictEqual(num3, test_number.Test(num3)); + +const num4 = Number.MAX_SAFE_INTEGER + 10; +assert.strictEqual(num4, test_number.Test(num4)); + +const num5 = Number.MAX_VALUE; +assert.strictEqual(num5, test_number.Test(num5)); + +const num6 = Number.MAX_VALUE + 10; +assert.strictEqual(num6, test_number.Test(num6)); + +const num7 = Number.POSITIVE_INFINITY; +assert.strictEqual(num7, test_number.Test(num7)); + +const num8 = Number.NEGATIVE_INFINITY; +assert.strictEqual(num8, test_number.Test(num8)); diff --git a/test/addons-napi/test_number/test_number.c b/test/addons-napi/test_number/test_number.c new file mode 100644 index 0000000000..4725ddb6a2 --- /dev/null +++ b/test/addons-napi/test_number/test_number.c @@ -0,0 +1,35 @@ +#include +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_number, + "Wrong type of arguments. Expects a number as first argument."); + + double input; + NAPI_CALL(env, napi_get_value_double(env, args[0], &input)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, input, &output)); + + return output; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("Test", Test), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_object/binding.gyp b/test/addons-napi/test_object/binding.gyp similarity index 65% rename from test/addons-abi/test_object/binding.gyp rename to test/addons-napi/test_object/binding.gyp index 961735aad1..be225ace77 100644 --- a/test/addons-abi/test_object/binding.gyp +++ b/test/addons-napi/test_object/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_object", - "sources": [ "test_object.cc" ] + "sources": [ "test_object.c" ] } ] } diff --git a/test/addons-napi/test_object/test.js b/test/addons-napi/test_object/test.js new file mode 100644 index 0000000000..d14a15421f --- /dev/null +++ b/test/addons-napi/test_object/test.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// Testing api calls for objects +const test_object = require(`./build/${common.buildType}/test_object`); + + +const object = { + hello: 'world', + array: [ + 1, 94, 'str', 12.321, { test: 'obj in arr' } + ], + newObject: { + test: 'obj in obj' + } +}; + +assert.strictEqual(test_object.Get(object, 'hello'), 'world'); +assert.deepStrictEqual(test_object.Get(object, 'array'), + [ 1, 94, 'str', 12.321, { test: 'obj in arr' } ]); +assert.deepStrictEqual(test_object.Get(object, 'newObject'), + { test: 'obj in obj' }); + +assert(test_object.Has(object, 'hello')); +assert(test_object.Has(object, 'array')); +assert(test_object.Has(object, 'newObject')); + +const newObject = test_object.New(); +assert(test_object.Has(newObject, 'test_number')); +assert.strictEqual(newObject.test_number, 987654321); +assert.strictEqual(newObject.test_string, 'test string'); + +// test_object.Inflate increases all properties by 1 +const cube = { + x: 10, + y: 10, + z: 10 +}; + +assert.deepStrictEqual(test_object.Inflate(cube), {x: 11, y: 11, z: 11}); +assert.deepStrictEqual(test_object.Inflate(cube), {x: 12, y: 12, z: 12}); +assert.deepStrictEqual(test_object.Inflate(cube), {x: 13, y: 13, z: 13}); +cube.t = 13; +assert.deepStrictEqual(test_object.Inflate(cube), {x: 14, y: 14, z: 14, t: 14}); + +const sym1 = Symbol('1'); +const sym2 = Symbol('2'); +const sym3 = Symbol('3'); +const sym4 = Symbol('4'); +const object2 = { + [sym1]: '@@iterator', + [sym2]: sym3 +}; + +assert(test_object.Has(object2, sym1)); +assert(test_object.Has(object2, sym2)); +assert.strictEqual(test_object.Get(object2, sym1), '@@iterator'); +assert.strictEqual(test_object.Get(object2, sym2), sym3); +assert(test_object.Set(object2, 'string', 'value')); +assert(test_object.Set(object2, sym4, 123)); +assert(test_object.Has(object2, 'string')); +assert(test_object.Has(object2, sym4)); +assert.strictEqual(test_object.Get(object2, 'string'), 'value'); +assert.strictEqual(test_object.Get(object2, sym4), 123); diff --git a/test/addons-napi/test_object/test_object.c b/test/addons-napi/test_object/test_object.c new file mode 100644 index 0000000000..9e02c1d0bb --- /dev/null +++ b/test/addons-napi/test_object/test_object.c @@ -0,0 +1,154 @@ +#include +#include "../common.h" +#include + +napi_value Get(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NAPI_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol, + "Wrong type of arguments. Expects a string or symbol as second."); + + napi_value object = args[0]; + napi_value output; + NAPI_CALL(env, napi_get_property(env, object, args[1], &output)); + + return output; +} + +napi_value Set(napi_env env, napi_callback_info info) { + size_t argc = 3; + napi_value args[3]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 3, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NAPI_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol, + "Wrong type of arguments. Expects a string or symbol as second."); + + NAPI_CALL(env, napi_set_property(env, args[0], args[1], args[2])); + + napi_value valuetrue; + NAPI_CALL(env, napi_get_boolean(env, true, &valuetrue)) + + return valuetrue; +} + +napi_value Has(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NAPI_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol, + "Wrong type of arguments. Expects a string or symbol as second."); + + bool has_property; + NAPI_CALL(env, napi_has_property(env, args[0], args[1], &has_property)); + + napi_value ret; + NAPI_CALL(env, napi_get_boolean(env, has_property, &ret)); + + return ret; +} + +napi_value New(napi_env env, napi_callback_info info) { + napi_value ret; + NAPI_CALL(env, napi_create_object(env, &ret)); + + napi_value num; + NAPI_CALL(env, napi_create_number(env, 987654321, &num)); + + NAPI_CALL(env, napi_set_named_property(env, ret, "test_number", num)); + + napi_value str; + const char* str_val = "test string"; + size_t str_len = strlen(str_val); + NAPI_CALL(env, napi_create_string_utf8(env, str_val, str_len, &str)); + + NAPI_CALL(env, napi_set_named_property(env, ret, "test_string", str)); + + return ret; +} + +napi_value Inflate(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + napi_value obj = args[0]; + napi_value propertynames; + NAPI_CALL(env, napi_get_property_names(env, obj, &propertynames)); + + uint32_t i, length; + NAPI_CALL(env, napi_get_array_length(env, propertynames, &length)); + + for (i = 0; i < length; i++) { + napi_value property_str; + NAPI_CALL(env, napi_get_element(env, propertynames, i, &property_str)); + + napi_value value; + NAPI_CALL(env, napi_get_property(env, obj, property_str, &value)); + + double double_val; + NAPI_CALL(env, napi_get_value_double(env, value, &double_val)); + NAPI_CALL(env, napi_create_number(env, double_val + 1, &value)); + NAPI_CALL(env, napi_set_property(env, obj, property_str, value)); + } + + return obj; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("Get", Get), + DECLARE_NAPI_PROPERTY("Set", Set), + DECLARE_NAPI_PROPERTY("Has", Has), + DECLARE_NAPI_PROPERTY("New", New), + DECLARE_NAPI_PROPERTY("Inflate", Inflate), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_properties/binding.gyp b/test/addons-napi/test_properties/binding.gyp similarity index 64% rename from test/addons-abi/test_properties/binding.gyp rename to test/addons-napi/test_properties/binding.gyp index ddcfb41da0..345e5c88d7 100644 --- a/test/addons-abi/test_properties/binding.gyp +++ b/test/addons-napi/test_properties/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_properties", - "sources": [ "test_properties.cc" ] + "sources": [ "test_properties.c" ] } ] } diff --git a/test/addons-napi/test_properties/test.js b/test/addons-napi/test_properties/test.js new file mode 100644 index 0000000000..868c603879 --- /dev/null +++ b/test/addons-napi/test_properties/test.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// Testing api calls for defining properties +const test_object = require(`./build/${common.buildType}/test_properties`); + +assert.strictEqual(test_object.echo('hello'), 'hello'); + +test_object.readwriteValue = 1; +assert.strictEqual(test_object.readwriteValue, 1); +test_object.readwriteValue = 2; +assert.strictEqual(test_object.readwriteValue, 2); + +assert.throws(() => { test_object.readonlyValue = 3; }, TypeError); + +assert.ok(test_object.hiddenValue); + +// Properties with napi_enumerable attribute should be enumerable. +const propertyNames = []; +for (const name in test_object) { + propertyNames.push(name); +} +assert.ok(propertyNames.indexOf('echo') >= 0); +assert.ok(propertyNames.indexOf('readwriteValue') >= 0); +assert.ok(propertyNames.indexOf('readonlyValue') >= 0); +assert.ok(propertyNames.indexOf('hiddenValue') < 0); +assert.ok(propertyNames.indexOf('readwriteAccessor1') < 0); +assert.ok(propertyNames.indexOf('readwriteAccessor2') < 0); +assert.ok(propertyNames.indexOf('readonlyAccessor1') < 0); +assert.ok(propertyNames.indexOf('readonlyAccessor2') < 0); + +// The napi_writable attribute should be ignored for accessors. +test_object.readwriteAccessor1 = 1; +assert.strictEqual(test_object.readwriteAccessor1, 1); +assert.strictEqual(test_object.readonlyAccessor1, 1); +assert.throws(() => { test_object.readonlyAccessor1 = 3; }, TypeError); +test_object.readwriteAccessor2 = 2; +assert.strictEqual(test_object.readwriteAccessor2, 2); +assert.strictEqual(test_object.readonlyAccessor2, 2); +assert.throws(() => { test_object.readonlyAccessor2 = 3; }, TypeError); diff --git a/test/addons-napi/test_properties/test_properties.c b/test/addons-napi/test_properties/test_properties.c new file mode 100644 index 0000000000..67e6b1a0d5 --- /dev/null +++ b/test/addons-napi/test_properties/test_properties.c @@ -0,0 +1,59 @@ +#include +#include "../common.h" + +static double value_ = 1; + +napi_value GetValue(napi_env env, napi_callback_info info) { + size_t argc = 0; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); + + NAPI_ASSERT(env, argc == 0, "Wrong number of arguments"); + + napi_value number; + NAPI_CALL(env, napi_create_number(env, value_, &number)); + + return number; +} + +napi_value SetValue(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + + NAPI_CALL(env, napi_get_value_double(env, args[0], &value_)); + + return NULL; +} + +napi_value Echo(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + + return args[0]; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_value number; + NAPI_CALL_RETURN_VOID(env, napi_create_number(env, value_, &number)); + + napi_property_descriptor properties[] = { + { "echo", 0, Echo, 0, 0, 0, napi_enumerable, 0 }, + { "readwriteValue", 0, 0, 0, 0, number, napi_enumerable | napi_writable, 0 }, + { "readonlyValue", 0, 0, 0, 0, number, napi_enumerable, 0}, + { "hiddenValue", 0, 0, 0, 0, number, napi_default, 0}, + { "readwriteAccessor1", 0, 0, GetValue, SetValue, 0, napi_default, 0}, + { "readwriteAccessor2", 0, 0, GetValue, SetValue, 0, napi_writable, 0}, + { "readonlyAccessor1", 0, 0, GetValue, NULL, 0, napi_default, 0}, + { "readonlyAccessor2", 0, 0, GetValue, NULL, 0, napi_writable, 0}, + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_string/binding.gyp b/test/addons-napi/test_string/binding.gyp similarity index 65% rename from test/addons-abi/test_string/binding.gyp rename to test/addons-napi/test_string/binding.gyp index dd7ea0fa77..d4825de933 100644 --- a/test/addons-abi/test_string/binding.gyp +++ b/test/addons-napi/test_string/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_string", - "sources": [ "test_string.cc" ] + "sources": [ "test_string.c" ] } ] } diff --git a/test/addons-napi/test_string/test.js b/test/addons-napi/test_string/test.js new file mode 100644 index 0000000000..6e4962d129 --- /dev/null +++ b/test/addons-napi/test_string/test.js @@ -0,0 +1,71 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// testing api calls for string +const test_string = require(`./build/${common.buildType}/test_string`); + +const empty = ''; +assert.strictEqual(test_string.TestLatin1(empty), empty); +assert.strictEqual(test_string.TestUtf8(empty), empty); +assert.strictEqual(test_string.TestUtf16(empty), empty); +assert.strictEqual(test_string.Utf16Length(empty), 0); +assert.strictEqual(test_string.Utf8Length(empty), 0); + +const str1 = 'hello world'; +assert.strictEqual(test_string.TestLatin1(str1), str1); +assert.strictEqual(test_string.TestUtf8(str1), str1); +assert.strictEqual(test_string.TestUtf16(str1), str1); +assert.strictEqual(test_string.TestLatin1Insufficient(str1), str1.slice(0, 3)); +assert.strictEqual(test_string.TestUtf8Insufficient(str1), str1.slice(0, 3)); +assert.strictEqual(test_string.TestUtf16Insufficient(str1), str1.slice(0, 3)); +assert.strictEqual(test_string.Utf16Length(str1), 11); +assert.strictEqual(test_string.Utf8Length(str1), 11); + +const str2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; +assert.strictEqual(test_string.TestLatin1(str2), str2); +assert.strictEqual(test_string.TestUtf8(str2), str2); +assert.strictEqual(test_string.TestUtf16(str2), str2); +assert.strictEqual(test_string.TestLatin1Insufficient(str2), str2.slice(0, 3)); +assert.strictEqual(test_string.TestUtf8Insufficient(str2), str2.slice(0, 3)); +assert.strictEqual(test_string.TestUtf16Insufficient(str2), str2.slice(0, 3)); +assert.strictEqual(test_string.Utf16Length(str2), 62); +assert.strictEqual(test_string.Utf8Length(str2), 62); + +const str3 = '?!@#$%^&*()_+-=[]{}/.,<>\'"\\'; +assert.strictEqual(test_string.TestLatin1(str3), str3); +assert.strictEqual(test_string.TestUtf8(str3), str3); +assert.strictEqual(test_string.TestUtf16(str3), str3); +assert.strictEqual(test_string.TestLatin1Insufficient(str3), str3.slice(0, 3)); +assert.strictEqual(test_string.TestUtf8Insufficient(str3), str3.slice(0, 3)); +assert.strictEqual(test_string.TestUtf16Insufficient(str3), str3.slice(0, 3)); +assert.strictEqual(test_string.Utf16Length(str3), 27); +assert.strictEqual(test_string.Utf8Length(str3), 27); + +const str4 = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿'; +assert.strictEqual(test_string.TestLatin1(str4), str4); +assert.strictEqual(test_string.TestUtf8(str4), str4); +assert.strictEqual(test_string.TestUtf16(str4), str4); +assert.strictEqual(test_string.TestLatin1Insufficient(str4), str4.slice(0, 3)); +assert.strictEqual(test_string.TestUtf8Insufficient(str4), str4.slice(0, 1)); +assert.strictEqual(test_string.TestUtf16Insufficient(str4), str4.slice(0, 3)); +assert.strictEqual(test_string.Utf16Length(str4), 31); +assert.strictEqual(test_string.Utf8Length(str4), 62); + +const str5 = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ'; +assert.strictEqual(test_string.TestLatin1(str5), str5); +assert.strictEqual(test_string.TestUtf8(str5), str5); +assert.strictEqual(test_string.TestUtf16(str5), str5); +assert.strictEqual(test_string.TestLatin1Insufficient(str5), str5.slice(0, 3)); +assert.strictEqual(test_string.TestUtf8Insufficient(str5), str5.slice(0, 1)); +assert.strictEqual(test_string.TestUtf16Insufficient(str5), str5.slice(0, 3)); +assert.strictEqual(test_string.Utf16Length(str5), 63); +assert.strictEqual(test_string.Utf8Length(str5), 126); + +const str6 = '\u{2003}\u{2101}\u{2001}\u{202}\u{2011}'; +assert.strictEqual(test_string.TestUtf8(str6), str6); +assert.strictEqual(test_string.TestUtf16(str6), str6); +assert.strictEqual(test_string.TestUtf8Insufficient(str6), str6.slice(0, 1)); +assert.strictEqual(test_string.TestUtf16Insufficient(str6), str6.slice(0, 3)); +assert.strictEqual(test_string.Utf16Length(str6), 5); +assert.strictEqual(test_string.Utf8Length(str6), 14); diff --git a/test/addons-napi/test_string/test_string.c b/test/addons-napi/test_string/test_string.c new file mode 100644 index 0000000000..5cd6d413a6 --- /dev/null +++ b/test/addons-napi/test_string/test_string.c @@ -0,0 +1,220 @@ +#include +#include "../common.h" + +napi_value TestLatin1(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + char buffer[128]; + size_t buffer_size = 128; + size_t copied; + + NAPI_CALL(env, + napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied)); + + napi_value output; + NAPI_CALL(env, napi_create_string_latin1(env, buffer, copied, &output)); + + return output; +} + +napi_value TestUtf8(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + char buffer[128]; + size_t buffer_size = 128; + size_t copied; + + NAPI_CALL(env, + napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied)); + + napi_value output; + NAPI_CALL(env, napi_create_string_utf8(env, buffer, copied, &output)); + + return output; +} + +napi_value TestUtf16(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + char16_t buffer[128]; + size_t buffer_size = 128; + size_t copied; + + NAPI_CALL(env, + napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied)); + + napi_value output; + NAPI_CALL(env, napi_create_string_utf16(env, buffer, copied, &output)); + + return output; +} + +napi_value TestLatin1Insufficient(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + char buffer[4]; + size_t buffer_size = 4; + size_t copied; + + NAPI_CALL(env, + napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied)); + + napi_value output; + NAPI_CALL(env, napi_create_string_latin1(env, buffer, copied, &output)); + + return output; +} + +napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + char buffer[4]; + size_t buffer_size = 4; + size_t copied; + + NAPI_CALL(env, + napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied)); + + napi_value output; + NAPI_CALL(env, napi_create_string_utf8(env, buffer, copied, &output)); + + return output; +} + +napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + char16_t buffer[4]; + size_t buffer_size = 4; + size_t copied; + + NAPI_CALL(env, + napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied)); + + napi_value output; + NAPI_CALL(env, napi_create_string_utf16(env, buffer, copied, &output)); + + return output; +} + +napi_value Utf16Length(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + size_t length; + NAPI_CALL(env, napi_get_value_string_utf16(env, args[0], NULL, 0, &length)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, (double)length, &output)); + + return output; +} + +napi_value Utf8Length(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of argment. Expects a string."); + + size_t length; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], NULL, 0, &length)); + + napi_value output; + NAPI_CALL(env, napi_create_number(env, (double)length, &output)); + + return output; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("TestLatin1", TestLatin1), + DECLARE_NAPI_PROPERTY("TestLatin1Insufficient", TestLatin1Insufficient), + DECLARE_NAPI_PROPERTY("TestUtf8", TestUtf8), + DECLARE_NAPI_PROPERTY("TestUtf8Insufficient", TestUtf8Insufficient), + DECLARE_NAPI_PROPERTY("TestUtf16", TestUtf16), + DECLARE_NAPI_PROPERTY("TestUtf16Insufficient", TestUtf16Insufficient), + DECLARE_NAPI_PROPERTY("Utf16Length", Utf16Length), + DECLARE_NAPI_PROPERTY("Utf8Length", Utf8Length), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_symbol/binding.gyp b/test/addons-napi/test_symbol/binding.gyp similarity index 65% rename from test/addons-abi/test_symbol/binding.gyp rename to test/addons-napi/test_symbol/binding.gyp index 9b8ec0e89d..6ef3407968 100644 --- a/test/addons-abi/test_symbol/binding.gyp +++ b/test/addons-napi/test_symbol/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_symbol", - "sources": [ "test_symbol.cc" ] + "sources": [ "test_symbol.c" ] } ] } diff --git a/test/addons-napi/test_symbol/test1.js b/test/addons-napi/test_symbol/test1.js new file mode 100644 index 0000000000..25eb473c4b --- /dev/null +++ b/test/addons-napi/test_symbol/test1.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// testing api calls for symbol +const test_symbol = require(`./build/${common.buildType}/test_symbol`); + +const sym = test_symbol.New('test'); +assert.strictEqual(sym.toString(), 'Symbol(test)'); + + +const myObj = {}; +const fooSym = test_symbol.New('foo'); +const otherSym = test_symbol.New('bar'); +myObj['foo'] = 'bar'; +myObj[fooSym] = 'baz'; +myObj[otherSym] = 'bing'; +assert.strictEqual(myObj.foo, 'bar'); +assert.strictEqual(myObj[fooSym], 'baz'); +assert.strictEqual(myObj[otherSym], 'bing'); diff --git a/test/addons-napi/test_symbol/test2.js b/test/addons-napi/test_symbol/test2.js new file mode 100644 index 0000000000..6051243111 --- /dev/null +++ b/test/addons-napi/test_symbol/test2.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// testing api calls for symbol +const test_symbol = require(`./build/${common.buildType}/test_symbol`); + +const fooSym = test_symbol.New('foo'); +const myObj = {}; +myObj['foo'] = 'bar'; +myObj[fooSym] = 'baz'; +Object.keys(myObj); // -> [ 'foo' ] +Object.getOwnPropertyNames(myObj); // -> [ 'foo' ] +Object.getOwnPropertySymbols(myObj); // -> [ Symbol(foo) ] +assert.strictEqual(Object.getOwnPropertySymbols(myObj)[0], fooSym); diff --git a/test/addons-napi/test_symbol/test3.js b/test/addons-napi/test_symbol/test3.js new file mode 100644 index 0000000000..a7c6c18c02 --- /dev/null +++ b/test/addons-napi/test_symbol/test3.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// testing api calls for symbol +const test_symbol = require(`./build/${common.buildType}/test_symbol`); + +assert.notStrictEqual(test_symbol.New(), test_symbol.New()); +assert.notStrictEqual(test_symbol.New('foo'), test_symbol.New('foo')); +assert.notStrictEqual(test_symbol.New('foo'), test_symbol.New('bar')); + +const foo1 = test_symbol.New('foo'); +const foo2 = test_symbol.New('foo'); +const object = { + [foo1]: 1, + [foo2]: 2, +}; +assert.strictEqual(object[foo1], 1); +assert.strictEqual(object[foo2], 2); diff --git a/test/addons-napi/test_symbol/test_symbol.c b/test/addons-napi/test_symbol/test_symbol.c new file mode 100644 index 0000000000..f85567c071 --- /dev/null +++ b/test/addons-napi/test_symbol/test_symbol.c @@ -0,0 +1,60 @@ +#include +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_symbol, + "Wrong type of argments. Expects a symbol."); + + char buffer[128]; + size_t buffer_size = 128; + + NAPI_CALL(env, napi_get_value_string_utf8( + env, args[0], buffer, buffer_size, NULL)); + + napi_value output; + NAPI_CALL(env, napi_create_string_utf8(env, buffer, buffer_size, &output)); + + return output; +} + +napi_value New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value description = NULL; + if (argc >= 1) { + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NAPI_ASSERT(env, valuetype == napi_string, + "Wrong type of arguments. Expects a string."); + + description = args[0]; + } + + napi_value symbol; + NAPI_CALL(env, napi_create_symbol(env, description, &symbol)); + + return symbol; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("New", New), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/test_typedarray/binding.gyp b/test/addons-napi/test_typedarray/binding.gyp similarity index 64% rename from test/addons-abi/test_typedarray/binding.gyp rename to test/addons-napi/test_typedarray/binding.gyp index e512fbdcdb..8b4a4dc622 100644 --- a/test/addons-abi/test_typedarray/binding.gyp +++ b/test/addons-napi/test_typedarray/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_typedarray", - "sources": [ "test_typedarray.cc" ] + "sources": [ "test_typedarray.c" ] } ] } diff --git a/test/addons-napi/test_typedarray/test.js b/test/addons-napi/test_typedarray/test.js new file mode 100644 index 0000000000..cc1fcbe356 --- /dev/null +++ b/test/addons-napi/test_typedarray/test.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); + +// Testing api calls for arrays +const test_typedarray = require(`./build/${common.buildType}/test_typedarray`); + +const byteArray = new Uint8Array(3); +byteArray[0] = 0; +byteArray[1] = 1; +byteArray[2] = 2; +assert.strictEqual(byteArray.length, 3); + +const doubleArray = new Float64Array(3); +doubleArray[0] = 0.0; +doubleArray[1] = 1.1; +doubleArray[2] = 2.2; +assert.strictEqual(doubleArray.length, 3); + +const byteResult = test_typedarray.Multiply(byteArray, 3); +assert.ok(byteResult instanceof Uint8Array); +assert.strictEqual(byteResult.length, 3); +assert.strictEqual(byteResult[0], 0); +assert.strictEqual(byteResult[1], 3); +assert.strictEqual(byteResult[2], 6); + +const doubleResult = test_typedarray.Multiply(doubleArray, -3); +assert.ok(doubleResult instanceof Float64Array); +assert.strictEqual(doubleResult.length, 3); +assert.strictEqual(doubleResult[0], 0); +assert.strictEqual(Math.round(10 * doubleResult[1]) / 10, -3.3); +assert.strictEqual(Math.round(10 * doubleResult[2]) / 10, -6.6); + +const externalResult = test_typedarray.External(); +assert.ok(externalResult instanceof Int8Array); +assert.strictEqual(externalResult.length, 3); +assert.strictEqual(externalResult[0], 0); +assert.strictEqual(externalResult[1], 1); +assert.strictEqual(externalResult[2], 2); diff --git a/test/addons-napi/test_typedarray/test_typedarray.c b/test/addons-napi/test_typedarray/test_typedarray.c new file mode 100644 index 0000000000..d779454865 --- /dev/null +++ b/test/addons-napi/test_typedarray/test_typedarray.c @@ -0,0 +1,108 @@ +#include +#include +#include "../common.h" + +napi_value Multiply(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc == 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of argments. Expects a typed array as first argument."); + + napi_value input_array = args[0]; + bool is_typedarray; + NAPI_CALL(env, napi_is_typedarray(env, input_array, &is_typedarray)); + + NAPI_ASSERT(env, is_typedarray, + "Wrong type of argments. Expects a typed array as first argument."); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NAPI_ASSERT(env, valuetype1 == napi_number, + "Wrong type of argments. Expects a number as second argument."); + + double multiplier; + NAPI_CALL(env, napi_get_value_double(env, args[1], &multiplier)); + + napi_typedarray_type type; + napi_value input_buffer; + size_t byte_offset; + size_t i, length; + NAPI_CALL(env, napi_get_typedarray_info( + env, input_array, &type, &length, NULL, &input_buffer, &byte_offset)); + + void* data; + size_t byte_length; + NAPI_CALL(env, napi_get_arraybuffer_info( + env, input_buffer, &data, &byte_length)); + + napi_value output_buffer; + void* output_ptr = NULL; + NAPI_CALL(env, napi_create_arraybuffer( + env, byte_length, &output_ptr, &output_buffer)); + + napi_value output_array; + NAPI_CALL(env, napi_create_typedarray( + env, type, length, output_buffer, byte_offset, &output_array)); + + if (type == napi_uint8_array) { + uint8_t* input_bytes = (uint8_t*)(data) + byte_offset; + uint8_t* output_bytes = (uint8_t*)(output_ptr); + for (i = 0; i < length; i++) { + output_bytes[i] = (uint8_t)(input_bytes[i] * multiplier); + } + } else if (type == napi_float64_array) { + double* input_doubles = (double*)((uint8_t*)(data) + byte_offset); + double* output_doubles = (double*)(output_ptr); + for (i = 0; i < length; i++) { + output_doubles[i] = input_doubles[i] * multiplier; + } + } else { + napi_throw_error(env, "Typed array was of a type not expected by test."); + return NULL; + } + + return output_array; +} + +napi_value External(napi_env env, napi_callback_info info) { + static int8_t externalData[] = {0, 1, 2}; + + napi_value output_buffer; + NAPI_CALL(env, napi_create_external_arraybuffer( + env, + externalData, + sizeof(externalData), + NULL, // finalize_callback + NULL, // finalize_hint + &output_buffer)); + + napi_value output_array; + NAPI_CALL(env, napi_create_typedarray(env, + napi_int8_array, + sizeof(externalData) / sizeof(int8_t), + output_buffer, + 0, + &output_array)); + + return output_array; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("Multiply", Multiply), + DECLARE_NAPI_PROPERTY("External", External), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) diff --git a/test/addons-abi/testcfg.py b/test/addons-napi/testcfg.py similarity index 59% rename from test/addons-abi/testcfg.py rename to test/addons-napi/testcfg.py index 4fd225a5f0..ef7c18fdae 100644 --- a/test/addons-abi/testcfg.py +++ b/test/addons-napi/testcfg.py @@ -3,4 +3,4 @@ import testpy def GetConfiguration(context, root): - return testpy.AddonTestConfiguration(context, root, 'addon-abi') + return testpy.AddonTestConfiguration(context, root, 'addons-napi', ['--napi-modules']) diff --git a/tools/test.py b/tools/test.py index e3ccea4e99..78da68c4af 100755 --- a/tools/test.py +++ b/tools/test.py @@ -1494,7 +1494,7 @@ def ExpandCommand(args): 'message', 'internet', 'addons', - 'addons-abi', + 'addons-napi', 'gc', 'debugger', 'doctool', diff --git a/vcbuild.bat b/vcbuild.bat index 157e0d4d22..eb5bb129b0 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -60,10 +60,10 @@ if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok if /i "%1"=="noetw" set noetw=1&goto arg-ok if /i "%1"=="noperfctr" set noperfctr=1&goto arg-ok if /i "%1"=="licensertf" set licensertf=1&goto arg-ok -if /i "%1"=="test" set test_args=%test_args% addons addons-abi doctool known_issues message parallel sequential -J&set jslint=1&set build_addons=1&&set build_addons_abi=1goto arg-ok -if /i "%1"=="test-ci" set test_args=%test_args% %test_ci_args% -p tap --logfile test.tap addons addons-abi doctool inspector known_issues message sequential parallel&set cctest_args=%cctest_args% --gtest_output=tap:cctest.tap&set build_addons=1&&set build_addons_abi=1goto arg-ok +if /i "%1"=="test" set test_args=%test_args% addons addons-napi doctool known_issues message parallel sequential -J&set jslint=1&set build_addons=1&&set build_addons_abi=1goto arg-ok +if /i "%1"=="test-ci" set test_args=%test_args% %test_ci_args% -p tap --logfile test.tap addons addons-napi doctool inspector known_issues message sequential parallel&set cctest_args=%cctest_args% --gtest_output=tap:cctest.tap&set build_addons=1&&set build_addons_abi=1goto arg-ok if /i "%1"=="test-addons" set test_args=%test_args% addons&set build_addons=1&goto arg-ok -if /i "%1"=="test-addons-abi" set test_args=%test_args% addons-abi&set build_addons_abi=1&goto arg-ok +if /i "%1"=="test-addons-napi" set test_args=%test_args% addons-napi&set build_addons_abi=1&goto arg-ok if /i "%1"=="test-simple" set test_args=%test_args% sequential parallel -J&goto arg-ok if /i "%1"=="test-message" set test_args=%test_args% message&goto arg-ok if /i "%1"=="test-gc" set test_args=%test_args% gc&set buildnodeweak=1&goto arg-ok @@ -333,10 +333,10 @@ echo Failed to build node-weak. goto exit :build-addons -if not defined build_addons goto build-addons-abi +if not defined build_addons goto build-addons-napi if not exist "%node_exe%" ( echo Failed to find node.exe - goto build-addons-abi + goto build-addons-napi ) echo Building addons :: clear @@ -355,22 +355,22 @@ for /d %%F in (test\addons\*) do ( if !errorlevel! neq 0 exit /b !errorlevel! ) -:build-addons-abi +:build-addons-napi if not defined build_addons_abi goto run-tests if not exist "%node_exe%" ( echo Failed to find node.exe goto run-tests ) -echo Building addons-abi +echo Building addons-napi if not defined build_addons_abi goto run-tests echo Building add-ons :: clear -for /d %%F in (test\addons-abi\??_*) do ( +for /d %%F in (test\addons-napi\??_*) do ( rd /s /q %%F ) -:: building addons-abi -for /d %%F in (test\addons-abi\*) do ( +:: building addons-napi +for /d %%F in (test\addons-napi\*) do ( "%node_exe%" deps\npm\node_modules\node-gyp\bin\node-gyp rebuild ^ --directory="%%F" ^ --nodedir="%cd%"