From 56e881d0b0b2997b518753cb627eb3b50eeb6f62 Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Mon, 20 Mar 2017 14:55:26 -0700 Subject: [PATCH] n-api: add support for abi stable module API Add support for abi stable module API (N-API) as "Experimental feature". The goal of this API is to provide a stable Node API for native module developers. N-API aims to provide ABI compatibility guarantees across different Node versions and also across different Node VMs - allowing N-API enabled native modules to just work across different versions and flavors of Node.js without recompilation. A more detailed introduction is provided in: https://github.com/nodejs/node-eps/blob/master/005-ABI-Stable-Module-API.md and https://github.com/nodejs/abi-stable-node/blob/doc/VM%20Summit.pdf. The feature, during its experimental state, will be guarded by a runtime flag "--napi-modules". Only when this flag is added to the command line will N-API modules along with regular non N-API modules be supported. The API is defined by the methods in "src/node_api.h" and "src/node_api_types.h". This is the best starting point to review the API surface. More documentation will follow. In addition to the implementation of the API using V8, which is included in this PR, the API has also been validated against chakracore and that port is available in https://github.com/nodejs/abi-stable-node/tree/api-prototype-chakracore-8.x. The current plan is to provide N-API support in versions 8.X and 6.X directly. For older versions, such as 4.X or pre N-API versions of 6.X, we plan to create an external npm module to provide a migration path that will allow modules targeting older Node.js versions to use the API, albeit without getting the advantage of not having to recompile. In addition, we also plan an external npm package with C++ sugar to simplify the use of the API. The sugar will be in-line only and will only use the exported N-API methods but is not part of the N-API itself. The current version is in: https://github.com/nodejs/node-api. This PR is a result of work in the abi-stable-node repo: https://github.com/nodejs/abi-stable-node/tree/doc, with this PR being the cumulative work on the api-prototype-8.x branch with the following contributors in alphabetical order: Author: Arunesh Chandra Author: Gabriel Schulhof Author: Hitesh Kanwathirtha Author: Ian Halliday Author: Jason Ginchereau Author: Michael Dawson Author: Sampson Gao Author: Taylor Woll PR-URL: https://github.com/nodejs/node/pull/11975 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- Makefile | 60 +- doc/api/cli.md | 8 + doc/node.1 | 5 + node.gyp | 3 + src/node.cc | 39 +- src/node_api.cc | 2528 +++++++++++++++++ src/node_api.h | 479 ++++ src/node_api_types.h | 95 + test/addons-napi/.gitignore | 7 + test/addons-napi/1_hello_world/binding.c | 22 + test/addons-napi/1_hello_world/binding.gyp | 8 + test/addons-napi/1_hello_world/test.js | 6 + .../2_function_arguments/binding.c | 58 + .../2_function_arguments/binding.gyp | 8 + test/addons-napi/2_function_arguments/test.js | 6 + test/addons-napi/3_callbacks/binding.c | 51 + test/addons-napi/3_callbacks/binding.gyp | 8 + test/addons-napi/3_callbacks/test.js | 22 + test/addons-napi/4_object_factory/binding.c | 31 + test/addons-napi/4_object_factory/binding.gyp | 8 + test/addons-napi/4_object_factory/test.js | 8 + test/addons-napi/5_function_factory/binding.c | 36 + .../5_function_factory/binding.gyp | 8 + test/addons-napi/5_function_factory/test.js | 7 + test/addons-napi/6_object_wrap/binding.cc | 7 + test/addons-napi/6_object_wrap/binding.gyp | 8 + test/addons-napi/6_object_wrap/myobject.cc | 201 ++ test/addons-napi/6_object_wrap/myobject.h | 26 + test/addons-napi/6_object_wrap/test.js | 19 + test/addons-napi/7_factory_wrap/binding.cc | 32 + test/addons-napi/7_factory_wrap/binding.gyp | 8 + test/addons-napi/7_factory_wrap/myobject.cc | 110 + test/addons-napi/7_factory_wrap/myobject.h | 26 + test/addons-napi/7_factory_wrap/test.js | 14 + test/addons-napi/8_passing_wrapped/binding.cc | 57 + .../addons-napi/8_passing_wrapped/binding.gyp | 8 + .../addons-napi/8_passing_wrapped/myobject.cc | 81 + test/addons-napi/8_passing_wrapped/myobject.h | 26 + test/addons-napi/8_passing_wrapped/test.js | 9 + test/addons-napi/test_array/binding.gyp | 8 + test/addons-napi/test_array/test.js | 37 + test/addons-napi/test_array/test_array.c | 137 + test/addons-napi/test_buffer/binding.gyp | 8 + test/addons-napi/test_buffer/test.js | 25 + test/addons-napi/test_buffer/test_buffer.c | 160 ++ test/addons-napi/test_constructor/binding.gyp | 8 + test/addons-napi/test_constructor/test.js | 28 + .../test_constructor/test_constructor.c | 104 + test/addons-napi/test_conversions/binding.gyp | 8 + test/addons-napi/test_conversions/test.js | 140 + .../test_conversions/test_conversions.c | 237 ++ test/addons-napi/test_error/binding.gyp | 8 + test/addons-napi/test_error/test.js | 57 + test/addons-napi/test_error/test_error.cc | 37 + test/addons-napi/test_exception/binding.gyp | 8 + test/addons-napi/test_exception/test.js | 53 + .../test_exception/test_exception.c | 75 + test/addons-napi/test_function/binding.gyp | 8 + test/addons-napi/test_function/test.js | 28 + .../addons-napi/test_function/test_function.c | 55 + test/addons-napi/test_instanceof/binding.gyp | 8 + test/addons-napi/test_instanceof/test.js | 87 + .../test_instanceof/test_instanceof.c | 39 + test/addons-napi/test_number/binding.gyp | 8 + test/addons-napi/test_number/test.js | 39 + test/addons-napi/test_number/test_number.c | 55 + test/addons-napi/test_object/binding.gyp | 8 + test/addons-napi/test_object/test.js | 65 + test/addons-napi/test_object/test_object.c | 246 ++ test/addons-napi/test_properties/binding.gyp | 8 + test/addons-napi/test_properties/test.js | 27 + .../test_properties/test_properties.c | 85 + test/addons-napi/test_string/binding.gyp | 8 + test/addons-napi/test_string/test.js | 26 + test/addons-napi/test_string/test_string.c | 134 + test/addons-napi/test_symbol/binding.gyp | 8 + test/addons-napi/test_symbol/test1.js | 20 + test/addons-napi/test_symbol/test2.js | 15 + test/addons-napi/test_symbol/test3.js | 19 + test/addons-napi/test_symbol/test_symbol.c | 94 + test/addons-napi/test_typedarray/binding.gyp | 8 + test/addons-napi/test_typedarray/test.js | 39 + .../test_typedarray/test_typedarray.c | 144 + test/addons-napi/testcfg.py | 6 + test/testpy/__init__.py | 2 +- tools/install.py | 2 + tools/test.py | 1 + vcbuild.bat | 30 +- 88 files changed, 6580 insertions(+), 23 deletions(-) create mode 100644 src/node_api.cc create mode 100644 src/node_api.h create mode 100644 src/node_api_types.h create mode 100644 test/addons-napi/.gitignore create mode 100644 test/addons-napi/1_hello_world/binding.c create mode 100644 test/addons-napi/1_hello_world/binding.gyp create mode 100644 test/addons-napi/1_hello_world/test.js create mode 100644 test/addons-napi/2_function_arguments/binding.c create mode 100644 test/addons-napi/2_function_arguments/binding.gyp create mode 100644 test/addons-napi/2_function_arguments/test.js create mode 100644 test/addons-napi/3_callbacks/binding.c create mode 100644 test/addons-napi/3_callbacks/binding.gyp create mode 100644 test/addons-napi/3_callbacks/test.js create mode 100644 test/addons-napi/4_object_factory/binding.c create mode 100644 test/addons-napi/4_object_factory/binding.gyp create mode 100644 test/addons-napi/4_object_factory/test.js create mode 100644 test/addons-napi/5_function_factory/binding.c create mode 100644 test/addons-napi/5_function_factory/binding.gyp create mode 100644 test/addons-napi/5_function_factory/test.js create mode 100644 test/addons-napi/6_object_wrap/binding.cc create mode 100644 test/addons-napi/6_object_wrap/binding.gyp create mode 100644 test/addons-napi/6_object_wrap/myobject.cc create mode 100644 test/addons-napi/6_object_wrap/myobject.h create mode 100644 test/addons-napi/6_object_wrap/test.js create mode 100644 test/addons-napi/7_factory_wrap/binding.cc create mode 100644 test/addons-napi/7_factory_wrap/binding.gyp create mode 100644 test/addons-napi/7_factory_wrap/myobject.cc create mode 100644 test/addons-napi/7_factory_wrap/myobject.h create mode 100644 test/addons-napi/7_factory_wrap/test.js create mode 100644 test/addons-napi/8_passing_wrapped/binding.cc create mode 100644 test/addons-napi/8_passing_wrapped/binding.gyp create mode 100644 test/addons-napi/8_passing_wrapped/myobject.cc create mode 100644 test/addons-napi/8_passing_wrapped/myobject.h create mode 100644 test/addons-napi/8_passing_wrapped/test.js create mode 100644 test/addons-napi/test_array/binding.gyp create mode 100644 test/addons-napi/test_array/test.js create mode 100644 test/addons-napi/test_array/test_array.c create mode 100644 test/addons-napi/test_buffer/binding.gyp create mode 100644 test/addons-napi/test_buffer/test.js create mode 100644 test/addons-napi/test_buffer/test_buffer.c create mode 100644 test/addons-napi/test_constructor/binding.gyp create mode 100644 test/addons-napi/test_constructor/test.js create mode 100644 test/addons-napi/test_constructor/test_constructor.c create mode 100644 test/addons-napi/test_conversions/binding.gyp create mode 100644 test/addons-napi/test_conversions/test.js create mode 100644 test/addons-napi/test_conversions/test_conversions.c create mode 100644 test/addons-napi/test_error/binding.gyp create mode 100644 test/addons-napi/test_error/test.js create mode 100644 test/addons-napi/test_error/test_error.cc create mode 100644 test/addons-napi/test_exception/binding.gyp create mode 100644 test/addons-napi/test_exception/test.js create mode 100644 test/addons-napi/test_exception/test_exception.c create mode 100644 test/addons-napi/test_function/binding.gyp create mode 100644 test/addons-napi/test_function/test.js create mode 100644 test/addons-napi/test_function/test_function.c create mode 100644 test/addons-napi/test_instanceof/binding.gyp create mode 100644 test/addons-napi/test_instanceof/test.js create mode 100644 test/addons-napi/test_instanceof/test_instanceof.c create mode 100644 test/addons-napi/test_number/binding.gyp create mode 100644 test/addons-napi/test_number/test.js create mode 100644 test/addons-napi/test_number/test_number.c create mode 100644 test/addons-napi/test_object/binding.gyp create mode 100644 test/addons-napi/test_object/test.js create mode 100644 test/addons-napi/test_object/test_object.c create mode 100644 test/addons-napi/test_properties/binding.gyp create mode 100644 test/addons-napi/test_properties/test.js create mode 100644 test/addons-napi/test_properties/test_properties.c create mode 100644 test/addons-napi/test_string/binding.gyp create mode 100644 test/addons-napi/test_string/test.js create mode 100644 test/addons-napi/test_string/test_string.c create mode 100644 test/addons-napi/test_symbol/binding.gyp create mode 100644 test/addons-napi/test_symbol/test1.js create mode 100644 test/addons-napi/test_symbol/test2.js create mode 100644 test/addons-napi/test_symbol/test3.js create mode 100644 test/addons-napi/test_symbol/test_symbol.c create mode 100644 test/addons-napi/test_typedarray/binding.gyp create mode 100644 test/addons-napi/test_typedarray/test.js create mode 100644 test/addons-napi/test_typedarray/test_typedarray.c create mode 100644 test/addons-napi/testcfg.py 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` + + +Enable loading native modules compiled with the ABI-stable Node.js API (N-API) +(experimental). + ### `--trace-warnings`