summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile60
-rw-r--r--doc/api/cli.md8
-rw-r--r--doc/node.15
-rw-r--r--node.gyp3
-rw-r--r--src/node.cc39
-rw-r--r--src/node_api.cc2528
-rw-r--r--src/node_api.h479
-rw-r--r--src/node_api_types.h95
-rw-r--r--test/addons-napi/.gitignore7
-rw-r--r--test/addons-napi/1_hello_world/binding.c22
-rw-r--r--test/addons-napi/1_hello_world/binding.gyp8
-rw-r--r--test/addons-napi/1_hello_world/test.js6
-rw-r--r--test/addons-napi/2_function_arguments/binding.c58
-rw-r--r--test/addons-napi/2_function_arguments/binding.gyp8
-rw-r--r--test/addons-napi/2_function_arguments/test.js6
-rw-r--r--test/addons-napi/3_callbacks/binding.c51
-rw-r--r--test/addons-napi/3_callbacks/binding.gyp8
-rw-r--r--test/addons-napi/3_callbacks/test.js22
-rw-r--r--test/addons-napi/4_object_factory/binding.c31
-rw-r--r--test/addons-napi/4_object_factory/binding.gyp8
-rw-r--r--test/addons-napi/4_object_factory/test.js8
-rw-r--r--test/addons-napi/5_function_factory/binding.c36
-rw-r--r--test/addons-napi/5_function_factory/binding.gyp8
-rw-r--r--test/addons-napi/5_function_factory/test.js7
-rw-r--r--test/addons-napi/6_object_wrap/binding.cc7
-rw-r--r--test/addons-napi/6_object_wrap/binding.gyp8
-rw-r--r--test/addons-napi/6_object_wrap/myobject.cc201
-rw-r--r--test/addons-napi/6_object_wrap/myobject.h26
-rw-r--r--test/addons-napi/6_object_wrap/test.js19
-rw-r--r--test/addons-napi/7_factory_wrap/binding.cc32
-rw-r--r--test/addons-napi/7_factory_wrap/binding.gyp8
-rw-r--r--test/addons-napi/7_factory_wrap/myobject.cc110
-rw-r--r--test/addons-napi/7_factory_wrap/myobject.h26
-rw-r--r--test/addons-napi/7_factory_wrap/test.js14
-rw-r--r--test/addons-napi/8_passing_wrapped/binding.cc57
-rw-r--r--test/addons-napi/8_passing_wrapped/binding.gyp8
-rw-r--r--test/addons-napi/8_passing_wrapped/myobject.cc81
-rw-r--r--test/addons-napi/8_passing_wrapped/myobject.h26
-rw-r--r--test/addons-napi/8_passing_wrapped/test.js9
-rw-r--r--test/addons-napi/test_array/binding.gyp8
-rw-r--r--test/addons-napi/test_array/test.js37
-rw-r--r--test/addons-napi/test_array/test_array.c137
-rw-r--r--test/addons-napi/test_buffer/binding.gyp8
-rw-r--r--test/addons-napi/test_buffer/test.js25
-rw-r--r--test/addons-napi/test_buffer/test_buffer.c160
-rw-r--r--test/addons-napi/test_constructor/binding.gyp8
-rw-r--r--test/addons-napi/test_constructor/test.js28
-rw-r--r--test/addons-napi/test_constructor/test_constructor.c104
-rw-r--r--test/addons-napi/test_conversions/binding.gyp8
-rw-r--r--test/addons-napi/test_conversions/test.js140
-rw-r--r--test/addons-napi/test_conversions/test_conversions.c237
-rw-r--r--test/addons-napi/test_error/binding.gyp8
-rw-r--r--test/addons-napi/test_error/test.js57
-rw-r--r--test/addons-napi/test_error/test_error.cc37
-rw-r--r--test/addons-napi/test_exception/binding.gyp8
-rw-r--r--test/addons-napi/test_exception/test.js53
-rw-r--r--test/addons-napi/test_exception/test_exception.c75
-rw-r--r--test/addons-napi/test_function/binding.gyp8
-rw-r--r--test/addons-napi/test_function/test.js28
-rw-r--r--test/addons-napi/test_function/test_function.c55
-rw-r--r--test/addons-napi/test_instanceof/binding.gyp8
-rw-r--r--test/addons-napi/test_instanceof/test.js87
-rw-r--r--test/addons-napi/test_instanceof/test_instanceof.c39
-rw-r--r--test/addons-napi/test_number/binding.gyp8
-rw-r--r--test/addons-napi/test_number/test.js39
-rw-r--r--test/addons-napi/test_number/test_number.c55
-rw-r--r--test/addons-napi/test_object/binding.gyp8
-rw-r--r--test/addons-napi/test_object/test.js65
-rw-r--r--test/addons-napi/test_object/test_object.c246
-rw-r--r--test/addons-napi/test_properties/binding.gyp8
-rw-r--r--test/addons-napi/test_properties/test.js27
-rw-r--r--test/addons-napi/test_properties/test_properties.c85
-rw-r--r--test/addons-napi/test_string/binding.gyp8
-rw-r--r--test/addons-napi/test_string/test.js26
-rw-r--r--test/addons-napi/test_string/test_string.c134
-rw-r--r--test/addons-napi/test_symbol/binding.gyp8
-rw-r--r--test/addons-napi/test_symbol/test1.js20
-rw-r--r--test/addons-napi/test_symbol/test2.js15
-rw-r--r--test/addons-napi/test_symbol/test3.js19
-rw-r--r--test/addons-napi/test_symbol/test_symbol.c94
-rw-r--r--test/addons-napi/test_typedarray/binding.gyp8
-rw-r--r--test/addons-napi/test_typedarray/test.js39
-rw-r--r--test/addons-napi/test_typedarray/test_typedarray.c144
-rw-r--r--test/addons-napi/testcfg.py6
-rw-r--r--test/testpy/__init__.py2
-rwxr-xr-xtools/install.py2
-rwxr-xr-xtools/test.py1
-rw-r--r--vcbuild.bat30
88 files changed, 6580 insertions, 23 deletions
diff --git a/Makefile b/Makefile
index 410f643c91..3644851c73 100644
--- a/Makefile
+++ b/Makefile
@@ -193,9 +193,10 @@ v8:
test: all
$(MAKE) build-addons
+ $(MAKE) build-addons-napi
$(MAKE) cctest
$(PYTHON) tools/test.py --mode=release -J \
- addons 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
@@ -262,6 +263,41 @@ test/addons/.buildstamp: config.gypi \
# TODO(bnoordhuis) Force rebuild after gyp update.
build-addons: $(NODE_EXE) test/addons/.buildstamp
+ADDONS_NAPI_BINDING_GYPS := \
+ $(filter-out test/addons-napi/??_*/binding.gyp, \
+ $(wildcard test/addons-napi/*/binding.gyp))
+
+ADDONS_NAPI_BINDING_SOURCES := \
+ $(filter-out test/addons-napi/??_*/*.cc, $(wildcard test/addons-napi/*/*.cc)) \
+ $(filter-out test/addons-napi/??_*/*.h, $(wildcard test/addons-napi/*/*.h))
+
+# Implicitly depends on $(NODE_EXE), see the build-addons-napi rule for rationale.
+test/addons-napi/.buildstamp: config.gypi \
+ deps/npm/node_modules/node-gyp/package.json \
+ $(ADDONS_NAPI_BINDING_GYPS) $(ADDONS_NAPI_BINDING_SOURCES) \
+ deps/uv/include/*.h deps/v8/include/*.h \
+ src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h \
+ src/node_api.h src/node_api_types.h
+# Cannot use $(wildcard test/addons-napi/*/) here, it's evaluated before
+# embedded addons have been generated from the documentation.
+ @for dirname in test/addons-napi/*/; do \
+ printf "\nBuilding addon $$PWD/$$dirname\n" ; \
+ env MAKEFLAGS="-j1" $(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp \
+ --loglevel=$(LOGLEVEL) rebuild \
+ --python="$(PYTHON)" \
+ --directory="$$PWD/$$dirname" \
+ --nodedir="$$PWD" || exit 1 ; \
+ done
+ touch $@
+
+# .buildstamp and .docbuildstamp need $(NODE_EXE) but cannot depend on it
+# directly because it calls make recursively. The parent make cannot know
+# if the subprocess touched anything so it pessimistically assumes that
+# .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-napi: $(NODE_EXE) test/addons-napi/.buildstamp
+
ifeq ($(OSTYPE),$(filter $(OSTYPE),darwin aix))
XARGS = xargs
else
@@ -274,7 +310,9 @@ clear-stalled:
test-gc: all test/gc/build/Release/binding.node
$(PYTHON) tools/test.py --mode=release gc
-test-build: | all build-addons
+test-build: | all build-addons build-addons-napi
+
+test-build-addons-napi: all build-addons-napi
test-all: test-build test/gc/build/Release/binding.node
$(PYTHON) tools/test.py --mode=debug,release
@@ -282,12 +320,12 @@ test-all: test-build test/gc/build/Release/binding.node
test-all-valgrind: test-build
$(PYTHON) tools/test.py --mode=debug,release --valgrind
-CI_NATIVE_SUITES := addons
+CI_NATIVE_SUITES := addons addons-napi
CI_JS_SUITES := doctool inspector known_issues message parallel pseudo-tty sequential
# Build and test addons without building anything else
test-ci-native: LOGLEVEL := info
-test-ci-native: | test/addons/.buildstamp
+test-ci-native: | test/addons/.buildstamp test/addons-napi/.buildstamp
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
--mode=release --flaky-tests=$(FLAKY_TESTS) \
$(TEST_CI_ARGS) $(CI_NATIVE_SUITES)
@@ -304,11 +342,11 @@ test-ci-js: | clear-stalled
fi
test-ci: LOGLEVEL := info
-test-ci: | clear-stalled build-addons
+test-ci: | clear-stalled 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) $(CI_NATIVE_SUITES) $(CI_JS_SUITES)
+ $(TEST_CI_ARGS) $(CI_NATIVE_SUITES) addons-napi $(CI_JS_SUITES)
# Clean up any leftover processes
PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
if [ "$${PS_OUT}" ]; then \
@@ -355,7 +393,10 @@ test-npm: $(NODE_EXE)
test-npm-publish: $(NODE_EXE)
npm_package_config_publishtest=true $(NODE) deps/npm/test/run.js
-test-addons: test-build
+test-addons-napi: test-build-addons-napi
+ $(PYTHON) tools/test.py --mode=release addons-napi
+
+test-addons: test-build test-addons-napi
$(PYTHON) tools/test.py --mode=release addons
test-addons-clean:
@@ -821,6 +862,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-napi/??_*/*.cc test/addons-napi/??_*/*.h)
CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \
src/*.c \
@@ -830,6 +872,8 @@ CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \
test/addons/*/*.h \
test/cctest/*.cc \
test/cctest/*.h \
+ test/addons-napi/*/*.cc \
+ test/addons-napi/*/*.h \
test/gc/binding.cc \
tools/icu/*.cc \
tools/icu/*.h \
@@ -869,4 +913,4 @@ endif
test-v8-intl test-v8-benchmarks test-v8-all v8 lint-ci bench-ci jslint-ci \
doc-only $(TARBALL)-headers test-ci test-ci-native test-ci-js build-ci \
clear-stalled coverage-clean coverage-build coverage-test coverage \
- list-gtests
+ list-gtests test-addons-napi build-addons-napi
diff --git a/doc/api/cli.md b/doc/api/cli.md
index a24aa5ee80..5f5e577895 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -144,6 +144,14 @@ added: v6.0.0
Silence all process warnings (including deprecations).
+### `--napi-modules`
+<!-- YAML
+added: REPLACEME
+-->
+
+Enable loading native modules compiled with the ABI-stable Node.js API (N-API)
+(experimental).
+
### `--trace-warnings`
<!-- YAML
added: v6.0.0
diff --git a/doc/node.1 b/doc/node.1
index 287b4e868a..0905b1f944 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -120,6 +120,11 @@ Throw errors for deprecations.
Silence all process warnings (including deprecations).
.TP
+.BR \-\-napi\-modules
+Enable loading native modules compiled with the ABI-stable Node.js API (N-API)
+(experimental).
+
+.TP
.BR \-\-trace\-warnings
Print stack traces for process warnings (including deprecations).
diff --git a/node.gyp b/node.gyp
index 597df9d126..d99653a63c 100644
--- a/node.gyp
+++ b/node.gyp
@@ -167,6 +167,9 @@
'src/handle_wrap.cc',
'src/js_stream.cc',
'src/node.cc',
+ 'src/node_api.cc',
+ 'src/node_api.h',
+ 'src/node_api_types.h',
'src/node_buffer.cc',
'src/node_config.cc',
'src/node_constants.cc',
diff --git a/src/node.cc b/src/node.cc
index 77e6a5826e..853e0aa796 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -180,6 +180,9 @@ static const char* trace_enabled_categories = nullptr;
std::string icu_data_dir; // NOLINT(runtime/string)
#endif
+// N-API is in experimental state, disabled by default.
+bool load_napi_modules = false;
+
// used by C++ modules as well
bool no_deprecation = false;
@@ -2463,15 +2466,25 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
}
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
@@ -3537,6 +3550,7 @@ static void PrintHelp() {
" --trace-deprecation show stack traces on deprecations\n"
" --throw-deprecation throw an exception on deprecations\n"
" --no-warnings silence all process warnings\n"
+ " --napi-modules load N-API modules\n"
" --trace-warnings show stack traces on process warnings\n"
" --redirect-warnings=path\n"
" write warnings to path instead of\n"
@@ -3709,6 +3723,8 @@ static void ParseArgs(int* argc,
force_repl = true;
} else if (strcmp(arg, "--no-deprecation") == 0) {
no_deprecation = true;
+ } else if (strcmp(arg, "--napi-modules") == 0) {
+ load_napi_modules = true;
} else if (strcmp(arg, "--no-warnings") == 0) {
no_process_warnings = true;
} else if (strcmp(arg, "--trace-warnings") == 0) {
@@ -4489,6 +4505,11 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
if (debug_enabled)
EnableDebug(&env);
+ if (load_napi_modules) {
+ ProcessEmitWarning(&env, "N-API is an experimental feature "
+ "and could change at any time.");
+ }
+
{
SealHandleScope seal(isolate);
bool more;
diff --git a/src/node_api.cc b/src/node_api.cc
new file mode 100644
index 0000000000..a94ee6af4f
--- /dev/null
+++ b/src/node_api.cc
@@ -0,0 +1,2528 @@
+/******************************************************************************
+ * 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 <node_buffer.h>
+#include <node_object_wrap.h>
+#include <string.h>
+#include <algorithm>
+#include <cmath>
+#include <vector>
+#include "node_api.h"
+
+namespace v8impl {
+
+//=== Conversion between V8 Isolate and napi_env ==========================
+
+napi_env JsEnvFromV8Isolate(v8::Isolate* isolate) {
+ return reinterpret_cast<napi_env>(isolate);
+}
+
+v8::Isolate* V8IsolateFromJsEnv(napi_env e) {
+ return reinterpret_cast<v8::Isolate*>(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<T> v) to mimic the behavior
+// of a EscapableHandleScope::Escape(Local<T> 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 <typename T>
+ v8::Local<T> Escape(v8::Local<T> handle) {
+ return scope.Escape(handle);
+ }
+
+ private:
+ v8::EscapableHandleScope scope;
+};
+
+napi_handle_scope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) {
+ return reinterpret_cast<napi_handle_scope>(s);
+}
+
+HandleScopeWrapper* V8HandleScopeFromJsHandleScope(napi_handle_scope s) {
+ return reinterpret_cast<HandleScopeWrapper*>(s);
+}
+
+napi_escapable_handle_scope JsEscapableHandleScopeFromV8EscapableHandleScope(
+ EscapableHandleScopeWrapper* s) {
+ return reinterpret_cast<napi_escapable_handle_scope>(s);
+}
+
+EscapableHandleScopeWrapper*
+V8EscapableHandleScopeFromJsEscapableHandleScope(
+ napi_escapable_handle_scope s) {
+ return reinterpret_cast<EscapableHandleScopeWrapper*>(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<v8::Value>) == sizeof(napi_value),
+ "Cannot convert between v8::Local<v8::Value> and napi_value");
+
+napi_value JsValueFromV8LocalValue(v8::Local<v8::Value> local) {
+ return reinterpret_cast<napi_value>(*local);
+}
+
+v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {
+ v8::Local<v8::Value> local;
+ memcpy(&local, &v, sizeof(v));
+ return local;
+}
+
+// Adapter for napi_finalize callbacks.
+class Finalizer {
+ protected:
+ Finalizer(v8::Isolate* isolate,
+ napi_finalize finalize_callback,
+ void* finalize_data,
+ void* finalize_hint)
+ : _isolate(isolate),
+ _finalize_callback(finalize_callback),
+ _finalize_data(finalize_data),
+ _finalize_hint(finalize_hint) {
+ }
+
+ ~Finalizer() {
+ }
+
+ public:
+ static Finalizer* New(v8::Isolate* isolate,
+ napi_finalize finalize_callback = nullptr,
+ void* finalize_data = nullptr,
+ void* finalize_hint = nullptr) {
+ return new Finalizer(
+ isolate, 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<Finalizer*>(hint);
+ if (finalizer->_finalize_callback != nullptr) {
+ finalizer->_finalize_callback(
+ v8impl::JsEnvFromV8Isolate(finalizer->_isolate),
+ data,
+ finalizer->_finalize_hint);
+ }
+
+ Delete(finalizer);
+ }
+
+ protected:
+ v8::Isolate* _isolate;
+ napi_finalize _finalize_callback;
+ void* _finalize_data;
+ void* _finalize_hint;
+};
+
+// Wrapper around v8::Persistent that implements reference counting.
+class Reference : private Finalizer {
+ private:
+ Reference(v8::Isolate* isolate,
+ v8::Local<v8::Value> value,
+ uint32_t initial_refcount,
+ bool delete_self,
+ napi_finalize finalize_callback,
+ void* finalize_data,
+ void* finalize_hint)
+ : Finalizer(isolate, finalize_callback, finalize_data, finalize_hint),
+ _persistent(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(v8::Isolate* isolate,
+ v8::Local<v8::Value> value,
+ uint32_t initial_refcount,
+ bool delete_self,
+ napi_finalize finalize_callback = nullptr,
+ void* finalize_data = nullptr,
+ void* finalize_hint = nullptr) {
+ return new Reference(isolate,
+ 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<v8::Value> Get() {
+ if (_persistent.IsEmpty()) {
+ return v8::Local<v8::Value>();
+ } else {
+ return v8::Local<v8::Value>::New(_isolate, _persistent);
+ }
+ }
+
+ private:
+ static void FinalizeCallback(const v8::WeakCallbackInfo<Reference>& 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(
+ v8impl::JsEnvFromV8Isolate(reference->_isolate),
+ reference->_finalize_data,
+ reference->_finalize_hint);
+ }
+
+ if (delete_self) {
+ Delete(reference);
+ }
+ }
+
+ v8::Persistent<v8::Value> _persistent;
+ uint32_t _refcount;
+ bool _delete_self;
+};
+
+class TryCatch : public v8::TryCatch {
+ public:
+ explicit TryCatch(v8::Isolate* isolate)
+ : v8::TryCatch(isolate), _isolate(isolate) {}
+
+ ~TryCatch() {
+ if (HasCaught()) {
+ _the_exception.Reset(_isolate, Exception());
+ }
+ }
+
+ static v8::Persistent<v8::Value>& LastException() { return _the_exception; }
+
+ private:
+ static v8::Persistent<v8::Value> _the_exception;
+ v8::Isolate* _isolate;
+};
+
+v8::Persistent<v8::Value> TryCatch::_the_exception;
+
+//=== 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;
+
+// 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 <typename Info, int kInternalFieldIndex>
+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<v8::Object>::Cast(cbinfo.Data())) {
+ _data = v8::Local<v8::External>::Cast(_cbdata->GetInternalField(kDataIndex))
+ ->Value();
+ }
+
+ /*virtual*/
+ bool IsConstructCall() override { return false; }
+
+ protected:
+ void InvokeCallback() {
+ napi_callback_info cbinfo_wrapper = reinterpret_cast<napi_callback_info>(
+ static_cast<CallbackWrapper*>(this));
+ napi_callback cb = reinterpret_cast<napi_callback>(
+ v8::Local<v8::External>::Cast(
+ _cbdata->GetInternalField(kInternalFieldIndex))->Value());
+ v8::Isolate* isolate = _cbinfo.GetIsolate();
+ cb(v8impl::JsEnvFromV8Isolate(isolate), cbinfo_wrapper);
+
+ if (!TryCatch::LastException().IsEmpty()) {
+ isolate->ThrowException(
+ v8::Local<v8::Value>::New(isolate, TryCatch::LastException()));
+ TryCatch::LastException().Reset();
+ }
+ }
+
+ const Info& _cbinfo;
+ const v8::Local<v8::Object> _cbdata;
+};
+
+class FunctionCallbackWrapper
+ : public CallbackWrapperBase<v8::FunctionCallbackInfo<v8::Value>,
+ kFunctionIndex> {
+ public:
+ static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ FunctionCallbackWrapper cbwrapper(info);
+ cbwrapper.InvokeCallback();
+ }
+
+ explicit FunctionCallbackWrapper(
+ const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ _cbinfo.GetReturnValue().Set(val);
+ }
+};
+
+class GetterCallbackWrapper
+ : public CallbackWrapperBase<v8::PropertyCallbackInfo<v8::Value>,
+ kGetterIndex> {
+ public:
+ static void Invoke(v8::Local<v8::Name> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ GetterCallbackWrapper cbwrapper(info);
+ cbwrapper.InvokeCallback();
+ }
+
+ explicit GetterCallbackWrapper(
+ const v8::PropertyCallbackInfo<v8::Value>& 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<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ _cbinfo.GetReturnValue().Set(val);
+ }
+};
+
+class SetterCallbackWrapper
+ : public CallbackWrapperBase<v8::PropertyCallbackInfo<void>, kSetterIndex> {
+ public:
+ static void Invoke(v8::Local<v8::Name> property,
+ v8::Local<v8::Value> value,
+ const v8::PropertyCallbackInfo<void>& info) {
+ SetterCallbackWrapper cbwrapper(info, value);
+ cbwrapper.InvokeCallback();
+ }
+
+ SetterCallbackWrapper(const v8::PropertyCallbackInfo<void>& cbinfo,
+ const v8::Local<v8::Value>& 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<v8::Value>& _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<v8::Object> CreateFunctionCallbackData(napi_env env,
+ napi_callback cb,
+ void* data) {
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::ObjectTemplate> otpl = v8::ObjectTemplate::New(isolate);
+ otpl->SetInternalFieldCount(v8impl::kFunctionFieldCount);
+ v8::Local<v8::Object> cbdata = otpl->NewInstance(context).ToLocalChecked();
+
+ cbdata->SetInternalField(
+ v8impl::kFunctionIndex,
+ v8::External::New(isolate, reinterpret_cast<void*>(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<v8::Object> CreateAccessorCallbackData(napi_env env,
+ napi_callback getter,
+ napi_callback setter,
+ void* data) {
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::ObjectTemplate> otpl = v8::ObjectTemplate::New(isolate);
+ otpl->SetInternalFieldCount(v8impl::kAccessorFieldCount);
+ v8::Local<v8::Object> cbdata = otpl->NewInstance(context).ToLocalChecked();
+
+ if (getter != nullptr) {
+ cbdata->SetInternalField(
+ v8impl::kGetterIndex,
+ v8::External::New(isolate, reinterpret_cast<void*>(getter)));
+ }
+
+ if (setter != nullptr) {
+ cbdata->SetInternalField(
+ v8impl::kSetterIndex,
+ v8::External::New(isolate, reinterpret_cast<void*>(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<v8::Object> exports,
+ v8::Local<v8::Value> module,
+ v8::Local<v8::Context> context,
+ void* priv) {
+ napi_module* mod = static_cast<napi_module*>(priv);
+ mod->nm_register_func(
+ v8impl::JsEnvFromV8Isolate(context->GetIsolate()),
+ v8impl::JsValueFromV8LocalValue(exports),
+ v8impl::JsValueFromV8LocalValue(module),
+ mod->nm_priv);
+}
+
+#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);
+}
+
+#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_MAYBE_EMPTY(maybe, status) \
+ RETURN_STATUS_IF_FALSE(!((maybe).IsEmpty()), (status))
+
+#define CHECK_MAYBE_NOTHING(maybe, status) \
+ RETURN_STATUS_IF_FALSE(!((maybe).IsNothing()), (status))
+
+// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope
+#define NAPI_PREAMBLE(env) \
+ CHECK_ARG(env); \
+ RETURN_STATUS_IF_FALSE(v8impl::TryCatch::LastException().IsEmpty(), \
+ napi_pending_exception); \
+ napi_clear_last_error(); \
+ v8impl::TryCatch try_catch(v8impl::V8IsolateFromJsEnv((env)))
+
+#define CHECK_TO_TYPE(type, context, result, src, status) \
+ do { \
+ auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
+ CHECK_MAYBE_EMPTY(maybe, (status)); \
+ result = maybe.ToLocalChecked(); \
+ } while (0)
+
+#define CHECK_TO_OBJECT(context, result, src) \
+ CHECK_TO_TYPE(Object, context, result, src, napi_object_expected)
+
+#define CHECK_TO_STRING(context, result, src) \
+ CHECK_TO_TYPE(String, context, result, src, napi_string_expected)
+
+#define CHECK_TO_NUMBER(context, result, src) \
+ CHECK_TO_TYPE(Number, context, result, src, napi_number_expected)
+
+#define CHECK_TO_BOOL(context, result, src) \
+ CHECK_TO_TYPE(Boolean, context, result, src, napi_boolean_expected)
+
+#define CHECK_NEW_FROM_UTF8_LEN(isolate, result, str, len) \
+ do { \
+ auto str_maybe = v8::String::NewFromUtf8( \
+ (isolate), (str), v8::NewStringType::kInternalized, (len)); \
+ CHECK_MAYBE_EMPTY(str_maybe, napi_generic_failure); \
+ result = str_maybe.ToLocalChecked(); \
+ } while (0)
+
+#define CHECK_NEW_FROM_UTF8(isolate, result, str) \
+ CHECK_NEW_FROM_UTF8_LEN((isolate), (result), (str), -1)
+
+#define GET_RETURN_STATUS() \
+ (!try_catch.HasCaught() ? napi_ok \
+ : napi_set_last_error(napi_pending_exception))
+
+// Static last error returned from napi_get_last_error_info
+napi_extended_error_info static_last_error = { nullptr, nullptr, 0, napi_ok };
+
+// 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 function was expected",
+ "A number was expected",
+ "A boolean was expected",
+ "An array was expected",
+ "Unknown failure",
+ "An exception is pending"};
+
+void napi_clear_last_error() {
+ static_last_error.error_code = napi_ok;
+
+ // TODO(boingoing): Should this be a callback?
+ 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) {
+ static_last_error.error_code = error_code;
+ static_last_error.engine_error_code = engine_error_code;
+ static_last_error.engine_reserved = engine_reserved;
+
+ return error_code;
+}
+
+napi_status napi_get_last_error_info(napi_env env,
+ const napi_extended_error_info** result) {
+ CHECK_ARG(env);
+
+ static_assert(node::arraysize(error_messages) == napi_status_last,
+ "Count of error messages must match count of error values");
+ assert(static_last_error.error_code < napi_status_last);
+
+ // Wait until someone requests the last error information to fetch the error
+ // message string
+ static_last_error.error_message =
+ error_messages[static_last_error.error_code];
+
+ *result = &static_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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Function> return_value;
+ v8::EscapableHandleScope scope(isolate);
+ v8::Local<v8::Object> cbdata =
+ v8impl::CreateFunctionCallbackData(env, cb, callback_data);
+
+ RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure);
+
+ v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(
+ isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);
+
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::MaybeLocal<v8::Function> maybe_function = tpl->GetFunction(context);
+ CHECK_MAYBE_EMPTY(maybe_function, napi_generic_failure);
+
+ return_value = scope.Escape(maybe_function.ToLocalChecked());
+
+ if (utf8name != nullptr) {
+ v8::Local<v8::String> name_string;
+ CHECK_NEW_FROM_UTF8(isolate, name_string, utf8name);
+ return_value->SetName(name_string);
+ }
+
+ *result = v8impl::JsValueFromV8LocalValue(return_value);
+
+ return GET_RETURN_STATUS();
+}
+
+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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+
+ v8::EscapableHandleScope scope(isolate);
+ v8::Local<v8::Object> cbdata =
+ v8impl::CreateFunctionCallbackData(env, constructor, callback_data);
+
+ RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure);
+
+ v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(
+ isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);
+
+ // we need an internal field to stash the wrapped object
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+
+ v8::Local<v8::String> name_string;
+ CHECK_NEW_FROM_UTF8(isolate, 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_property) != 0) {
+ // Static properties are handled separately below.
+ static_property_count++;
+ continue;
+ }
+
+ v8::Local<v8::String> property_name;
+ CHECK_NEW_FROM_UTF8(isolate, property_name, p->utf8name);
+
+ v8::PropertyAttribute attributes =
+ static_cast<v8::PropertyAttribute>(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<v8::Object> cbdata =
+ v8impl::CreateFunctionCallbackData(env, p->method, p->data);
+
+ RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure);
+
+ v8::Local<v8::FunctionTemplate> t =
+ v8::FunctionTemplate::New(isolate,
+ v8impl::FunctionCallbackWrapper::Invoke,
+ cbdata,
+ v8::Signature::New(isolate, tpl));
+ t->SetClassName(property_name);
+
+ tpl->PrototypeTemplate()->Set(property_name, t, attributes);
+ } else if (p->getter || p->setter) {
+ v8::Local<v8::Object> 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 {
+ v8::Local<v8::Value> 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<napi_property_descriptor> 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_property) != 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();
+}
+
+napi_status napi_set_return_value(napi_env env,
+ napi_callback_info cbinfo,
+ napi_value value) {
+ NAPI_PREAMBLE(env);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ info->SetReturnValue(value);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_property_names(napi_env env,
+ napi_value object,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+ CHECK_TO_OBJECT(context, obj, object);
+
+ auto maybe_propertynames = obj->GetPropertyNames(context);
+
+ CHECK_MAYBE_EMPTY(maybe_propertynames, napi_generic_failure);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ maybe_propertynames.ToLocalChecked());
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_set_property(napi_env env,
+ napi_value object,
+ napi_value key,
+ napi_value value) {
+ NAPI_PREAMBLE(env);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+
+ v8::Maybe<bool> set_maybe = obj->Set(context, k, val);
+
+ RETURN_STATUS_IF_FALSE(set_maybe.FromMaybe(false), napi_generic_failure);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_has_property(napi_env env,
+ napi_value object,
+ napi_value key,
+ bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
+ v8::Maybe<bool> has_maybe = obj->Has(context, k);
+
+ CHECK_MAYBE_NOTHING(has_maybe, napi_generic_failure);
+
+ *result = has_maybe.FromMaybe(false);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_property(napi_env env,
+ napi_value object,
+ napi_value key,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ auto get_maybe = obj->Get(context, k);
+
+ CHECK_MAYBE_EMPTY(get_maybe, napi_generic_failure);
+
+ v8::Local<v8::Value> val = get_maybe.ToLocalChecked();
+ *result = v8impl::JsValueFromV8LocalValue(val);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_set_named_property(napi_env env,
+ napi_value object,
+ const char* utf8name,
+ napi_value value) {
+ NAPI_PREAMBLE(env);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Local<v8::Name> key;
+ CHECK_NEW_FROM_UTF8(isolate, key, utf8name);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+
+ v8::Maybe<bool> set_maybe = obj->Set(context, key, val);
+
+ RETURN_STATUS_IF_FALSE(set_maybe.FromMaybe(false), napi_generic_failure);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_has_named_property(napi_env env,
+ napi_value object,
+ const char* utf8name,
+ bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Local<v8::Name> key;
+ CHECK_NEW_FROM_UTF8(isolate, key, utf8name);
+
+ v8::Maybe<bool> has_maybe = obj->Has(context, key);
+
+ CHECK_MAYBE_NOTHING(has_maybe, napi_generic_failure);
+
+ *result = has_maybe.FromMaybe(false);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_named_property(napi_env env,
+ napi_value object,
+ const char* utf8name,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::Name> key;
+ CHECK_NEW_FROM_UTF8(isolate, key, utf8name);
+
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ auto get_maybe = obj->Get(context, key);
+
+ CHECK_MAYBE_EMPTY(get_maybe, napi_generic_failure);
+
+ v8::Local<v8::Value> val = get_maybe.ToLocalChecked();
+ *result = v8impl::JsValueFromV8LocalValue(val);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_set_element(napi_env env,
+ napi_value object,
+ uint32_t index,
+ napi_value value) {
+ NAPI_PREAMBLE(env);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ auto set_maybe = obj->Set(context, index, val);
+
+ RETURN_STATUS_IF_FALSE(set_maybe.FromMaybe(false), napi_generic_failure);
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_has_element(napi_env env,
+ napi_value object,
+ uint32_t index,
+ bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Maybe<bool> has_maybe = obj->Has(context, index);
+
+ CHECK_MAYBE_NOTHING(has_maybe, napi_generic_failure);
+
+ *result = has_maybe.FromMaybe(false);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_element(napi_env env,
+ napi_value object,
+ uint32_t index,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+
+ CHECK_TO_OBJECT(context, obj, object);
+
+ auto get_maybe = obj->Get(context, index);
+
+ CHECK_MAYBE_EMPTY(get_maybe, napi_generic_failure);
+
+ *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked());
+ return GET_RETURN_STATUS();
+}
+
+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 = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj =
+ v8impl::V8LocalValueFromJsValue(object).As<v8::Object>();
+
+ for (size_t i = 0; i < property_count; i++) {
+ const napi_property_descriptor* p = &properties[i];
+
+ v8::Local<v8::Name> name;
+ CHECK_NEW_FROM_UTF8(isolate, name, p->utf8name);
+
+ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+ p->attributes & ~napi_static_property);
+
+ if (p->method) {
+ v8::Local<v8::Object> cbdata =
+ v8impl::CreateFunctionCallbackData(env, p->method, p->data);
+
+ RETURN_STATUS_IF_FALSE(!cbdata.IsEmpty(), napi_generic_failure);
+
+ v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(
+ isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);
+
+ auto define_maybe =
+ obj->DefineOwnProperty(context, name, t->GetFunction(), attributes);
+
+ if (!define_maybe.FromMaybe(false)) {
+ return napi_set_last_error(napi_generic_failure);
+ }
+ } else if (p->getter || p->setter) {
+ v8::Local<v8::Object> cbdata = v8impl::CreateAccessorCallbackData(
+ env,
+ p->getter,
+ p->setter,
+ p->data);
+
+ auto set_maybe = obj->SetAccessor(
+ context,
+ 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(napi_invalid_arg);
+ }
+ } else {
+ v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
+
+ auto define_maybe =
+ obj->DefineOwnProperty(context, name, value, attributes);
+
+ if (!define_maybe.FromMaybe(false)) {
+ return napi_set_last_error(napi_invalid_arg);
+ }
+ }
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_is_array(napi_env env, napi_value value, bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+
+ *result = val->IsArray();
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_array_length(napi_env env,
+ napi_value value,
+ uint32_t* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsArray(), napi_array_expected);
+
+ v8::Local<v8::Array> arr = val.As<v8::Array>();
+ *result = arr->Length();
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_strict_equals(napi_env env,
+ napi_value lhs,
+ napi_value rhs,
+ bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> a = v8impl::V8LocalValueFromJsValue(lhs);
+ v8::Local<v8::Value> b = v8impl::V8LocalValueFromJsValue(rhs);
+
+ *result = a->StrictEquals(b);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_prototype(napi_env env,
+ napi_value object,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::Object> obj;
+ CHECK_TO_OBJECT(context, obj, object);
+
+ v8::Local<v8::Value> val = obj->GetPrototype();
+ *result = v8impl::JsValueFromV8LocalValue(val);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_object(napi_env env, napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Object::New(v8impl::V8IsolateFromJsEnv(env)));
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_array(napi_env env, napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Array::New(v8impl::V8IsolateFromJsEnv(env)));
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_array_with_length(napi_env env,
+ size_t length,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Array::New(v8impl::V8IsolateFromJsEnv(env), length));
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_string_utf8(napi_env env,
+ const char* str,
+ size_t length,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ auto isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::String> s;
+ CHECK_NEW_FROM_UTF8_LEN(isolate, s, str, length);
+
+ *result = v8impl::JsValueFromV8LocalValue(s);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_string_utf16(napi_env env,
+ const char16_t* str,
+ size_t length,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ auto isolate = v8impl::V8IsolateFromJsEnv(env);
+ auto str_maybe =
+ v8::String::NewFromTwoByte(isolate,
+ reinterpret_cast<const uint16_t*>(str),
+ v8::NewStringType::kInternalized,
+ length);
+ CHECK_MAYBE_EMPTY(str_maybe, napi_generic_failure);
+
+ *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_number(napi_env env,
+ double value,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Number::New(v8impl::V8IsolateFromJsEnv(env), value));
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) {
+ CHECK_ARG(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+
+ 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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+
+ if (description == nullptr) {
+ *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate));
+ } else {
+ v8::Local<v8::Value> desc = v8impl::V8LocalValueFromJsValue(description);
+ RETURN_STATUS_IF_FALSE(desc->IsString(), napi_string_expected);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Symbol::New(isolate, desc.As<v8::String>()));
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_error(napi_env env,
+ napi_value msg,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
+ RETURN_STATUS_IF_FALSE(message_value->IsString(), napi_string_expected);
+
+ *result = v8impl::JsValueFromV8LocalValue(v8::Exception::Error(
+ message_value.As<v8::String>()));
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_type_error(napi_env env,
+ napi_value msg,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
+ RETURN_STATUS_IF_FALSE(message_value->IsString(), napi_string_expected);
+
+ *result = v8impl::JsValueFromV8LocalValue(v8::Exception::TypeError(
+ message_value.As<v8::String>()));
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_range_error(napi_env env,
+ napi_value msg,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
+ RETURN_STATUS_IF_FALSE(message_value->IsString(), napi_string_expected);
+
+ *result = v8impl::JsValueFromV8LocalValue(v8::Exception::RangeError(
+ message_value.As<v8::String>()));
+
+ return GET_RETURN_STATUS();
+}
+
+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_ARG(result);
+
+ v8::Local<v8::Value> 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(napi_invalid_arg);
+ }
+
+ return napi_ok;
+}
+
+napi_status napi_get_undefined(napi_env env, napi_value* result) {
+ CHECK_ARG(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Undefined(v8impl::V8IsolateFromJsEnv(env)));
+
+ return napi_ok;
+}
+
+napi_status napi_get_null(napi_env env, napi_value* result) {
+ CHECK_ARG(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsValueFromV8LocalValue(
+ v8::Null(v8impl::V8IsolateFromJsEnv(env)));
+
+ 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_ARG(argc);
+ CHECK_ARG(argv);
+ CHECK_ARG(this_arg);
+ CHECK_ARG(data);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ info->Args(argv, std::min(*argc, info->ArgsLength()));
+ *argc = info->ArgsLength();
+ *this_arg = info->This();
+ *data = info->Data();
+
+ return napi_ok;
+}
+
+napi_status napi_get_cb_args_length(napi_env env,
+ napi_callback_info cbinfo,
+ size_t* result) {
+ // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
+ CHECK_ARG(result);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ *result = info->ArgsLength();
+ 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_ARG(result);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ *result = info->IsConstructCall();
+ return napi_ok;
+}
+
+// copy encoded arguments into provided buffer or return direct pointer to
+// encoded arguments array?
+napi_status napi_get_cb_args(napi_env env,
+ napi_callback_info cbinfo,
+ napi_value* buf,
+ size_t bufsize) {
+ // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
+ CHECK_ARG(buf);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ info->Args(buf, bufsize);
+ return napi_ok;
+}
+
+napi_status napi_get_cb_this(napi_env env,
+ napi_callback_info cbinfo,
+ napi_value* result) {
+ // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
+ CHECK_ARG(result);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ *result = info->This();
+ return napi_ok;
+}
+
+napi_status napi_get_cb_data(napi_env env,
+ napi_callback_info cbinfo,
+ void** result) {
+ // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because no V8 APIs are called.
+ CHECK_ARG(result);
+
+ v8impl::CallbackWrapper* info =
+ reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
+
+ *result = info->Data();
+ 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 = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::Value> v8recv = v8impl::V8LocalValueFromJsValue(recv);
+
+ v8::Local<v8::Value> v8value = v8impl::V8LocalValueFromJsValue(func);
+ RETURN_STATUS_IF_FALSE(v8value->IsFunction(), napi_invalid_arg);
+
+ v8::Local<v8::Function> v8func = v8value.As<v8::Function>();
+ auto maybe = v8func->Call(context, v8recv, argc,
+ reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
+
+ if (try_catch.HasCaught()) {
+ return napi_set_last_error(napi_pending_exception);
+ } else {
+ if (result != nullptr) {
+ CHECK_MAYBE_EMPTY(maybe, napi_generic_failure);
+ *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
+ }
+ return napi_ok;
+ }
+}
+
+napi_status napi_get_global(napi_env env, napi_value* result) {
+ CHECK_ARG(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ // 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<v8::Context> 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 = v8impl::V8IsolateFromJsEnv(env);
+
+ 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 = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::String> str;
+ CHECK_NEW_FROM_UTF8(isolate, 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 = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::String> str;
+ CHECK_NEW_FROM_UTF8(isolate, 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 = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::String> str;
+ CHECK_NEW_FROM_UTF8(isolate, 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_ARG(result);
+
+ v8::Local<v8::Value> 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_ARG(env);
+ CHECK_ARG(value);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsNumber(), napi_number_expected);
+
+ *result = val.As<v8::Number>()->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_ARG(env);
+ CHECK_ARG(value);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsNumber(), napi_number_expected);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> 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_ARG(env);
+ CHECK_ARG(value);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsNumber(), napi_number_expected);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> 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_ARG(env);
+ CHECK_ARG(value);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(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<v8::Number>()->Value();
+ if (std::isnan(doubleValue)) {
+ *result = 0;
+ } else {
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> 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_ARG(value);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsBoolean(), napi_boolean_expected);
+
+ *result = val.As<v8::Boolean>()->Value();
+
+ return napi_ok;
+}
+
+// Gets the number of CHARACTERS in the string.
+napi_status napi_get_value_string_length(napi_env env,
+ napi_value value,
+ size_t* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsString(), napi_string_expected);
+
+ *result = val.As<v8::String>()->Length();
+
+ return GET_RETURN_STATUS();
+}
+
+// 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 bufsize
+// is insufficient, the string will be truncated, including a null terminator.
+// 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<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsString(), napi_string_expected);
+
+ if (!buf) {
+ CHECK_ARG(result);
+ *result = val.As<v8::String>()->Utf8Length();
+ } else {
+ int copied = val.As<v8::String>()->WriteUtf8(
+ buf, bufsize, nullptr, v8::String::REPLACE_INVALID_UTF8);
+
+ if (result != nullptr) {
+ *result = copied;
+ }
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+// 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 bufsize is insufficient, the string will be truncated, including a null
+// terminator. 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<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsString(), napi_string_expected);
+
+ if (!buf) {
+ CHECK_ARG(result);
+ // V8 assumes UTF-16 length is the same as the number of characters.
+ *result = val.As<v8::String>()->Length();
+ } else {
+ int copied = val.As<v8::String>()->Write(
+ reinterpret_cast<uint16_t*>(buf), 0, bufsize, v8::String::NO_OPTIONS);
+
+ if (result != nullptr) {
+ *result = copied;
+ }
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_coerce_to_object(napi_env env,
+ napi_value value,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> obj;
+ CHECK_TO_OBJECT(context, obj, value);
+
+ *result = v8impl::JsValueFromV8LocalValue(obj);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_coerce_to_bool(napi_env env,
+ napi_value value,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Boolean> b;
+
+ CHECK_TO_BOOL(context, b, value);
+
+ *result = v8impl::JsValueFromV8LocalValue(b);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_coerce_to_number(napi_env env,
+ napi_value value,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Number> num;
+
+ CHECK_TO_NUMBER(context, num, value);
+
+ *result = v8impl::JsValueFromV8LocalValue(num);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_coerce_to_string(napi_env env,
+ napi_value value,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::String> str;
+
+ CHECK_TO_STRING(context, str, value);
+
+ *result = v8impl::JsValueFromV8LocalValue(str);
+ return GET_RETURN_STATUS();
+}
+
+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(js_object);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Object> obj =
+ v8impl::V8LocalValueFromJsValue(js_object).As<v8::Object>();
+
+ // Only objects that were created from a NAPI constructor's prototype
+ // via napi_define_class() can be (un)wrapped.
+ RETURN_STATUS_IF_FALSE(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(finalize_cb);
+ v8impl::Reference* reference = v8impl::Reference::New(
+ isolate, obj, 0, false, finalize_cb, native_object, finalize_hint);
+ *result = reinterpret_cast<napi_ref>(reference);
+ } else if (finalize_cb != nullptr) {
+ // Create a self-deleting reference just for the finalize callback.
+ v8impl::Reference::New(
+ isolate, obj, 0, true, finalize_cb, native_object, finalize_hint);
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+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_ARG(js_object);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object);
+ RETURN_STATUS_IF_FALSE(value->IsObject(), napi_invalid_arg);
+ v8::Local<v8::Object> obj = value.As<v8::Object>();
+
+ // Only objects that were created from a NAPI constructor's prototype
+ // via napi_define_class() can be (un)wrapped.
+ RETURN_STATUS_IF_FALSE(obj->InternalFieldCount() > 0, napi_invalid_arg);
+
+ v8::Local<v8::Value> unwrappedValue = obj->GetInternalField(0);
+ RETURN_STATUS_IF_FALSE(unwrappedValue->IsExternal(), napi_invalid_arg);
+
+ *result = unwrappedValue.As<v8::External>()->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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+
+ v8::Local<v8::Value> external_value = v8::External::New(isolate, data);
+
+ // The Reference object will delete itself after invoking the finalizer
+ // callback.
+ v8impl::Reference::New(isolate,
+ external_value,
+ 0,
+ true,
+ finalize_cb,
+ data,
+ finalize_hint);
+
+ *result = v8impl::JsValueFromV8LocalValue(external_value);
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_value_external(napi_env env,
+ napi_value value,
+ void** result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(value);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ RETURN_STATUS_IF_FALSE(val->IsExternal(), napi_invalid_arg);
+
+ v8::Local<v8::External> external_value = val.As<v8::External>();
+ *result = external_value->Value();
+
+ return GET_RETURN_STATUS();
+}
+
+// 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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+
+ v8impl::Reference* reference = v8impl::Reference::New(
+ isolate, v8impl::V8LocalValueFromJsValue(value), initial_refcount, false);
+
+ *result = reinterpret_cast<napi_ref>(reference);
+ return GET_RETURN_STATUS();
+}
+
+// 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_ARG(ref);
+
+ v8impl::Reference::Delete(reinterpret_cast<v8impl::Reference*>(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(ref);
+
+ v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
+ uint32_t count = reference->Ref();
+
+ if (result != nullptr) {
+ *result = count;
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+// 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(ref);
+
+ v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
+
+ if (reference->RefCount() == 0) {
+ return napi_set_last_error(napi_generic_failure);
+ }
+
+ uint32_t count = reference->Unref();
+
+ if (result != nullptr) {
+ *result = count;
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+// 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(ref);
+ CHECK_ARG(result);
+
+ v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
+ *result = v8impl::JsValueFromV8LocalValue(reference->Get());
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsHandleScopeFromV8HandleScope(
+ new v8impl::HandleScopeWrapper(v8impl::V8IsolateFromJsEnv(env)));
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(scope);
+
+ delete v8impl::V8HandleScopeFromJsHandleScope(scope);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_open_escapable_handle_scope(
+ napi_env env,
+ napi_escapable_handle_scope* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(
+ new v8impl::EscapableHandleScopeWrapper(v8impl::V8IsolateFromJsEnv(env)));
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_close_escapable_handle_scope(
+ napi_env env,
+ napi_escapable_handle_scope scope) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(scope);
+
+ delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_escape_handle(napi_env env,
+ napi_escapable_handle_scope scope,
+ napi_value escapee,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(scope);
+ CHECK_ARG(result);
+
+ v8impl::EscapableHandleScopeWrapper* s =
+ v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
+ *result = v8impl::JsValueFromV8LocalValue(
+ s->Escape(v8impl::V8LocalValueFromJsValue(escapee)));
+ return GET_RETURN_STATUS();
+}
+
+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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ v8::Local<v8::Value> v8value = v8impl::V8LocalValueFromJsValue(constructor);
+ RETURN_STATUS_IF_FALSE(v8value->IsFunction(), napi_invalid_arg);
+
+ v8::Local<v8::Function> ctor = v8value.As<v8::Function>();
+
+ auto maybe = ctor->NewInstance(context, argc,
+ reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
+
+ CHECK_MAYBE_EMPTY(maybe, napi_generic_failure);
+
+ *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_instanceof(napi_env env,
+ napi_value object,
+ napi_value constructor,
+ bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = false;
+
+ v8::Local<v8::Object> ctor;
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ CHECK_TO_OBJECT(context, ctor, constructor);
+
+ if (!ctor->IsFunction()) {
+ napi_throw_type_error(env, "constructor must be a function");
+
+ return napi_set_last_error(napi_function_expected);
+ }
+
+ napi_value value, js_result;
+ napi_status status;
+ napi_valuetype value_type;
+
+ // Get "Symbol" from the global object
+ 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;
+
+ // Retrieve the function at the Symbol(hasInstance) key of the constructor
+ if (value_type == napi_symbol) {
+ status = napi_get_property(env, constructor, value, &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);
+ }
+ }
+ }
+
+ // If running constructor[Symbol.hasInstance](object) did not work, we perform
+ // a traditional instanceof (early Node.js 6.x).
+
+ v8::Local<v8::String> prototype_string;
+ CHECK_NEW_FROM_UTF8(isolate, prototype_string, "prototype");
+
+ auto maybe_prototype = ctor->Get(context, prototype_string);
+ CHECK_MAYBE_EMPTY(maybe_prototype, napi_generic_failure);
+
+ v8::Local<v8::Value> 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(napi_object_expected);
+ }
+
+ auto maybe_ctor = prototype_property->ToObject(context);
+ CHECK_MAYBE_EMPTY(maybe_ctor, napi_generic_failure);
+ ctor = maybe_ctor.ToLocalChecked();
+
+ v8::Local<v8::Value> current_obj = v8impl::V8LocalValueFromJsValue(object);
+ if (!current_obj->StrictEquals(ctor)) {
+ for (v8::Local<v8::Value> 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<v8::Object> obj;
+ CHECK_TO_OBJECT(context, obj, v8impl::JsValueFromV8LocalValue(
+ current_obj));
+ current_obj = obj->GetPrototype();
+ }
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+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 = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::Object> v8recv =
+ v8impl::V8LocalValueFromJsValue(recv).As<v8::Object>();
+ v8::Local<v8::Function> v8func =
+ v8impl::V8LocalValueFromJsValue(func).As<v8::Function>();
+
+ v8::Local<v8::Value> callback_result = node::MakeCallback(
+ isolate, v8recv, v8func, argc,
+ reinterpret_cast<v8::Local<v8::Value>*>(const_cast<napi_value*>(argv)));
+
+ if (result != nullptr) {
+ *result = v8impl::JsValueFromV8LocalValue(callback_result);
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+// 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_ARG(env);
+ CHECK_ARG(result);
+
+ *result = !v8impl::TryCatch::LastException().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_ARG(env);
+ CHECK_ARG(result);
+
+ if (v8impl::TryCatch::LastException().IsEmpty()) {
+ return napi_get_undefined(env, result);
+ } else {
+ *result = v8impl::JsValueFromV8LocalValue(v8::Local<v8::Value>::New(
+ v8impl::V8IsolateFromJsEnv(env), v8impl::TryCatch::LastException()));
+ v8impl::TryCatch::LastException().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(data);
+ CHECK_ARG(result);
+
+ auto maybe = node::Buffer::New(v8impl::V8IsolateFromJsEnv(env), length);
+
+ CHECK_MAYBE_EMPTY(maybe, napi_generic_failure);
+
+ v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
+
+ *result = v8impl::JsValueFromV8LocalValue(buffer);
+ *data = node::Buffer::Data(buffer);
+
+ return GET_RETURN_STATUS();
+}
+
+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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+
+ // The finalizer object will delete itself after invoking the callback.
+ v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
+ isolate, finalize_cb, nullptr, finalize_hint);
+
+ auto maybe = node::Buffer::New(isolate,
+ static_cast<char*>(data),
+ length,
+ v8impl::Finalizer::FinalizeBufferCallback,
+ finalizer);
+
+ CHECK_MAYBE_EMPTY(maybe, napi_generic_failure);
+
+ *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
+ return GET_RETURN_STATUS();
+}
+
+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(result);
+
+ auto maybe = node::Buffer::Copy(v8impl::V8IsolateFromJsEnv(env),
+ static_cast<const char*>(data), length);
+
+ CHECK_MAYBE_EMPTY(maybe, napi_generic_failure);
+
+ v8::Local<v8::Object> buffer = maybe.ToLocalChecked();
+ *result = v8impl::JsValueFromV8LocalValue(buffer);
+
+ if (result_data != nullptr) {
+ *result_data = node::Buffer::Data(buffer);
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ *result = node::Buffer::HasInstance(v8impl::V8LocalValueFromJsValue(value));
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_buffer_info(napi_env env,
+ napi_value value,
+ void** data,
+ size_t* length) {
+ NAPI_PREAMBLE(env);
+
+ v8::Local<v8::Object> buffer =
+ v8impl::V8LocalValueFromJsValue(value).As<v8::Object>();
+
+ if (data != nullptr) {
+ *data = node::Buffer::Data(buffer);
+ }
+ if (length != nullptr) {
+ *length = node::Buffer::Length(buffer);
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ *result = val->IsArrayBuffer();
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_create_arraybuffer(napi_env env,
+ size_t byte_length,
+ void** data,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::ArrayBuffer> 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();
+}
+
+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(result);
+
+ v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
+ v8::Local<v8::ArrayBuffer> 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(isolate,
+ buffer,
+ 0,
+ true,
+ finalize_cb,
+ external_data,
+ finalize_hint);
+ }
+
+ *result = v8impl::JsValueFromV8LocalValue(buffer);
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_get_arraybuffer_info(napi_env env,
+ napi_value arraybuffer,
+ void** data,
+ size_t* byte_length) {
+ NAPI_PREAMBLE(env);
+
+ v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
+ RETURN_STATUS_IF_FALSE(value->IsArrayBuffer(), napi_invalid_arg);
+
+ v8::ArrayBuffer::Contents contents =
+ value.As<v8::ArrayBuffer>()->GetContents();
+
+ if (data != nullptr) {
+ *data = contents.Data();
+ }
+
+ if (byte_length != nullptr) {
+ *byte_length = contents.ByteLength();
+ }
+
+ return GET_RETURN_STATUS();
+}
+
+napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(result);
+
+ v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
+ *result = val->IsTypedArray();
+
+ return GET_RETURN_STATUS();
+}
+
+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(result);
+
+ v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
+ RETURN_STATUS_IF_FALSE(value->IsArrayBuffer(), napi_invalid_arg);
+
+ v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
+ v8::Local<v8::TypedArray> 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(napi_invalid_arg);
+ }
+
+ *result = v8impl::JsValueFromV8LocalValue(typedArray);
+ return GET_RETURN_STATUS();
+}
+
+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<v8::Value> value = v8impl::V8LocalValueFromJsValue(typedarray);
+ RETURN_STATUS_IF_FALSE(value->IsTypedArray(), napi_invalid_arg);
+
+ v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
+
+ 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<v8::ArrayBuffer> buffer = array->Buffer();
+ if (data != nullptr) {
+ *data = static_cast<uint8_t*>(buffer->GetContents().Data()) +
+ array->ByteOffset();
+ }
+
+ if (arraybuffer != nullptr) {
+ *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer);
+ }
+
+ if (byte_offset != nullptr) {
+ *byte_offset = array->ByteOffset();
+ }
+
+ return GET_RETURN_STATUS();
+}
diff --git a/src/node_api.h b/src/node_api.h
new file mode 100644
index 0000000000..da14a01654
--- /dev/null
+++ b/src/node_api.h
@@ -0,0 +1,479 @@
+/******************************************************************************
+ * 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 <stddef.h>
+#include <stdbool.h>
+#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_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);
+
+// Gets the number of CHARACTERS in the string.
+NAPI_EXTERN napi_status napi_get_value_string_length(napi_env env,
+ napi_value value,
+ 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* thisArg, // [out] Receives the JS 'this' arg for the call
+ void** data); // [out] Receives the data pointer for the callback.
+
+NAPI_EXTERN napi_status napi_get_cb_args_length(napi_env env,
+ napi_callback_info cbinfo,
+ size_t* result);
+NAPI_EXTERN napi_status napi_get_cb_args(napi_env env,
+ napi_callback_info cbinfo,
+ napi_value* buf,
+ size_t bufsize);
+NAPI_EXTERN napi_status napi_get_cb_this(napi_env env,
+ napi_callback_info cbinfo,
+ napi_value* result);
+
+NAPI_EXTERN napi_status napi_get_cb_data(napi_env env,
+ napi_callback_info cbinfo,
+ void** result);
+NAPI_EXTERN napi_status napi_is_construct_call(napi_env env,
+ napi_callback_info cbinfo,
+ bool* result);
+NAPI_EXTERN napi_status napi_set_return_value(napi_env env,
+ napi_callback_info cbinfo,
+ napi_value value);
+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);
+EXTERN_C_END
+
+#endif // SRC_NODE_API_H__
diff --git a/src/node_api_types.h b/src/node_api_types.h
new file mode 100644
index 0000000000..7cb242368a
--- /dev/null
+++ b/src/node_api_types.h
@@ -0,0 +1,95 @@
+#ifndef SRC_NODE_API_TYPES_H_
+#define SRC_NODE_API_TYPES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#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 void (*napi_callback)(napi_env env,
+ napi_callback_info info);
+typedef void (*napi_finalize)(napi_env env,
+ void* finalize_data,
+ void* finalize_hint);
+
+typedef enum {
+ 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,
+} napi_property_attributes;
+
+typedef struct {
+ const char* utf8name;
+
+ napi_callback method;
+ napi_callback getter;
+ napi_callback setter;
+ napi_value value;
+
+ napi_property_attributes attributes;
+ void* data;
+} napi_property_descriptor;
+
+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_function_expected,
+ napi_number_expected,
+ napi_boolean_expected,
+ napi_array_expected,
+ napi_generic_failure,
+ napi_pending_exception,
+ napi_status_last
+} napi_status;
+
+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/test/addons-napi/.gitignore b/test/addons-napi/.gitignore
new file mode 100644
index 0000000000..bde1cf3ab9
--- /dev/null
+++ b/test/addons-napi/.gitignore
@@ -0,0 +1,7 @@
+.buildstamp
+.docbuildstamp
+Makefile
+*.Makefile
+*.mk
+gyp-mac-tool
+/*/build
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..882508b954
--- /dev/null
+++ b/test/addons-napi/1_hello_world/binding.c
@@ -0,0 +1,22 @@
+#include <node_api.h>
+
+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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+ napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);
+ status = napi_define_properties(env, exports, 1, &desc);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/1_hello_world/binding.gyp b/test/addons-napi/1_hello_world/binding.gyp
new file mode 100644
index 0000000000..62381d5e54
--- /dev/null
+++ b/test/addons-napi/1_hello_world/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "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..bdc25a235c
--- /dev/null
+++ b/test/addons-napi/2_function_arguments/binding.c
@@ -0,0 +1,58 @@
+#include <node_api.h>
+
+void Add(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(env, args[0], &valuetype0);
+ if (status != napi_ok) return;
+
+ napi_valuetype valuetype1;
+ status = napi_typeof(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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+ napi_property_descriptor addDescriptor = DECLARE_NAPI_METHOD("add", Add);
+ status = napi_define_properties(env, exports, 1, &addDescriptor);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/2_function_arguments/binding.gyp b/test/addons-napi/2_function_arguments/binding.gyp
new file mode 100644
index 0000000000..62381d5e54
--- /dev/null
+++ b/test/addons-napi/2_function_arguments/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "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..10e7318bce
--- /dev/null
+++ b/test/addons-napi/3_callbacks/binding.c
@@ -0,0 +1,51 @@
+#include <node_api.h>
+
+#define NAPI_CALL(env, theCall) \
+ if ((theCall) != napi_ok) { \
+ const napi_extended_error_info* error; \
+ napi_get_last_error_info((env), &error); \
+ const char* errorMessage = error->error_message; \
+ errorMessage = errorMessage ? errorMessage : "empty error message"; \
+ napi_throw_error((env), errorMessage); \
+ return; \
+ }
+
+void RunCallback(napi_env env, napi_callback_info info) {
+ napi_value args[1];
+ NAPI_CALL(env, napi_get_cb_args(env, info, args, 1));
+
+ napi_value cb = args[0];
+
+ napi_value argv[1];
+ NAPI_CALL(env, napi_create_string_utf8(env, "hello world", -1, argv));
+
+ napi_value global;
+ NAPI_CALL(env, napi_get_global(env, &global));
+
+ NAPI_CALL(env, napi_call_function(env, global, cb, 1, argv, NULL));
+}
+
+void RunCallbackWithRecv(napi_env env, napi_callback_info info) {
+ napi_value args[2];
+ NAPI_CALL(env, napi_get_cb_args(env, info, args, 2));
+
+ napi_value cb = args[0];
+ napi_value recv = args[1];
+
+ NAPI_CALL(env, napi_call_function(env, recv, cb, 0, NULL, NULL));
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+ napi_property_descriptor desc[2] = {
+ DECLARE_NAPI_METHOD("RunCallback", RunCallback),
+ DECLARE_NAPI_METHOD("RunCallbackWithRecv", RunCallbackWithRecv),
+ };
+ status = napi_define_properties(env, exports, 2, desc);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/3_callbacks/binding.gyp b/test/addons-napi/3_callbacks/binding.gyp
new file mode 100644
index 0000000000..62381d5e54
--- /dev/null
+++ b/test/addons-napi/3_callbacks/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "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..0d9902259f
--- /dev/null
+++ b/test/addons-napi/4_object_factory/binding.c
@@ -0,0 +1,31 @@
+#include <node_api.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 obj;
+ status = napi_create_object(env, &obj);
+ if (status != napi_ok) return;
+
+ status = napi_set_named_property(env, obj, "msg", args[0]);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, obj);
+ if (status != napi_ok) return;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+ napi_property_descriptor desc = DECLARE_NAPI_METHOD("exports", CreateObject);
+ status = napi_define_properties(env, module, 1, &desc);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/4_object_factory/binding.gyp b/test/addons-napi/4_object_factory/binding.gyp
new file mode 100644
index 0000000000..62381d5e54
--- /dev/null
+++ b/test/addons-napi/4_object_factory/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "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..867a527ee9
--- /dev/null
+++ b/test/addons-napi/5_function_factory/binding.c
@@ -0,0 +1,36 @@
+#include <node_api.h>
+
+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, "theFunction", MyFunction, NULL, &fn);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, fn);
+ if (status != napi_ok) return;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+ napi_property_descriptor desc =
+ DECLARE_NAPI_METHOD("exports", CreateFunction);
+ status = napi_define_properties(env, module, 1, &desc);
+ if (status != napi_ok) return;
+}
+
+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-napi/6_object_wrap/binding.gyp b/test/addons-napi/6_object_wrap/binding.gyp
new file mode 100644
index 0000000000..d8f91601e9
--- /dev/null
+++ b/test/addons-napi/6_object_wrap/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "sources": [ "binding.cc", "myobject.cc" ]
+ }
+ ]
+}
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..67521ae313
--- /dev/null
+++ b/test/addons-napi/6_object_wrap/myobject.cc
@@ -0,0 +1,201 @@
+#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(
+ napi_env env, void* nativeObject, void* /*finalize_hint*/) {
+ MyObject* obj = static_cast<MyObject*>(nativeObject);
+ delete obj;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void MyObject::Init(napi_env env, napi_value exports) {
+ napi_status status;
+ napi_property_descriptor properties[] = {
+ { "value", nullptr, GetValue, SetValue, 0, napi_default, 0 },
+ DECLARE_NAPI_METHOD("plusOne", PlusOne),
+ DECLARE_NAPI_METHOD("multiply", Multiply),
+ };
+
+ napi_value cons;
+ status =
+ napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons);
+ if (status != napi_ok) return;
+
+ status = napi_create_reference(env, cons, 1, &constructor);
+ if (status != napi_ok) return;
+
+ status = napi_set_named_property(env, exports, "MyObject", 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_typeof(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,
+ obj,
+ MyObject::Destructor,
+ nullptr, // finalize_hint
+ &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<void**>(&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<void**>(&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<void**>(&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_typeof(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<void**>(&obj));
+ if (status != napi_ok) return;
+
+ napi_value cons;
+ status = napi_get_reference_value(env, constructor, &cons);
+ if (status != napi_ok) return;
+
+ const int kArgCount = 1;
+ napi_value argv[kArgCount];
+ status = napi_create_number(env, obj->value_ * multiple, argv);
+ if (status != napi_ok) return;
+
+ napi_value instance;
+ status = napi_new_instance(env, cons, kArgCount, argv, &instance);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, instance);
+ if (status != napi_ok) return;
+}
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..2c8f477537
--- /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 <node_api.h>
+
+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 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 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..0aede8cbb8
--- /dev/null
+++ b/test/addons-napi/7_factory_wrap/binding.cc
@@ -0,0 +1,32 @@
+#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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ status = MyObject::Init(env);
+ if (status != napi_ok) return;
+
+ napi_property_descriptor desc = DECLARE_NAPI_METHOD("exports", CreateObject);
+ status = napi_define_properties(env, module, 1, &desc);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/7_factory_wrap/binding.gyp b/test/addons-napi/7_factory_wrap/binding.gyp
new file mode 100644
index 0000000000..d8f91601e9
--- /dev/null
+++ b/test/addons-napi/7_factory_wrap/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "sources": [ "binding.cc", "myobject.cc" ]
+ }
+ ]
+}
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..a3fbf6f9ec
--- /dev/null
+++ b/test/addons-napi/7_factory_wrap/myobject.cc
@@ -0,0 +1,110 @@
+#include "myobject.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<MyObject*>(nativeObject);
+ delete obj;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+napi_ref MyObject::constructor;
+
+napi_status MyObject::Init(napi_env env) {
+ napi_status status;
+ napi_property_descriptor properties[] = {
+ DECLARE_NAPI_METHOD("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_typeof(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,
+ obj,
+ MyObject::Destructor,
+ nullptr, /* finalize_hint */
+ &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<void**>(&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-napi/7_factory_wrap/myobject.h b/test/addons-napi/7_factory_wrap/myobject.h
new file mode 100644
index 0000000000..323079951e
--- /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 <node_api.h>
+
+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 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_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..d6a4eafd88
--- /dev/null
+++ b/test/addons-napi/8_passing_wrapped/binding.cc
@@ -0,0 +1,57 @@
+#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<void**>(&obj1));
+ if (status != napi_ok) return;
+
+ MyObject* obj2;
+ status = napi_unwrap(env, args[1], reinterpret_cast<void**>(&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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ MyObject::Init(env);
+
+ napi_property_descriptor desc[] = {
+ DECLARE_NAPI_METHOD("createObject", CreateObject),
+ DECLARE_NAPI_METHOD("add", Add),
+ };
+ status =
+ napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/8_passing_wrapped/binding.gyp b/test/addons-napi/8_passing_wrapped/binding.gyp
new file mode 100644
index 0000000000..d8f91601e9
--- /dev/null
+++ b/test/addons-napi/8_passing_wrapped/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "binding",
+ "sources": [ "binding.cc", "myobject.cc" ]
+ }
+ ]
+}
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..8bca17a3be
--- /dev/null
+++ b/test/addons-napi/8_passing_wrapped/myobject.cc
@@ -0,0 +1,81 @@
+#include "myobject.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<MyObject*>(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;
+}
+
+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_typeof(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,
+ obj,
+ MyObject::Destructor,
+ nullptr, // finalize_hint
+ &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-napi/8_passing_wrapped/myobject.h b/test/addons-napi/8_passing_wrapped/myobject.h
new file mode 100644
index 0000000000..773c86e04b
--- /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 <node_api.h>
+
+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 void 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/test_array/binding.gyp b/test/addons-napi/test_array/binding.gyp
new file mode 100644
index 0000000000..44920de098
--- /dev/null
+++ b/test/addons-napi/test_array/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_array",
+ "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..e5c4be4e8f
--- /dev/null
+++ b/test/addons-napi/test_array/test_array.c
@@ -0,0 +1,137 @@
+#include <node_api.h>
+#include <string.h>
+
+void Test(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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_typeof(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 >= (int)(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;
+
+ size_t 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_typeof(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 i, length;
+ status = napi_get_array_length(env, args[0], &length);
+ if (status != napi_ok) return;
+
+ for (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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("Test", Test),
+ DECLARE_NAPI_METHOD("New", New),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_buffer/binding.gyp b/test/addons-napi/test_buffer/binding.gyp
new file mode 100644
index 0000000000..e41a3993cd
--- /dev/null
+++ b/test/addons-napi/test_buffer/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_buffer",
+ "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..cfb8f44cc7
--- /dev/null
+++ b/test/addons-napi/test_buffer/test_buffer.c
@@ -0,0 +1,160 @@
+#include <stdlib.h>
+#include <string.h>
+#include <node_api.h>
+
+#define JS_ASSERT(env, assertion, message) \
+ if (!(assertion)) { \
+ napi_throw_error( \
+ (env), \
+ "assertion (" #assertion ") failed: " message); \
+ return; \
+ }
+
+#define NAPI_CALL(env, theCall) \
+ if ((theCall) != napi_ok) { \
+ const napi_extended_error_info* error; \
+ napi_get_last_error_info((env), &error); \
+ const char* errorMessage = error->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(napi_env env, void* data, void* finalize_hint) {
+ JS_ASSERT(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) {
+ JS_ASSERT(env, data != NULL && strcmp(data, theText) == 0, "invalid data");
+ (void)finalize_hint;
+ deleterCallCount++;
+}
+
+void 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));
+ JS_ASSERT(env, theCopy, "Failed to copy static text for newBuffer");
+ memcpy(theCopy, theText, kBufferSize);
+ NAPI_CALL(env, napi_set_return_value(env, info, theBuffer));
+}
+
+void newExternalBuffer(napi_env env, napi_callback_info info) {
+ 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,
+ NULL, // finalize_hint
+ &theBuffer));
+ NAPI_CALL(env, napi_set_return_value(env, info, theBuffer));
+}
+
+void getDeleterCallCount(napi_env env, napi_callback_info info) {
+ napi_value callCount;
+ NAPI_CALL(env, napi_create_number(env, deleterCallCount, &callCount));
+ NAPI_CALL(env, napi_set_return_value(env, info, callCount));
+}
+
+void copyBuffer(napi_env env, napi_callback_info info) {
+ napi_value theBuffer;
+ NAPI_CALL(env, napi_create_buffer_copy(
+ env, sizeof(theText), theText, NULL, &theBuffer));
+ NAPI_CALL(env, napi_set_return_value(env, info, theBuffer));
+}
+
+void bufferHasInstance(napi_env env, napi_callback_info info) {
+ size_t 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_typeof(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_get_boolean(env, hasInstance, &returnValue));
+ NAPI_CALL(env, napi_set_return_value(env, info, returnValue));
+}
+
+void bufferInfo(napi_env env, napi_callback_info info) {
+ size_t 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,
+ (void**)(&bufferData),
+ &bufferLength));
+ NAPI_CALL(env, napi_get_boolean(env,
+ !strcmp(bufferData, theText) && bufferLength == sizeof(theText),
+ &returnValue));
+ NAPI_CALL(env, napi_set_return_value(env, info, returnValue));
+}
+
+void 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));
+ NAPI_CALL(env, napi_set_return_value(env, info, theBuffer));
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_value theValue;
+
+ NAPI_CALL(env, napi_create_string_utf8(env,
+ theText, sizeof(theText), &theValue));
+ NAPI_CALL(env, napi_set_named_property(env, exports, "theText", theValue));
+
+ napi_property_descriptor methods[] = {
+ DECLARE_NAPI_METHOD("newBuffer", newBuffer),
+ DECLARE_NAPI_METHOD("newExternalBuffer", newExternalBuffer),
+ DECLARE_NAPI_METHOD("getDeleterCallCount", getDeleterCallCount),
+ DECLARE_NAPI_METHOD("copyBuffer", copyBuffer),
+ DECLARE_NAPI_METHOD("bufferHasInstance", bufferHasInstance),
+ DECLARE_NAPI_METHOD("bufferInfo", bufferInfo),
+ DECLARE_NAPI_METHOD("staticBuffer", staticBuffer),
+ };
+ NAPI_CALL(env,
+ napi_define_properties(
+ env, exports, sizeof(methods) / sizeof(methods[0]), methods));
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_constructor/binding.gyp b/test/addons-napi/test_constructor/binding.gyp
new file mode 100644
index 0000000000..55140e7c37
--- /dev/null
+++ b/test/addons-napi/test_constructor/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_constructor",
+ "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..9feae5a136
--- /dev/null
+++ b/test/addons-napi/test_constructor/test.js
@@ -0,0 +1,28 @@
+'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; });
+
+assert.ok(test_object.hiddenValue);
+
+// All properties except 'hiddenValue' 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);
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..5a45da3073
--- /dev/null
+++ b/test/addons-napi/test_constructor/test_constructor.c
@@ -0,0 +1,104 @@
+#include <node_api.h>
+
+static double value_ = 1;
+napi_ref constructor_;
+
+void GetValue(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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;
+
+ size_t 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;
+
+ size_t 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, void* priv) {
+ napi_status status;
+
+ napi_value number;
+ status = napi_create_number(env, value_, &number);
+ if (status != napi_ok) return;
+
+ napi_property_descriptor properties[] = {
+ { "echo", Echo, 0, 0, 0, napi_default, 0 },
+ { "accessorValue", 0, GetValue, SetValue, 0, napi_default, 0},
+ { "readwriteValue", 0, 0, 0, number, napi_default, 0 },
+ { "readonlyValue", 0, 0, 0, number, napi_read_only, 0},
+ { "hiddenValue", 0, 0, 0, number, napi_read_only | napi_dont_enum, 0},
+ };
+
+ napi_value cons;
+ status = napi_define_class(env, "MyObject", New,
+ NULL, sizeof(properties)/sizeof(*properties), properties, &cons);
+ if (status != napi_ok) return;
+
+ status = napi_set_named_property(env, module, "exports", cons);
+ if (status != napi_ok) return;
+
+ status = napi_create_reference(env, cons, 1, &constructor_);
+ if (status != napi_ok) return;
+}
+
+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..5fc8b0f263
--- /dev/null
+++ b/test/addons-napi/test_conversions/test_conversions.c
@@ -0,0 +1,237 @@
+#include <node_api.h>
+
+void ThrowLastError(napi_env env) {
+ const napi_extended_error_info* error_info;
+ napi_get_last_error_info(env, &error_info);
+ if (error_info->error_code != napi_ok) {
+ napi_throw_error(env, error_info->error_message);
+ }
+}
+
+void AsBool(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ bool value;
+ status = napi_get_value_bool(env, input, &value);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_get_boolean(env, value, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void AsInt32(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ int32_t value;
+ status = napi_get_value_int32(env, input, &value);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_create_number(env, value, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void AsUInt32(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ uint32_t value;
+ status = napi_get_value_uint32(env, input, &value);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_create_number(env, value, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void AsInt64(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ int64_t value;
+ status = napi_get_value_int64(env, input, &value);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_create_number(env, (double)value, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void AsDouble(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ double value;
+ status = napi_get_value_double(env, input, &value);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_create_number(env, value, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void AsString(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ char value[100];
+ status = napi_get_value_string_utf8(env, input, value, sizeof(value), NULL);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_create_string_utf8(env, value, -1, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void ToBool(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_coerce_to_bool(env, input, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void ToNumber(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_coerce_to_number(env, input, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void ToObject(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_coerce_to_object(env, input, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+void ToString(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value input;
+ status = napi_get_cb_args(env, info, &input, 1);
+ if (status != napi_ok) goto done;
+
+ napi_value output;
+ status = napi_coerce_to_string(env, input, &output);
+ if (status != napi_ok) goto done;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) goto done;
+
+done:
+ if (status != napi_ok) ThrowLastError(env);
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("asBool", AsBool),
+ DECLARE_NAPI_METHOD("asInt32", AsInt32),
+ DECLARE_NAPI_METHOD("asUInt32", AsUInt32),
+ DECLARE_NAPI_METHOD("asInt64", AsInt64),
+ DECLARE_NAPI_METHOD("asDouble", AsDouble),
+ DECLARE_NAPI_METHOD("asString", AsString),
+ DECLARE_NAPI_METHOD("toBool", ToBool),
+ DECLARE_NAPI_METHOD("toNumber", ToNumber),
+ DECLARE_NAPI_METHOD("toObject", ToObject),
+ DECLARE_NAPI_METHOD("toString", ToString),
+ };
+
+ napi_status status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+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..1395cb5c4e
--- /dev/null
+++ b/test/addons-napi/test_error/test_error.cc
@@ -0,0 +1,37 @@
+#include <node_api.h>
+
+void checkError(napi_env e, napi_callback_info info) {
+ napi_status status;
+ napi_value jsError;
+
+ status = napi_get_cb_args(e, info, &jsError, 1);
+ if (status != napi_ok) return;
+
+ bool r;
+ status = napi_is_error(e, jsError, &r);
+ if (status != napi_ok) return;
+
+ napi_value result;
+ status = napi_get_boolean(e, r, &result);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(e, info, result);
+ if (status != napi_ok) return;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("checkError", checkError),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_exception/binding.gyp b/test/addons-napi/test_exception/binding.gyp
new file mode 100644
index 0000000000..d2e4586e46
--- /dev/null
+++ b/test/addons-napi/test_exception/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_exception",
+ "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..f92bae323d
--- /dev/null
+++ b/test/addons-napi/test_exception/test_exception.c
@@ -0,0 +1,75 @@
+#include <node_api.h>
+
+static bool exceptionWasPending = false;
+
+void returnException(napi_env env, napi_callback_info info) {
+ 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;
+ }
+}
+
+void allowException(napi_env env, napi_callback_info info) {
+ 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;
+}
+
+void wasPending(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ napi_value result;
+ status = napi_get_boolean(env, exceptionWasPending, &result);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, result);
+ if (status != napi_ok) return;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("returnException", returnException),
+ DECLARE_NAPI_METHOD("allowException", allowException),
+ DECLARE_NAPI_METHOD("wasPending", wasPending),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_function/binding.gyp b/test/addons-napi/test_function/binding.gyp
new file mode 100644
index 0000000000..2b015bddd7
--- /dev/null
+++ b/test/addons-napi/test_function/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_function",
+ "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..1bd421e7bc
--- /dev/null
+++ b/test/addons-napi/test_function/test_function.c
@@ -0,0 +1,55 @@
+#include <node_api.h>
+
+void Test(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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, void* priv) {
+ napi_status status;
+
+ napi_value fn;
+ status = napi_create_function(env, NULL, Test, NULL, &fn);
+ if (status != napi_ok) return;
+
+ status = napi_set_named_property(env, exports, "Test", fn);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_instanceof/binding.gyp b/test/addons-napi/test_instanceof/binding.gyp
new file mode 100644
index 0000000000..7fca7e0736
--- /dev/null
+++ b/test/addons-napi/test_instanceof/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_instanceof",
+ "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..76df14eb39
--- /dev/null
+++ b/test/addons-napi/test_instanceof/test_instanceof.c
@@ -0,0 +1,39 @@
+#include <node_api.h>
+#include <stdio.h>
+
+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_get_boolean(env, instanceof, &result);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, result);
+ if (status != napi_ok) return;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("doInstanceOf", doInstanceOf),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_number/binding.gyp b/test/addons-napi/test_number/binding.gyp
new file mode 100644
index 0000000000..c934d5ef03
--- /dev/null
+++ b/test/addons-napi/test_number/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_number",
+ "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..f38c09d681
--- /dev/null
+++ b/test/addons-napi/test_number/test_number.c
@@ -0,0 +1,55 @@
+#include <node_api.h>
+
+void Test(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("Test", Test),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_object/binding.gyp b/test/addons-napi/test_object/binding.gyp
new file mode 100644
index 0000000000..be225ace77
--- /dev/null
+++ b/test/addons-napi/test_object/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_object",
+ "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..6080201877
--- /dev/null
+++ b/test/addons-napi/test_object/test_object.c
@@ -0,0 +1,246 @@
+#include <node_api.h>
+
+void Get(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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_typeof(env, args[1], &valuetype1);
+ if (status != napi_ok) return;
+
+ if (valuetype1 != napi_string && valuetype1 != napi_symbol) {
+ napi_throw_type_error(env,
+ "Wrong type of argments. Expects a string or symbol as second.");
+ return;
+ }
+
+ napi_value object = args[0];
+ napi_value output;
+ status = napi_get_property(env, object, args[1], &output);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) return;
+}
+
+void Set(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t argc;
+ status = napi_get_cb_args_length(env, info, &argc);
+ if (status != napi_ok) return;
+
+ if (argc < 3) {
+ napi_throw_type_error(env, "Wrong number of arguments");
+ return;
+ }
+
+ napi_value args[3];
+ status = napi_get_cb_args(env, info, args, 3);
+ if (status != napi_ok) return;
+
+ napi_valuetype valuetype0;
+ status = napi_typeof(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_typeof(env, args[1], &valuetype1);
+ if (status != napi_ok) return;
+
+ if (valuetype1 != napi_string && valuetype1 != napi_symbol) {
+ napi_throw_type_error(env,
+ "Wrong type of argments. Expects a string or symbol as second.");
+ return;
+ }
+
+ napi_value object = args[0];
+ status = napi_set_property(env, object, args[1], args[2]);
+ if (status != napi_ok) return;
+
+ napi_value valuetrue;
+ status = napi_get_boolean(env, true, &valuetrue);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, valuetrue);
+ if (status != napi_ok) return;
+}
+
+void Has(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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_typeof(env, args[1], &valuetype1);
+ if (status != napi_ok) return;
+
+ if (valuetype1 != napi_string && valuetype1 != napi_symbol) {
+ napi_throw_type_error(env,
+ "Wrong type of argments. Expects a string or symbol as second.");
+ return;
+ }
+
+ napi_value obj = args[0];
+ bool has_property;
+ status = napi_has_property(env, obj, args[1], &has_property);
+ if (status != napi_ok) return;
+
+ napi_value ret;
+ status = napi_get_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_value num;
+ status = napi_create_number(env, 987654321, &num);
+ if (status != napi_ok) return;
+
+ status = napi_set_named_property(env, ret, "test_number", num);
+ 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_named_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;
+
+ size_t 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_typeof(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_property_names(env, obj, &propertynames);
+ if (status != napi_ok) return;
+
+ uint32_t i, length;
+ status = napi_get_array_length(env, propertynames, &length);
+ if (status != napi_ok) return;
+
+ for (i = 0; i < length; i++) {
+ napi_value property_str;
+ status = napi_get_element(env, propertynames, i, &property_str);
+ if (status != napi_ok) return;
+
+ napi_value value;
+ status = napi_get_property(env, obj, property_str, &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, property_str, value);
+ if (status != napi_ok) return;
+ }
+ status = napi_set_return_value(env, info, obj);
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("Get", Get),
+ DECLARE_NAPI_METHOD("Set", Set),
+ DECLARE_NAPI_METHOD("Has", Has),
+ DECLARE_NAPI_METHOD("New", New),
+ DECLARE_NAPI_METHOD("Inflate", Inflate),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_properties/binding.gyp b/test/addons-napi/test_properties/binding.gyp
new file mode 100644
index 0000000000..345e5c88d7
--- /dev/null
+++ b/test/addons-napi/test_properties/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_properties",
+ "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..8e19903dcf
--- /dev/null
+++ b/test/addons-napi/test_properties/test.js
@@ -0,0 +1,27 @@
+'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; });
+
+assert.ok(test_object.hiddenValue);
+
+// All properties except 'hiddenValue' 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);
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..9474e97266
--- /dev/null
+++ b/test/addons-napi/test_properties/test_properties.c
@@ -0,0 +1,85 @@
+#include <node_api.h>
+
+static double value_ = 1;
+
+void GetValue(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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;
+
+ size_t 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;
+
+ size_t 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, void* priv) {
+ napi_status status;
+
+ napi_value number;
+ status = napi_create_number(env, value_, &number);
+ if (status != napi_ok) return;
+
+ napi_property_descriptor properties[] = {
+ { "echo", Echo, 0, 0, 0, napi_default, 0 },
+ { "accessorValue", 0, GetValue, SetValue, 0, napi_default, 0 },
+ { "readwriteValue", 0, 0, 0, number, napi_default, 0 },
+ { "readonlyValue", 0, 0, 0, number, napi_read_only, 0 },
+ { "hiddenValue", 0, 0, 0, number, napi_read_only | napi_dont_enum, 0 },
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(properties) / sizeof(*properties), properties);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_string/binding.gyp b/test/addons-napi/test_string/binding.gyp
new file mode 100644
index 0000000000..d4825de933
--- /dev/null
+++ b/test/addons-napi/test_string/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_string",
+ "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..3c9334a561
--- /dev/null
+++ b/test/addons-napi/test_string/test.js
@@ -0,0 +1,26 @@
+'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 str1 = 'hello world';
+assert.strictEqual(test_string.Copy(str1), str1);
+assert.strictEqual(test_string.Length(str1), 11);
+assert.strictEqual(test_string.Utf8Length(str1), 11);
+
+const str2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+assert.strictEqual(test_string.Copy(str2), str2);
+assert.strictEqual(test_string.Length(str2), 62);
+assert.strictEqual(test_string.Utf8Length(str2), 62);
+
+const str3 = '?!@#$%^&*()_+-=[]{}/.,<>\'"\\';
+assert.strictEqual(test_string.Copy(str3), str3);
+assert.strictEqual(test_string.Length(str3), 27);
+assert.strictEqual(test_string.Utf8Length(str3), 27);
+
+const str4 = '\u{2003}\u{2101}\u{2001}';
+assert.strictEqual(test_string.Copy(str4), str4);
+assert.strictEqual(test_string.Length(str4), 3);
+assert.strictEqual(test_string.Utf8Length(str4), 9);
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..1e9c8109d9
--- /dev/null
+++ b/test/addons-napi/test_string/test_string.c
@@ -0,0 +1,134 @@
+#include <node_api.h>
+
+void Copy(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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, NULL);
+ 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;
+
+ size_t 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_typeof(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;
+ }
+
+ size_t length;
+ status = napi_get_value_string_length(env, args[0], &length);
+ if (status != napi_ok) return;
+
+ napi_value output;
+ status = napi_create_number(env, (double)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;
+
+ size_t 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_typeof(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;
+ }
+
+ size_t length;
+ status = napi_get_value_string_utf8(env, args[0], NULL, 0, &length);
+ if (status != napi_ok) return;
+
+ napi_value output;
+ status = napi_create_number(env, (double)length, &output);
+ if (status != napi_ok) return;
+
+ status = napi_set_return_value(env, info, output);
+ if (status != napi_ok) return;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor properties[] = {
+ DECLARE_NAPI_METHOD("Copy", Copy),
+ DECLARE_NAPI_METHOD("Length", Length),
+ DECLARE_NAPI_METHOD("Utf8Length", Utf8Length),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(properties) / sizeof(*properties), properties);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_symbol/binding.gyp b/test/addons-napi/test_symbol/binding.gyp
new file mode 100644
index 0000000000..6ef3407968
--- /dev/null
+++ b/test/addons-napi/test_symbol/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_symbol",
+ "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..af694b1dd3
--- /dev/null
+++ b/test/addons-napi/test_symbol/test_symbol.c
@@ -0,0 +1,94 @@
+#include <node_api.h>
+
+void Test(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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, NULL);
+ 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;
+
+ size_t 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_typeof(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;
+ }
+
+ napi_value symbol;
+ status = napi_create_symbol(env, args[0], &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;
+ }
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor properties[] = {
+ DECLARE_NAPI_METHOD("New", New),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(properties) / sizeof(*properties), properties);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/test_typedarray/binding.gyp b/test/addons-napi/test_typedarray/binding.gyp
new file mode 100644
index 0000000000..8b4a4dc622
--- /dev/null
+++ b/test/addons-napi/test_typedarray/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_typedarray",
+ "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..00217a8cc0
--- /dev/null
+++ b/test/addons-napi/test_typedarray/test_typedarray.c
@@ -0,0 +1,144 @@
+#include <node_api.h>
+#include <string.h>
+
+void Multiply(napi_env env, napi_callback_info info) {
+ napi_status status;
+
+ size_t 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_typeof(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_typeof(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 i, length;
+ status = napi_get_typedarray_info(
+ env, input_array, &type, &length, NULL, &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 = NULL;
+ 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_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;
+ }
+
+ 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),
+ NULL, // finalize_callback
+ NULL, // finalize_hint
+ &output_buffer);
+ if (status != napi_ok) return;
+
+ napi_value output_array;
+ status = napi_create_typedarray(env,
+ napi_int8_array,
+ 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;
+}
+
+#define DECLARE_NAPI_METHOD(name, func) \
+ { name, func, 0, 0, 0, napi_default, 0 }
+
+void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
+ napi_status status;
+
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NAPI_METHOD("Multiply", Multiply),
+ DECLARE_NAPI_METHOD("External", External),
+ };
+
+ status = napi_define_properties(
+ env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors);
+ if (status != napi_ok) return;
+}
+
+NAPI_MODULE(addon, Init)
diff --git a/test/addons-napi/testcfg.py b/test/addons-napi/testcfg.py
new file mode 100644
index 0000000000..ef7c18fdae
--- /dev/null
+++ b/test/addons-napi/testcfg.py
@@ -0,0 +1,6 @@
+import sys, os
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+import testpy
+
+def GetConfiguration(context, root):
+ return testpy.AddonTestConfiguration(context, root, 'addons-napi', ['--napi-modules'])
diff --git a/test/testpy/__init__.py b/test/testpy/__init__.py
index d7ec88992e..f999b6a6ba 100644
--- a/test/testpy/__init__.py
+++ b/test/testpy/__init__.py
@@ -162,5 +162,5 @@ class AddonTestConfiguration(SimpleTestConfiguration):
if self.Contains(path, test):
file_path = join(self.root, reduce(join, test[1:], "") + ".js")
result.append(
- SimpleTestCase(test, file_path, arch, mode, self.context, self))
+ SimpleTestCase(test, file_path, arch, mode, self.context, self, self.additional_flags))
return result
diff --git a/tools/install.py b/tools/install.py
index fdaf2b2e7a..2235cb7350 100755
--- a/tools/install.py
+++ b/tools/install.py
@@ -148,6 +148,8 @@ def headers(action):
'common.gypi',
'config.gypi',
'src/node.h',
+ 'src/node_api.h',
+ 'src/node_api_types.h',
'src/node_buffer.h',
'src/node_object_wrap.h',
'src/node_version.h',
diff --git a/tools/test.py b/tools/test.py
index deeb7aeffe..6e1262931f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -1529,6 +1529,7 @@ BUILT_IN_TESTS = [
'message',
'internet',
'addons',
+ 'addons-napi',
'gc',
'debugger',
'doctool',
diff --git a/vcbuild.bat b/vcbuild.bat
index 9cbac9088f..985b7f1fcc 100644
--- a/vcbuild.bat
+++ b/vcbuild.bat
@@ -40,6 +40,7 @@ set enable_vtune_arg=
set configure_flags=
set build_addons=
set dll=
+set build_addons_napi=
set test_node_inspect=
:next-arg
@@ -59,9 +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 doctool known_issues message parallel sequential -J&set cpplint=1&set jslint=1&set build_addons=1&goto arg-ok
-if /i "%1"=="test-ci" set test_args=%test_args% %test_ci_args% -p tap --logfile test.tap addons doctool inspector known_issues message sequential parallel&set cctest_args=%cctest_args% --gtest_output=tap:cctest.tap&set build_addons=1&goto arg-ok
+if /i "%1"=="test" set test_args=%test_args% addons addons-napi doctool known_issues message parallel sequential -J&set cpplint=1&set jslint=1&set build_addons=1&set build_addons_napi=1&goto 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_napi=1&goto 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-napi" set test_args=%test_args% addons-napi&set build_addons_napi=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 build_testgc_addon=1&goto arg-ok
@@ -312,12 +314,12 @@ echo Failed to build test/gc add-on."
goto exit
:build-addons
-if not defined build_addons goto run-tests
+if not defined build_addons goto build-addons-napi
if not exist "%node_exe%" (
echo Failed to find node.exe
- goto run-tests
+ goto build-addons-napi
)
-echo Building add-ons
+echo Building addons
:: clear
for /d %%F in (test\addons\??_*) do (
rd /s /q %%F
@@ -333,6 +335,24 @@ for /d %%F in (test\addons\*) do (
--nodedir="%cd%"
if !errorlevel! neq 0 exit /b !errorlevel!
)
+
+:build-addons-napi
+if not defined build_addons_napi goto run-tests
+if not exist "%node_exe%" (
+ echo Failed to find node.exe
+ goto run-tests
+)
+echo Building addons-napi
+:: clear
+for /d %%F in (test\addons-napi\??_*) do (
+ rd /s /q %%F
+)
+:: 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%"
+)
endlocal
goto run-tests