Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
9cd5c0e
test: add test for missing dynamic instantiate hook
targos Jun 24, 2018
96dae83
zlib: fix memory leak for unused zlib instances
addaleax Jun 30, 2018
3a627c8
src: add context-aware init macro and doc
Jun 13, 2018
221c8bd
messaging: use actual DOMException for DataCloneError
TimothyGu Jun 25, 2018
ed774b7
messaging: fix edge cases with transferring ports
TimothyGu Jun 25, 2018
fe9888a
test: check type for Worker filename argument
Jul 1, 2018
25dac95
test: fix args passed to strictEqual
aitchkhan Jun 27, 2018
6f8ebc0
doc: unify spelling of backpressure
watson Jul 3, 2018
4fa7150
fs: support pseudofiles in promises.readFile
TimothyGu Jun 24, 2018
47b10e3
test: replace third argument with comment in strict equals
developerdavo Jun 30, 2018
f3c397c
console: implement timeLog method
targos Jun 13, 2018
ceec23e
src: remove using directives from spawn_sync.h
danbev Jul 3, 2018
c88af23
http2: pass incoming set-cookie header as array
Flarna Jun 15, 2018
021dd54
test: remove unnecessary string literals
jpospychala Jul 3, 2018
cec166e
n-api: restrict exports by version
kfarnung Dec 7, 2017
a5233c7
deps: cherry-pick 477df06 from upstream v8
devsnek Jul 3, 2018
f7aa22a
doc: improve guide text for CI runs
Trott Jul 3, 2018
9776f1c
benchmark: add n-api function args benchmark
kenny-y Jun 27, 2018
c8d5bab
doc: fix doc for napi_create_function
Jul 2, 2018
04eed23
doc: remove "note that" from fs doc
Trott Jul 3, 2018
214c608
tools: lint doc code examples in strict mode
vsemozhetbyt Jul 1, 2018
034fe19
doc: add links to inline HTML table
Trott Jun 20, 2018
24f649c
test: fix pummel/test-net-connect-memleak
Trott Jul 4, 2018
9716793
doc: add codebytere as collaborator
codebytere Jul 7, 2018
1044baf
doc: remove _Node.js style callback_
Trott Jul 7, 2018
dc84858
test,util: add missing tests and conditions
Jun 22, 2018
109c599
n-api: create functions directly
Jul 6, 2018
b8ba003
n-api: remove experimental gate from status codes
Jul 6, 2018
cd77d87
doc: improve documentation of fs sync methods
iwko Jun 10, 2018
194d195
test: fix test-tls-connect-memleak
Trott Jul 6, 2018
eda7fff
test: swap arguments in strictEqual()
sohailrajdev97 Jul 4, 2018
11e9b4e
test: fix parallel/test-tls-env-extra-ca.js
Niicck Jul 4, 2018
92d7921
lib: consolidate redundant require() calls
cjihrig Jul 7, 2018
6e1917a
doc: update changelog with 9.x EOL
ChALkeR Jun 30, 2018
51db88b
doc: fix http2stream.pushStream error doc
ChALkeR Jun 23, 2018
b758006
fs: fix fsPromises.lchmod error on non-Mac
Jun 21, 2018
07160cd
http2: order declarations in core.js
Trott Jul 6, 2018
dae7130
zlib: track memory allocated by zlib
addaleax Jun 30, 2018
122ae24
deps: icu 62.1 bump (Unicode 11, CLDR 33.1)
srl295 Jul 9, 2018
25fef3d
workers: fix invalid exit code in parent upon uncaught exception
lundibundi Jul 8, 2018
cd6601b
doc: fix HTTP res 'finish' description
tadjik1 Jul 5, 2018
577d24b
doc: fix module.children description
transitive-bullshit Jul 5, 2018
f386c0a
test: add test for dns.promises.resolve .
kakts Jul 6, 2018
c4d7413
trace_events: add process_name metadata
jasnell Jun 22, 2018
d063056
src: add --title command line argument
jasnell Jun 22, 2018
6920091
deps: upgrade to libuv 1.22.0
cjihrig Jul 10, 2018
2770778
src: make Environment::is_stopping_worker inline
maclover7 Jul 9, 2018
4f3bbfa
n-api: test uint32 truncation
Jul 9, 2018
b9bbbbe
tools: build all.json by combining generated JSON
rubys Jul 3, 2018
48b16aa
zlib: instance-ify two methods
maclover7 Jul 7, 2018
07cce88
crypto: handle OpenSSL error queue in CipherBase
tniessen Jun 12, 2018
27d17d4
trace_events: add traced_value.cc/traced_value.h
jasnell Jun 22, 2018
3096ee5
napi: add bigint support
devsnek Jun 9, 2018
e030dd7
tools: add no-duplicate-requires rule
devsnek Jul 8, 2018
c26ba08
tools: avoid global install of dmn for lint update
Trott Jul 11, 2018
3fffc7e
errors: fix undefined HTTP2 and tls errors
thatshailesh Jun 27, 2018
3d93273
doc: add OS X to instead of only macOS
XadillaX May 30, 2018
bba500d
http: fix request with option timeout and agent
killagu Jun 8, 2018
4b613d3
repl: make own properties shadow prototype properties
rubys Jun 29, 2018
c02fb88
build: enabling lto at configure
Jul 5, 2018
f46536b
test: fix timeouts when running worker tests with `--worker`
addaleax Jul 13, 2018
712809e
src: enable more detailed memory tracking
addaleax Jun 10, 2018
ada3f34
test: fix weird string error
maclover7 Jul 13, 2018
7ab6efd
doc: add policy for landing new npm releases
MylesBorins Jun 29, 2018
42d7539
deps: patch V8 to 6.7.288.49
MylesBorins Jul 9, 2018
b229129
inspector: split main thread interface from transport
May 21, 2018
ae5d565
test: fix flaky watchFile()
Trott Jul 12, 2018
1502651
test: remove timer in fs.watchFile() test
Trott Jul 13, 2018
0de0f89
doc: add "Edit on GitHub" link
Trott Jul 7, 2018
cb69811
src: add comment on CallbackScope exception behaviour
addaleax Jul 10, 2018
aa5994f
src,tools: use https://nodejs.org URL when possible.
XhmikosR Jul 9, 2018
961f6e8
process: fix process.exitCode handling for fatalException
lundibundi Jul 10, 2018
600349a
test: refactor process/worker exitCode tests
lundibundi Jul 10, 2018
b0943a6
worker: exit after uncaught exception
lundibundi Jul 13, 2018
a68b7dd
src: add node_process.cc
jasnell Jun 3, 2018
f1b18ba
process: implement process.hrtime.bigint()
joyeecheung Jun 11, 2018
4433ecb
lib: refactor cli table
BridgeAR May 25, 2018
a09bdb5
test: improve console table error output
BridgeAR May 25, 2018
466601f
src: remove .h if -inl.h is already included
danbev Jun 18, 2018
51d613d
src: start annotating native code side effect
TimothyGu Jun 22, 2018
4ed5d1a
src: add HandleWrap::AddWrapMethods
maclover7 Jul 11, 2018
d33281b
doc: prevent some redirections
vsemozhetbyt Jul 14, 2018
ca8c960
doc: update readme with my pronouns
lance Jul 15, 2018
678313d
test: add filehandle sync() and datasync() tests
May 5, 2018
0298299
doc: add my pronoun
BridgeAR Jul 14, 2018
d42dbde
src: add iteration over all base objects to Environment
addaleax Jun 10, 2018
355c5e3
deps: cherry-pick 555c811 from upstream V8
addaleax Jun 30, 2018
5121278
src: use V8 graph heap snapshot API
addaleax Jul 10, 2018
a9a7186
src: make heap snapshot & embedder graph accessible for tests
addaleax Jun 30, 2018
7352b72
test: add heap snapshot tests
addaleax Jul 10, 2018
10f9374
doc: make markdown input compliant
rubys Jul 12, 2018
51dfebf
doc: fix vm.runInNewContext signature
targos Jul 15, 2018
1019c2d
src: fix async hooks crashing when there is no node context
xaviergonz Mar 4, 2018
238ef58
http2: remove `waitTrailers` listener after closing a stream
RidgeA Jul 11, 2018
32ad163
test: add test of fs.promises write for non-string buffers
Shagamii Jul 8, 2018
a2edb59
test: fix comment of fs.promises write
Shagamii Jul 8, 2018
4e60ce8
test: fix flaky test-debug-prompt
Trott Jul 15, 2018
73cafd8
console,util: avoid pair array generation in C++
addaleax May 18, 2018
2a0862c
console: fix timeEnd() not coercing the input
BridgeAR Jul 12, 2018
8c97ffb
assert: improve simple assert
BridgeAR Jul 2, 2018
53b587a
doc: add documentation for buffer.byteOffset
AndreasMadsen Jul 9, 2018
6bb2b5a
build: account for pure C sources in `build-addons-napi`
addaleax Jul 13, 2018
0b3c80c
http2: fix issues with aborted `respondWithFile()`s
addaleax Jun 27, 2018
b338ff5
test: add gc tracking to common API
addaleax Jul 13, 2018
174a9db
test: refactor test-net-connect-memleak, move to parallel
addaleax Jul 13, 2018
6b72583
test: refactor test-tls-connect-memleak, move to parallel
addaleax Jul 13, 2018
67908e9
test: fix build warnings in bigint N-API test
addaleax Jul 13, 2018
4ed4bf3
lib: update punycode to 2.1.1
Trott Jul 11, 2018
eef975e
test: move inspector test back to parallel, unmark flaky
addaleax Jul 13, 2018
581390c
process: split bootstrappers by threads that can run them
joyeecheung Jun 17, 2018
576f1ea
buffer: remove superfluous assignment
tniessen Jul 17, 2018
d9825c7
crypto: prevent Sign::SignFinal from crashing
tniessen Jul 14, 2018
0108ff6
test: add support for NODE_TEST_DIR on a separate mount point
aduh95 Jun 26, 2018
2922028
inspector: expose original console
mcollina Jul 4, 2018
506631a
doc: fix structure and formatting in inspector.md
vsemozhetbyt Jul 8, 2018
991bb95
2018-07-18, Version 10.7.0 (Current)
targos Jul 17, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
src: add context-aware init macro and doc
Introduces macros `NODE_MODULE_INITIALIZER` which expands to the name
of the special symbol that process.dlopen() will look for to initialize
an addon, and `NODE_MODULE_INIT()` which creates the boilerplate for
a context-aware module which can be loaded multiple times via the
special symbol mechanism.

