Skip to content

Commit 56e881d

Browse files
jasonginaddaleax
authored andcommitted
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 <[email protected]> Author: Gabriel Schulhof <[email protected]> Author: Hitesh Kanwathirtha <[email protected]> Author: Ian Halliday <[email protected]> Author: Jason Ginchereau <[email protected]> Author: Michael Dawson <[email protected]> Author: Sampson Gao <[email protected]> Author: Taylor Woll <[email protected]> PR-URL: #11975 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 6481c93 commit 56e881d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+6580
-23
lines changed

Makefile

+52-8
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,10 @@ v8:
193193

194194
test: all
195195
$(MAKE) build-addons
196+
$(MAKE) build-addons-napi
196197
$(MAKE) cctest
197198
$(PYTHON) tools/test.py --mode=release -J \
198-
addons doctool inspector known_issues message pseudo-tty parallel sequential
199+
addons addons-napi doctool inspector known_issues message pseudo-tty parallel sequential
199200
$(MAKE) lint
200201

201202
test-parallel: all
@@ -262,6 +263,41 @@ test/addons/.buildstamp: config.gypi \
262263
# TODO(bnoordhuis) Force rebuild after gyp update.
263264
build-addons: $(NODE_EXE) test/addons/.buildstamp
264265

266+
ADDONS_NAPI_BINDING_GYPS := \
267+
$(filter-out test/addons-napi/??_*/binding.gyp, \
268+
$(wildcard test/addons-napi/*/binding.gyp))
269+
270+
ADDONS_NAPI_BINDING_SOURCES := \
271+
$(filter-out test/addons-napi/??_*/*.cc, $(wildcard test/addons-napi/*/*.cc)) \
272+
$(filter-out test/addons-napi/??_*/*.h, $(wildcard test/addons-napi/*/*.h))
273+
274+
# Implicitly depends on $(NODE_EXE), see the build-addons-napi rule for rationale.
275+
test/addons-napi/.buildstamp: config.gypi \
276+
deps/npm/node_modules/node-gyp/package.json \
277+
$(ADDONS_NAPI_BINDING_GYPS) $(ADDONS_NAPI_BINDING_SOURCES) \
278+
deps/uv/include/*.h deps/v8/include/*.h \
279+
src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h \
280+
src/node_api.h src/node_api_types.h
281+
# Cannot use $(wildcard test/addons-napi/*/) here, it's evaluated before
282+
# embedded addons have been generated from the documentation.
283+
@for dirname in test/addons-napi/*/; do \
284+
printf "\nBuilding addon $$PWD/$$dirname\n" ; \
285+
env MAKEFLAGS="-j1" $(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp \
286+
--loglevel=$(LOGLEVEL) rebuild \
287+
--python="$(PYTHON)" \
288+
--directory="$$PWD/$$dirname" \
289+
--nodedir="$$PWD" || exit 1 ; \
290+
done
291+
touch $@
292+
293+
# .buildstamp and .docbuildstamp need $(NODE_EXE) but cannot depend on it
294+
# directly because it calls make recursively. The parent make cannot know
295+
# if the subprocess touched anything so it pessimistically assumes that
296+
# .buildstamp and .docbuildstamp are out of date and need a rebuild.
297+
# Just goes to show that recursive make really is harmful...
298+
# TODO(bnoordhuis) Force rebuild after gyp or node-gyp update.
299+
build-addons-napi: $(NODE_EXE) test/addons-napi/.buildstamp
300+
265301
ifeq ($(OSTYPE),$(filter $(OSTYPE),darwin aix))
266302
XARGS = xargs
267303
else
@@ -274,20 +310,22 @@ clear-stalled:
274310
test-gc: all test/gc/build/Release/binding.node
275311
$(PYTHON) tools/test.py --mode=release gc
276312

277-
test-build: | all build-addons
313+
test-build: | all build-addons build-addons-napi
314+
315+
test-build-addons-napi: all build-addons-napi
278316

279317
test-all: test-build test/gc/build/Release/binding.node
280318
$(PYTHON) tools/test.py --mode=debug,release
281319

282320
test-all-valgrind: test-build
283321
$(PYTHON) tools/test.py --mode=debug,release --valgrind
284322

285-
CI_NATIVE_SUITES := addons
323+
CI_NATIVE_SUITES := addons addons-napi
286324
CI_JS_SUITES := doctool inspector known_issues message parallel pseudo-tty sequential
287325

288326
# Build and test addons without building anything else
289327
test-ci-native: LOGLEVEL := info
290-
test-ci-native: | test/addons/.buildstamp
328+
test-ci-native: | test/addons/.buildstamp test/addons-napi/.buildstamp
291329
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
292330
--mode=release --flaky-tests=$(FLAKY_TESTS) \
293331
$(TEST_CI_ARGS) $(CI_NATIVE_SUITES)
@@ -304,11 +342,11 @@ test-ci-js: | clear-stalled
304342
fi
305343

306344
test-ci: LOGLEVEL := info
307-
test-ci: | clear-stalled build-addons
345+
test-ci: | clear-stalled build-addons build-addons-napi
308346
out/Release/cctest --gtest_output=tap:cctest.tap
309347
$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
310348
--mode=release --flaky-tests=$(FLAKY_TESTS) \
311-
$(TEST_CI_ARGS) $(CI_NATIVE_SUITES) $(CI_JS_SUITES)
349+
$(TEST_CI_ARGS) $(CI_NATIVE_SUITES) addons-napi $(CI_JS_SUITES)
312350
# Clean up any leftover processes
313351
PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
314352
if [ "$${PS_OUT}" ]; then \
@@ -355,7 +393,10 @@ test-npm: $(NODE_EXE)
355393
test-npm-publish: $(NODE_EXE)
356394
npm_package_config_publishtest=true $(NODE) deps/npm/test/run.js
357395

358-
test-addons: test-build
396+
test-addons-napi: test-build-addons-napi
397+
$(PYTHON) tools/test.py --mode=release addons-napi
398+
399+
test-addons: test-build test-addons-napi
359400
$(PYTHON) tools/test.py --mode=release addons
360401

361402
test-addons-clean:
@@ -821,6 +862,7 @@ CPPLINT_EXCLUDE += src/node_root_certs.h
821862
CPPLINT_EXCLUDE += src/queue.h
822863
CPPLINT_EXCLUDE += src/tree.h
823864
CPPLINT_EXCLUDE += $(wildcard test/addons/??_*/*.cc test/addons/??_*/*.h)
865+
CPPLINT_EXCLUDE += $(wildcard test/addons-napi/??_*/*.cc test/addons-napi/??_*/*.h)
824866

825867
CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \
826868
src/*.c \
@@ -830,6 +872,8 @@ CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \
830872
test/addons/*/*.h \
831873
test/cctest/*.cc \
832874
test/cctest/*.h \
875+
test/addons-napi/*/*.cc \
876+
test/addons-napi/*/*.h \
833877
test/gc/binding.cc \
834878
tools/icu/*.cc \
835879
tools/icu/*.h \
@@ -869,4 +913,4 @@ endif
869913
test-v8-intl test-v8-benchmarks test-v8-all v8 lint-ci bench-ci jslint-ci \
870914
doc-only $(TARBALL)-headers test-ci test-ci-native test-ci-js build-ci \
871915
clear-stalled coverage-clean coverage-build coverage-test coverage \
872-
list-gtests
916+
list-gtests test-addons-napi build-addons-napi

doc/api/cli.md

+8
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ added: v6.0.0
144144

145145
Silence all process warnings (including deprecations).
146146

147+
### `--napi-modules`
148+
<!-- YAML
149+
added: REPLACEME
150+
-->
151+
152+
Enable loading native modules compiled with the ABI-stable Node.js API (N-API)
153+
(experimental).
154+
147155
### `--trace-warnings`
148156
<!-- YAML
149157
added: v6.0.0

doc/node.1

+5
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ Throw errors for deprecations.
119119
.BR \-\-no\-warnings
120120
Silence all process warnings (including deprecations).
121121

122+
.TP
123+
.BR \-\-napi\-modules
124+
Enable loading native modules compiled with the ABI-stable Node.js API (N-API)
125+
(experimental).
126+
122127
.TP
123128
.BR \-\-trace\-warnings
124129
Print stack traces for process warnings (including deprecations).

node.gyp

+3
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@
167167
'src/handle_wrap.cc',
168168
'src/js_stream.cc',
169169
'src/node.cc',
170+
'src/node_api.cc',
171+
'src/node_api.h',
172+
'src/node_api_types.h',
170173
'src/node_buffer.cc',
171174
'src/node_config.cc',
172175
'src/node_constants.cc',

src/node.cc

+30-9
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ static const char* trace_enabled_categories = nullptr;
180180
std::string icu_data_dir; // NOLINT(runtime/string)
181181
#endif
182182

183+
// N-API is in experimental state, disabled by default.
184+
bool load_napi_modules = false;
185+
183186
// used by C++ modules as well
184187
bool no_deprecation = false;
185188

@@ -2463,15 +2466,25 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
24632466
}
24642467
if (mp->nm_version != NODE_MODULE_VERSION) {
24652468
char errmsg[1024];
2466-
snprintf(errmsg,
2467-
sizeof(errmsg),
2468-
"The module '%s'"
2469-
"\nwas compiled against a different Node.js version using"
2470-
"\nNODE_MODULE_VERSION %d. This version of Node.js requires"
2471-
"\nNODE_MODULE_VERSION %d. Please try re-compiling or "
2472-
"re-installing\nthe module (for instance, using `npm rebuild` or "
2473-
"`npm install`).",
2474-
*filename, mp->nm_version, NODE_MODULE_VERSION);
2469+
if (mp->nm_version == -1) {
2470+
snprintf(errmsg,
2471+
sizeof(errmsg),
2472+
"The module '%s'"
2473+
"\nwas compiled against the ABI-stable Node.js API (N-API)."
2474+
"\nThis feature is experimental and must be enabled on the "
2475+
"\ncommand-line by adding --napi-modules.",
2476+
*filename);
2477+
} else {
2478+
snprintf(errmsg,
2479+
sizeof(errmsg),
2480+
"The module '%s'"
2481+
"\nwas compiled against a different Node.js version using"
2482+
"\nNODE_MODULE_VERSION %d. This version of Node.js requires"
2483+
"\nNODE_MODULE_VERSION %d. Please try re-compiling or "
2484+
"re-installing\nthe module (for instance, using `npm rebuild` "
2485+
"or `npm install`).",
2486+
*filename, mp->nm_version, NODE_MODULE_VERSION);
2487+
}
24752488

24762489
// NOTE: `mp` is allocated inside of the shared library's memory, calling
24772490
// `uv_dlclose` will deallocate it
@@ -3537,6 +3550,7 @@ static void PrintHelp() {
35373550
" --trace-deprecation show stack traces on deprecations\n"
35383551
" --throw-deprecation throw an exception on deprecations\n"
35393552
" --no-warnings silence all process warnings\n"
3553+
" --napi-modules load N-API modules\n"
35403554
" --trace-warnings show stack traces on process warnings\n"
35413555
" --redirect-warnings=path\n"
35423556
" write warnings to path instead of\n"
@@ -3709,6 +3723,8 @@ static void ParseArgs(int* argc,
37093723
force_repl = true;
37103724
} else if (strcmp(arg, "--no-deprecation") == 0) {
37113725
no_deprecation = true;
3726+
} else if (strcmp(arg, "--napi-modules") == 0) {
3727+
load_napi_modules = true;
37123728
} else if (strcmp(arg, "--no-warnings") == 0) {
37133729
no_process_warnings = true;
37143730
} else if (strcmp(arg, "--trace-warnings") == 0) {
@@ -4489,6 +4505,11 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
44894505
if (debug_enabled)
44904506
EnableDebug(&env);
44914507

4508+
if (load_napi_modules) {
4509+
ProcessEmitWarning(&env, "N-API is an experimental feature "
4510+
"and could change at any time.");
4511+
}
4512+
44924513
{
44934514
SealHandleScope seal(isolate);
44944515
bool more;

0 commit comments

Comments
 (0)