Additionally, provides an example of using the new macro to construct
an addon which stores per-addon-instance data in a heap-allocated
structure that gets passed to each binding, rather than in a collection
of global static variables.

Re: #21291 (comment)
PR-URL: #21318
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
  • Loading branch information
Gabriel Schulhof authored and targos committed Jul 4, 2018
commit 3a627c830b15fd1c5ce9d8926991abf6772b8fb8
135 changes: 135 additions & 0 deletions doc/api/addons.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,140 @@ the `.node` suffix).
In the `hello.cc` example, then, the initialization function is `Initialize`
and the addon module name is `addon`.

When building addons with `node-gyp`, using the macro `NODE_GYP_MODULE_NAME` as
the first parameter of `NODE_MODULE()` will ensure that the name of the final
binary will be passed to `NODE_MODULE()`.

### Context-aware addons

There are environments in which Node.js addons may need to be loaded multiple
times in multiple contexts. For example, the [Electron][] runtime runs multiple
instances of Node.js in a single process. Each instance will have its own
`require()` cache, and thus each instance will need a native addon to behave
correctly when loaded via `require()`. From the addon's perspective, this means
that it must support multiple initializations.

A context-aware addon can be constructed by using the macro
`NODE_MODULE_INITIALIZER`, which expands to the name of a function which Node.js
will expect to find when it loads an addon. An addon can thus be initialized as
in the following example:

```cpp
using namespace v8;

extern "C" NODE_MODULE_EXPORT void
NODE_MODULE_INITIALIZER(Local<Object> exports,
Local<Value> module,
Local<Context> context) {
/* Perform addon initialization steps here. */
}
```

Another option is to use the macro `NODE_MODULE_INIT()`, which will also
construct a context-aware addon. Unlike `NODE_MODULE()`, which is used to
construct an addon around a given addon initializer function,
`NODE_MODULE_INIT()` serves as the declaration of such an initializer to be
followed by a function body.

The following three variables may be used inside the function body following an
invocation of `NODE_MODULE_INIT()`:
* `Local<Object> exports`,
* `Local<Value> module`, and
* `Local<Context> context`

The choice to build a context-aware addon carries with it the responsibility of
carefully managing global static data. Since the addon may be loaded multiple
times, potentially even from different threads, any global static data stored
in the addon must be properly protected, and must not contain any persistent
references to JavaScript objects. The reason for this is that JavaScript
objects are only valid in one context, and will likely cause a crash when
accessed from the wrong context or from a different thread than the one on which
they were created.

The context-aware addon can be structured to avoid global static data by
performing the following steps:
* defining a class which will hold per-addon-instance data. Such
a class should include a `v8::Persistent<v8::Object>` which will hold a weak
reference to the addon's `exports` object. The callback associated with the weak
reference will then destroy the instance of the class.
* constructing an instance of this class in the addon initializer such that the
`v8::Persistent<v8::Object>` is set to the `exports` object.
* storing the instance of the class in a `v8::External`, and
* passing the `v8::External` to all methods exposed to JavaScript by passing it
to the `v8::FunctionTemplate` constructor which creates the native-backed
JavaScript functions. The `v8::FunctionTemplate` constructor's third parameter
accepts the `v8::External`.

This will ensure that the per-addon-instance data reaches each binding that can
be called from JavaScript. The per-addon-instance data must also be passed into
any asynchronous callbacks the addon may create.

The following example illustrates the implementation of a context-aware addon:

```cpp
#include <node.h>

using namespace v8;

class AddonData {
public:
AddonData(Isolate* isolate, Local<Object> exports):
call_count(0) {
// Link the existence of this object instance to the existence of exports.
exports_.Reset(isolate, exports);
exports_.SetWeak(this, DeleteMe, WeakCallbackType::kParameter);
}

~AddonData() {
if (!exports_.IsEmpty()) {
// Reset the reference to avoid leaking data.
exports_.ClearWeak();
exports_.Reset();
}
}

// Per-addon data.
int call_count;

private:
// Method to call when "exports" is about to be garbage-collected.
static void DeleteMe(const WeakCallbackInfo<AddonData>& info) {
delete info.GetParameter();
}

// Weak handle to the "exports" object. An instance of this class will be
// destroyed along with the exports object to which it is weakly bound.
v8::Persistent<v8::Object> exports_;
};

static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
// Retrieve the per-addon-instance data.
AddonData* data =
reinterpret_cast<AddonData*>(info.Data().As<External>()->Value());
data->call_count++;
info.GetReturnValue().Set((double)data->call_count);
}

// Initialize this addon to be context-aware.
NODE_MODULE_INIT(/* exports, module, context */) {
Isolate* isolate = context->GetIsolate();

// Create a new instance of AddonData for this instance of the addon.
AddonData* data = new AddonData(isolate, exports);
// Wrap the data in a v8::External so we can pass it to the method we expose.
Local<External> external = External::New(isolate, data);

// Expose the method "Method" to JavaScript, and make sure it receives the
// per-addon-instance data we created above by passing `external` as the
// third parameter to the FunctionTemplate constructor.
exports->Set(context,
String::NewFromUtf8(isolate, "method", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, Method, external)
->GetFunction(context).ToLocalChecked()).FromJust();
}
```

### Building

Once the source code has been written, it must be compiled into the binary
Expand Down Expand Up @@ -1162,6 +1296,7 @@ Test in JavaScript by running:
require('./build/Release/addon');
```

[Electron]: https://electronjs.org/
[Embedder's Guide]: https://github.com/v8/v8/wiki/Embedder's%20Guide
[Linking to Node.js' own dependencies]: #addons_linking_to_node_js_own_dependencies
[Native Abstractions for Node.js]: https://github.com/nodejs/nan
Expand Down
22 changes: 22 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,28 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
*/
#define NODE_MODULE_DECL /* nothing */

#define NODE_MODULE_INITIALIZER_BASE node_register_module_v

#define NODE_MODULE_INITIALIZER_X(base, version) \
NODE_MODULE_INITIALIZER_X_HELPER(base, version)

#define NODE_MODULE_INITIALIZER_X_HELPER(base, version) base##version

#define NODE_MODULE_INITIALIZER \
NODE_MODULE_INITIALIZER_X(NODE_MODULE_INITIALIZER_BASE, \
NODE_MODULE_VERSION)

#define NODE_MODULE_INIT() \
extern "C" NODE_MODULE_EXPORT void \
NODE_MODULE_INITIALIZER(v8::Local<v8::Object> exports, \
v8::Local<v8::Value> module, \
v8::Local<v8::Context> context); \
NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, \
NODE_MODULE_INITIALIZER) \
void NODE_MODULE_INITIALIZER(v8::Local<v8::Object> exports, \
v8::Local<v8::Value> module, \
v8::Local<v8::Context> context)

/* Called after the event loop exits but before the VM is disposed.
* Callbacks are run in reverse order of registration, i.e. newest first.
*/
Expand Down
13 changes: 6 additions & 7 deletions test/addons/hello-world/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ void Method(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world"));
}

#define CONCAT(a, b) CONCAT_HELPER(a, b)
#define CONCAT_HELPER(a, b) a##b
#define INITIALIZER CONCAT(node_register_module_v, NODE_MODULE_VERSION)

extern "C" NODE_MODULE_EXPORT void INITIALIZER(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context) {
// Not using the full NODE_MODULE_INIT() macro here because we want to test the
// addon loader's reaction to the FakeInit() entry point below.
extern "C" NODE_MODULE_EXPORT void
NODE_MODULE_INITIALIZER(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context) {
NODE_SET_METHOD(exports, "hello", Method);
}

Expand Down