1354 lines
83 KiB
HTML
1354 lines
83 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width">
|
|
<meta name="nodejs.org:node-version" content="v12.22.12">
|
|
<title>C++ addons | Node.js v12.22.12 Documentation</title>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic&display=fallback">
|
|
<link rel="stylesheet" href="assets/style.css">
|
|
<link rel="stylesheet" href="assets/hljs.css">
|
|
<link rel="canonical" href="https://nodejs.org/api/addons.html">
|
|
</head>
|
|
<body class="alt apidoc" id="api-section-addons">
|
|
<div id="content" class="clearfix">
|
|
<div id="column2" class="interior">
|
|
<div id="intro" class="interior">
|
|
<a href="/" title="Go back to the home page">
|
|
Node.js
|
|
</a>
|
|
</div>
|
|
<ul>
|
|
<li><a href="documentation.html" class="nav-documentation">About this documentation</a></li>
|
|
<li><a href="synopsis.html" class="nav-synopsis">Usage and example</a></li>
|
|
</ul>
|
|
<hr class="line">
|
|
<ul>
|
|
<li><a href="assert.html" class="nav-assert">Assertion testing</a></li>
|
|
<li><a href="async_hooks.html" class="nav-async_hooks">Async hooks</a></li>
|
|
<li><a href="buffer.html" class="nav-buffer">Buffer</a></li>
|
|
<li><a href="addons.html" class="nav-addons active">C++ Addons</a></li>
|
|
<li><a href="n-api.html" class="nav-n-api">C/C++ Addons with N-API</a></li>
|
|
<li><a href="embedding.html" class="nav-embedding">C++ Embedder API</a></li>
|
|
<li><a href="child_process.html" class="nav-child_process">Child Processes</a></li>
|
|
<li><a href="cluster.html" class="nav-cluster">Cluster</a></li>
|
|
<li><a href="cli.html" class="nav-cli">Command line options</a></li>
|
|
<li><a href="console.html" class="nav-console">Console</a></li>
|
|
<li><a href="crypto.html" class="nav-crypto">Crypto</a></li>
|
|
<li><a href="debugger.html" class="nav-debugger">Debugger</a></li>
|
|
<li><a href="deprecations.html" class="nav-deprecations">Deprecated APIs</a></li>
|
|
<li><a href="dns.html" class="nav-dns">DNS</a></li>
|
|
<li><a href="domain.html" class="nav-domain">Domain</a></li>
|
|
<li><a href="errors.html" class="nav-errors">Errors</a></li>
|
|
<li><a href="events.html" class="nav-events">Events</a></li>
|
|
<li><a href="fs.html" class="nav-fs">File system</a></li>
|
|
<li><a href="globals.html" class="nav-globals">Globals</a></li>
|
|
<li><a href="http.html" class="nav-http">HTTP</a></li>
|
|
<li><a href="http2.html" class="nav-http2">HTTP/2</a></li>
|
|
<li><a href="https.html" class="nav-https">HTTPS</a></li>
|
|
<li><a href="inspector.html" class="nav-inspector">Inspector</a></li>
|
|
<li><a href="intl.html" class="nav-intl">Internationalization</a></li>
|
|
<li><a href="modules.html" class="nav-modules">Modules: CommonJS modules</a></li>
|
|
<li><a href="esm.html" class="nav-esm">Modules: ECMAScript modules</a></li>
|
|
<li><a href="module.html" class="nav-module">Modules: <code>module</code> API</a></li>
|
|
<li><a href="packages.html" class="nav-packages">Modules: Packages</a></li>
|
|
<li><a href="net.html" class="nav-net">Net</a></li>
|
|
<li><a href="os.html" class="nav-os">OS</a></li>
|
|
<li><a href="path.html" class="nav-path">Path</a></li>
|
|
<li><a href="perf_hooks.html" class="nav-perf_hooks">Performance hooks</a></li>
|
|
<li><a href="policy.html" class="nav-policy">Policies</a></li>
|
|
<li><a href="process.html" class="nav-process">Process</a></li>
|
|
<li><a href="punycode.html" class="nav-punycode">Punycode</a></li>
|
|
<li><a href="querystring.html" class="nav-querystring">Query strings</a></li>
|
|
<li><a href="readline.html" class="nav-readline">Readline</a></li>
|
|
<li><a href="repl.html" class="nav-repl">REPL</a></li>
|
|
<li><a href="report.html" class="nav-report">Report</a></li>
|
|
<li><a href="stream.html" class="nav-stream">Stream</a></li>
|
|
<li><a href="string_decoder.html" class="nav-string_decoder">String decoder</a></li>
|
|
<li><a href="timers.html" class="nav-timers">Timers</a></li>
|
|
<li><a href="tls.html" class="nav-tls">TLS/SSL</a></li>
|
|
<li><a href="tracing.html" class="nav-tracing">Trace events</a></li>
|
|
<li><a href="tty.html" class="nav-tty">TTY</a></li>
|
|
<li><a href="dgram.html" class="nav-dgram">UDP/datagram</a></li>
|
|
<li><a href="url.html" class="nav-url">URL</a></li>
|
|
<li><a href="util.html" class="nav-util">Utilities</a></li>
|
|
<li><a href="v8.html" class="nav-v8">V8</a></li>
|
|
<li><a href="vm.html" class="nav-vm">VM</a></li>
|
|
<li><a href="wasi.html" class="nav-wasi">WASI</a></li>
|
|
<li><a href="worker_threads.html" class="nav-worker_threads">Worker threads</a></li>
|
|
<li><a href="zlib.html" class="nav-zlib">Zlib</a></li>
|
|
</ul>
|
|
<hr class="line">
|
|
<ul>
|
|
<li><a href="https://github.com/nodejs/node" class="nav-https-github-com-nodejs-node">Code repository and issue tracker</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div id="column1" data-id="addons" class="interior">
|
|
<header>
|
|
<h1>Node.js v12.22.12 Documentation</h1>
|
|
<div id="gtoc">
|
|
<ul>
|
|
<li>
|
|
<a href="index.html">Index</a>
|
|
</li>
|
|
<li>
|
|
<a href="all.html">View on single page</a>
|
|
</li>
|
|
<li>
|
|
<a href="addons.json">View as JSON</a>
|
|
</li>
|
|
|
|
<li class="version-picker">
|
|
<a href="#">View another version <span>▼</span></a>
|
|
<ol class="version-picker"><li><a href="https://nodejs.org/docs/latest-v17.x/api/addons.html">17.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v16.x/api/addons.html">16.x <b>LTS</b></a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v15.x/api/addons.html">15.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v14.x/api/addons.html">14.x <b>LTS</b></a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v13.x/api/addons.html">13.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v12.x/api/addons.html">12.x <b>LTS</b></a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v11.x/api/addons.html">11.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v10.x/api/addons.html">10.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v9.x/api/addons.html">9.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v8.x/api/addons.html">8.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v7.x/api/addons.html">7.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v6.x/api/addons.html">6.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v5.x/api/addons.html">5.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v4.x/api/addons.html">4.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v0.12.x/api/addons.html">0.12.x</a></li>
|
|
<li><a href="https://nodejs.org/docs/latest-v0.10.x/api/addons.html">0.10.x</a></li></ol>
|
|
</li>
|
|
|
|
<li class="edit_on_github"><a href="https://github.com/nodejs/node/edit/master/doc/api/addons.md"><span class="github_icon"><svg height="16" width="16" viewBox="0 0 16.1 16.1" fill="currentColor"><path d="M8 0a8 8 0 0 0-2.5 15.6c.4 0 .5-.2.5-.4v-1.5c-2 .4-2.5-.5-2.7-1 0-.1-.5-.9-.8-1-.3-.2-.7-.6 0-.6.6 0 1 .6 1.2.8.7 1.2 1.9 1 2.4.7 0-.5.2-.9.5-1-1.8-.3-3.7-1-3.7-4 0-.9.3-1.6.8-2.2 0-.2-.3-1 .1-2 0 0 .7-.3 2.2.7a7.4 7.4 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.5 1.1.2 2 .1 2.1.5.6.8 1.3.8 2.2 0 3-1.9 3.7-3.6 4 .3.2.5.7.5 1.4v2.2c0 .2.1.5.5.4A8 8 0 0 0 16 8a8 8 0 0 0-8-8z"/></svg></span>Edit on GitHub</a></li>
|
|
</ul>
|
|
</div>
|
|
<hr>
|
|
</header>
|
|
|
|
<div id="toc">
|
|
<h2>Table of Contents</h2>
|
|
<ul>
|
|
<li><a href="#addons_c_addons">C++ addons</a>
|
|
<ul>
|
|
<li><a href="#addons_hello_world">Hello world</a>
|
|
<ul>
|
|
<li><a href="#addons_context_aware_addons">Context-aware addons</a>
|
|
<ul>
|
|
<li><a href="#addons_worker_support">Worker support</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#addons_building">Building</a></li>
|
|
<li><a href="#addons_linking_to_libraries_included_with_node_js">Linking to libraries included with Node.js</a></li>
|
|
<li><a href="#addons_loading_addons_using_require">Loading addons using <code>require()</code></a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#addons_native_abstractions_for_node_js">Native abstractions for Node.js</a></li>
|
|
<li><a href="#addons_n_api">N-API</a></li>
|
|
<li><a href="#addons_addon_examples">Addon examples</a>
|
|
<ul>
|
|
<li><a href="#addons_function_arguments">Function arguments</a></li>
|
|
<li><a href="#addons_callbacks">Callbacks</a></li>
|
|
<li><a href="#addons_object_factory">Object factory</a></li>
|
|
<li><a href="#addons_function_factory">Function factory</a></li>
|
|
<li><a href="#addons_wrapping_c_objects">Wrapping C++ objects</a></li>
|
|
<li><a href="#addons_factory_of_wrapped_objects">Factory of wrapped objects</a></li>
|
|
<li><a href="#addons_passing_wrapped_objects_around">Passing wrapped objects around</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div id="apicontent">
|
|
<h1>C++ addons<span><a class="mark" href="#addons_c_addons" id="addons_c_addons">#</a></span></h1>
|
|
|
|
|
|
<p><em>Addons</em> are dynamically-linked shared objects written in C++. The
|
|
<a href="modules.html#modules_require_id"><code>require()</code></a> function can load addons as ordinary Node.js modules.
|
|
Addons provide an interface between JavaScript and C/C++ libraries.</p>
|
|
<p>There are three options for implementing addons: N-API, nan, or direct
|
|
use of internal V8, libuv and Node.js libraries. Unless there is a need for
|
|
direct access to functionality which is not exposed by N-API, use N-API.
|
|
Refer to <a href="n-api.html">C/C++ addons with N-API</a> for more information on N-API.</p>
|
|
<p>When not using N-API, implementing addons is complicated,
|
|
involving knowledge of several components and APIs:</p>
|
|
<ul>
|
|
<li>
|
|
<p>V8: the C++ library Node.js uses to provide the
|
|
JavaScript implementation. V8 provides the mechanisms for creating objects,
|
|
calling functions, etc. V8's API is documented mostly in the
|
|
<code>v8.h</code> header file (<code>deps/v8/include/v8.h</code> in the Node.js source
|
|
tree), which is also available <a href="https://v8docs.nodesource.com/">online</a>.</p>
|
|
</li>
|
|
<li>
|
|
<p><a href="https://github.com/libuv/libuv">libuv</a>: The C library that implements the Node.js event loop, its worker
|
|
threads and all of the asynchronous behaviors of the platform. It also
|
|
serves as a cross-platform abstraction library, giving easy, POSIX-like
|
|
access across all major operating systems to many common system tasks, such
|
|
as interacting with the filesystem, sockets, timers, and system events. libuv
|
|
also provides a pthreads-like threading abstraction that may be used to
|
|
power more sophisticated asynchronous addons that need to move beyond the
|
|
standard event loop. Addon authors are encouraged to think about how to
|
|
avoid blocking the event loop with I/O or other time-intensive tasks by
|
|
off-loading work via libuv to non-blocking system operations, worker threads
|
|
or a custom use of libuv's threads.</p>
|
|
</li>
|
|
<li>
|
|
<p>Internal Node.js libraries. Node.js itself exports C++ APIs that addons can
|
|
use, the most important of which is the <code>node::ObjectWrap</code> class.</p>
|
|
</li>
|
|
<li>
|
|
<p>Node.js includes other statically linked libraries including OpenSSL. These
|
|
other libraries are located in the <code>deps/</code> directory in the Node.js source
|
|
tree. Only the libuv, OpenSSL, V8 and zlib symbols are purposefully
|
|
re-exported by Node.js and may be used to various extents by addons. See
|
|
<a href="#addons_linking_to_libraries_included_with_node_js">Linking to libraries included with Node.js</a> for additional information.</p>
|
|
</li>
|
|
</ul>
|
|
<p>All of the following examples are available for <a href="https://github.com/nodejs/node-addon-examples">download</a> and may
|
|
be used as the starting-point for an addon.</p>
|
|
<h2>Hello world<span><a class="mark" href="#addons_hello_world" id="addons_hello_world">#</a></span></h2>
|
|
<p>This "Hello world" example is a simple addon, written in C++, that is the
|
|
equivalent of the following JavaScript code:</p>
|
|
<pre><code class="language-js"><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">() =></span> <span class="hljs-string">'world'</span>;</code></pre>
|
|
<p>First, create the file <code>hello.cc</code>:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// hello.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Method</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
args.GetReturnValue().Set(String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"world"</span>, NewStringType::kNormal).ToLocalChecked());
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Initialize</span><span class="hljs-params">(Local<Object> exports)</span> </span>{
|
|
NODE_SET_METHOD(exports, <span class="hljs-string">"hello"</span>, Method);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>All Node.js addons must export an initialization function following
|
|
the pattern:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Initialize</span><span class="hljs-params">(Local<Object> exports)</span></span>;
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)</code></pre>
|
|
<p>There is no semi-colon after <code>NODE_MODULE</code> as it's not a function (see
|
|
<code>node.h</code>).</p>
|
|
<p>The <code>module_name</code> must match the filename of the final binary (excluding
|
|
the <code>.node</code> suffix).</p>
|
|
<p>In the <code>hello.cc</code> example, then, the initialization function is <code>Initialize</code>
|
|
and the addon module name is <code>addon</code>.</p>
|
|
<p>When building addons with <code>node-gyp</code>, using the macro <code>NODE_GYP_MODULE_NAME</code> as
|
|
the first parameter of <code>NODE_MODULE()</code> will ensure that the name of the final
|
|
binary will be passed to <code>NODE_MODULE()</code>.</p>
|
|
<h3>Context-aware addons<span><a class="mark" href="#addons_context_aware_addons" id="addons_context_aware_addons">#</a></span></h3>
|
|
<p>There are environments in which Node.js addons may need to be loaded multiple
|
|
times in multiple contexts. For example, the <a href="https://electronjs.org/">Electron</a> runtime runs multiple
|
|
instances of Node.js in a single process. Each instance will have its own
|
|
<code>require()</code> cache, and thus each instance will need a native addon to behave
|
|
correctly when loaded via <code>require()</code>. From the addon's perspective, this means
|
|
that it must support multiple initializations.</p>
|
|
<p>A context-aware addon can be constructed by using the macro
|
|
<code>NODE_MODULE_INITIALIZER</code>, 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:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> v8;
|
|
|
|
<span class="hljs-keyword">extern</span> <span class="hljs-string">"C"</span> <span class="hljs-function">NODE_MODULE_EXPORT <span class="hljs-keyword">void</span>
|
|
<span class="hljs-title">NODE_MODULE_INITIALIZER</span><span class="hljs-params">(Local<Object> exports,
|
|
Local<Value> <span class="hljs-keyword">module</span>,
|
|
Local<Context> context)</span> </span>{
|
|
<span class="hljs-comment">/* Perform addon initialization steps here. */</span>
|
|
}</code></pre>
|
|
<p>Another option is to use the macro <code>NODE_MODULE_INIT()</code>, which will also
|
|
construct a context-aware addon. Unlike <code>NODE_MODULE()</code>, which is used to
|
|
construct an addon around a given addon initializer function,
|
|
<code>NODE_MODULE_INIT()</code> serves as the declaration of such an initializer to be
|
|
followed by a function body.</p>
|
|
<p>The following three variables may be used inside the function body following an
|
|
invocation of <code>NODE_MODULE_INIT()</code>:</p>
|
|
<ul>
|
|
<li><code>Local<Object> exports</code>,</li>
|
|
<li><code>Local<Value> module</code>, and</li>
|
|
<li><code>Local<Context> context</code></li>
|
|
</ul>
|
|
<p>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.</p>
|
|
<p>The context-aware addon can be structured to avoid global static data by
|
|
performing the following steps:</p>
|
|
<ul>
|
|
<li>Define a class which will hold per-addon-instance data and which has a static
|
|
member of the form
|
|
<pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">DeleteInstance</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* data)</span> </span>{
|
|
<span class="hljs-comment">// Cast `data` to an instance of the class and delete it.</span>
|
|
}</code></pre>
|
|
</li>
|
|
<li>Heap-allocate an instance of this class in the addon initializer. This can be
|
|
accomplished using the <code>new</code> keyword.</li>
|
|
<li>Call <code>node::AddEnvironmentCleanupHook()</code>, passing it the above-created
|
|
instance and a pointer to <code>DeleteInstance()</code>. This will ensure the instance is
|
|
deleted when the environment is torn down.</li>
|
|
<li>Store the instance of the class in a <code>v8::External</code>, and</li>
|
|
<li>Pass the <code>v8::External</code> to all methods exposed to JavaScript by passing it
|
|
to <code>v8::FunctionTemplate::New()</code> or <code>v8::Function::New()</code> which creates the
|
|
native-backed JavaScript functions. The third parameter of
|
|
<code>v8::FunctionTemplate::New()</code> or <code>v8::Function::New()</code> accepts the
|
|
<code>v8::External</code> and makes it available in the native callback using the
|
|
<code>v8::FunctionCallbackInfo::Data()</code> method.</li>
|
|
</ul>
|
|
<p>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.</p>
|
|
<p>The following example illustrates the implementation of a context-aware addon:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> v8;
|
|
|
|
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddonData</span> {</span>
|
|
<span class="hljs-keyword">public</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">AddonData</span><span class="hljs-params">(Isolate* isolate)</span>:
|
|
<span class="hljs-title">call_count</span><span class="hljs-params">(<span class="hljs-number">0</span>)</span> </span>{
|
|
<span class="hljs-comment">// Ensure this per-addon-instance data is deleted at environment cleanup.</span>
|
|
node::AddEnvironmentCleanupHook(isolate, DeleteInstance, <span class="hljs-keyword">this</span>);
|
|
}
|
|
|
|
<span class="hljs-comment">// Per-addon data.</span>
|
|
<span class="hljs-keyword">int</span> call_count;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">DeleteInstance</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* data)</span> </span>{
|
|
<span class="hljs-keyword">delete</span> <span class="hljs-keyword">static_cast</span><AddonData*>(data);
|
|
}
|
|
};
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Method</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& info)</span> </span>{
|
|
<span class="hljs-comment">// Retrieve the per-addon-instance data.</span>
|
|
AddonData* data =
|
|
<span class="hljs-keyword">reinterpret_cast</span><AddonData*>(info.Data().As<External>()->Value());
|
|
data->call_count++;
|
|
info.GetReturnValue().Set((<span class="hljs-keyword">double</span>)data->call_count);
|
|
}
|
|
|
|
<span class="hljs-comment">// Initialize this addon to be context-aware.</span>
|
|
NODE_MODULE_INIT(<span class="hljs-comment">/* exports, module, context */</span>) {
|
|
Isolate* isolate = context->GetIsolate();
|
|
|
|
<span class="hljs-comment">// Create a new instance of `AddonData` for this instance of the addon and</span>
|
|
<span class="hljs-comment">// tie its life cycle to that of the Node.js environment.</span>
|
|
AddonData* data = <span class="hljs-keyword">new</span> AddonData(isolate);
|
|
|
|
<span class="hljs-comment">// Wrap the data in a `v8::External` so we can pass it to the method we</span>
|
|
<span class="hljs-comment">// expose.</span>
|
|
Local<External> external = External::New(isolate, data);
|
|
|
|
<span class="hljs-comment">// Expose the method `Method` to JavaScript, and make sure it receives the</span>
|
|
<span class="hljs-comment">// per-addon-instance data we created above by passing `external` as the</span>
|
|
<span class="hljs-comment">// third parameter to the `FunctionTemplate` constructor.</span>
|
|
exports->Set(context,
|
|
String::NewFromUtf8(isolate, <span class="hljs-string">"method"</span>, NewStringType::kNormal)
|
|
.ToLocalChecked(),
|
|
FunctionTemplate::New(isolate, Method, external)
|
|
->GetFunction(context).ToLocalChecked()).FromJust();
|
|
}</code></pre>
|
|
<h4>Worker support<span><a class="mark" href="#addons_worker_support" id="addons_worker_support">#</a></span></h4>
|
|
<div class="api_metadata">
|
|
<details class="changelog"><summary>History</summary>
|
|
<table>
|
|
<tbody><tr><th>Version</th><th>Changes</th></tr>
|
|
<tr><td>v12.19.0</td>
|
|
<td><p>Cleanup hooks may now be asynchronous.</p></td></tr>
|
|
</tbody></table>
|
|
</details>
|
|
</div>
|
|
<p>In order to be loaded from multiple Node.js environments,
|
|
such as a main thread and a Worker thread, an add-on needs to either:</p>
|
|
<ul>
|
|
<li>Be an N-API addon, or</li>
|
|
<li>Be declared as context-aware using <code>NODE_MODULE_INIT()</code> as described above</li>
|
|
</ul>
|
|
<p>In order to support <a href="worker_threads.html#worker_threads_class_worker"><code>Worker</code></a> threads, addons need to clean up any resources
|
|
they may have allocated when such a thread exists. This can be achieved through
|
|
the usage of the <code>AddEnvironmentCleanupHook()</code> function:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AddEnvironmentCleanupHook</span><span class="hljs-params">(v8::Isolate* isolate,
|
|
<span class="hljs-keyword">void</span> (*fun)(<span class="hljs-keyword">void</span>* arg),
|
|
<span class="hljs-keyword">void</span>* arg)</span></span>;</code></pre>
|
|
<p>This function adds a hook that will run before a given Node.js instance shuts
|
|
down. If necessary, such hooks can be removed before they are run using
|
|
<code>RemoveEnvironmentCleanupHook()</code>, which has the same signature. Callbacks are
|
|
run in last-in first-out order.</p>
|
|
<p>If necessary, there is an additional pair of <code>AddEnvironmentCleanupHook()</code>
|
|
and <code>RemoveEnvironmentCleanupHook()</code> overloads, where the cleanup hook takes a
|
|
callback function. This can be used for shutting down asynchronous resources,
|
|
such as any libuv handles registered by the addon.</p>
|
|
<p>The following <code>addon.cc</code> uses <code>AddEnvironmentCleanupHook</code>:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><assert.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><stdlib.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">using</span> node::AddEnvironmentCleanupHook;
|
|
<span class="hljs-keyword">using</span> v8::HandleScope;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
|
|
<span class="hljs-comment">// Note: In a real-world application, do not rely on static/global data.</span>
|
|
<span class="hljs-keyword">static</span> <span class="hljs-keyword">char</span> cookie[] = <span class="hljs-string">"yum yum"</span>;
|
|
<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> cleanup_cb1_called = <span class="hljs-number">0</span>;
|
|
<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> cleanup_cb2_called = <span class="hljs-number">0</span>;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">cleanup_cb1</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* arg)</span> </span>{
|
|
Isolate* isolate = <span class="hljs-keyword">static_cast</span><Isolate*>(arg);
|
|
<span class="hljs-function">HandleScope <span class="hljs-title">scope</span><span class="hljs-params">(isolate)</span></span>;
|
|
Local<Object> obj = Object::New(isolate);
|
|
assert(!obj.IsEmpty()); <span class="hljs-comment">// assert VM is still alive</span>
|
|
assert(obj->IsObject());
|
|
cleanup_cb1_called++;
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">cleanup_cb2</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* arg)</span> </span>{
|
|
assert(arg == <span class="hljs-keyword">static_cast</span><<span class="hljs-keyword">void</span>*>(cookie));
|
|
cleanup_cb2_called++;
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">sanity_check</span><span class="hljs-params">(<span class="hljs-keyword">void</span>*)</span> </span>{
|
|
assert(cleanup_cb1_called == <span class="hljs-number">1</span>);
|
|
assert(cleanup_cb2_called == <span class="hljs-number">1</span>);
|
|
}
|
|
|
|
<span class="hljs-comment">// Initialize this addon to be context-aware.</span>
|
|
NODE_MODULE_INIT(<span class="hljs-comment">/* exports, module, context */</span>) {
|
|
Isolate* isolate = context->GetIsolate();
|
|
|
|
AddEnvironmentCleanupHook(isolate, sanity_check, <span class="hljs-literal">nullptr</span>);
|
|
AddEnvironmentCleanupHook(isolate, cleanup_cb2, cookie);
|
|
AddEnvironmentCleanupHook(isolate, cleanup_cb1, isolate);
|
|
}</code></pre>
|
|
<p>Test in JavaScript by running:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);</code></pre>
|
|
<h3>Building<span><a class="mark" href="#addons_building" id="addons_building">#</a></span></h3>
|
|
<p>Once the source code has been written, it must be compiled into the binary
|
|
<code>addon.node</code> file. To do so, create a file called <code>binding.gyp</code> in the
|
|
top-level of the project describing the build configuration of the module
|
|
using a JSON-like format. This file is used by <a href="https://github.com/nodejs/node-gyp">node-gyp</a>, a tool written
|
|
specifically to compile Node.js addons.</p>
|
|
<pre><code class="language-json">{
|
|
<span class="hljs-attr">"targets"</span>: [
|
|
{
|
|
<span class="hljs-attr">"target_name"</span>: <span class="hljs-string">"addon"</span>,
|
|
<span class="hljs-attr">"sources"</span>: [ <span class="hljs-string">"hello.cc"</span> ]
|
|
}
|
|
]
|
|
}</code></pre>
|
|
<p>A version of the <code>node-gyp</code> utility is bundled and distributed with
|
|
Node.js as part of <code>npm</code>. This version is not made directly available for
|
|
developers to use and is intended only to support the ability to use the
|
|
<code>npm install</code> command to compile and install addons. Developers who wish to
|
|
use <code>node-gyp</code> directly can install it using the command
|
|
<code>npm install -g node-gyp</code>. See the <code>node-gyp</code> <a href="https://github.com/nodejs/node-gyp#installation">installation instructions</a> for
|
|
more information, including platform-specific requirements.</p>
|
|
<p>Once the <code>binding.gyp</code> file has been created, use <code>node-gyp configure</code> to
|
|
generate the appropriate project build files for the current platform. This
|
|
will generate either a <code>Makefile</code> (on Unix platforms) or a <code>vcxproj</code> file
|
|
(on Windows) in the <code>build/</code> directory.</p>
|
|
<p>Next, invoke the <code>node-gyp build</code> command to generate the compiled <code>addon.node</code>
|
|
file. This will be put into the <code>build/Release/</code> directory.</p>
|
|
<p>When using <code>npm install</code> to install a Node.js addon, npm uses its own bundled
|
|
version of <code>node-gyp</code> to perform this same set of actions, generating a
|
|
compiled version of the addon for the user's platform on demand.</p>
|
|
<p>Once built, the binary addon can be used from within Node.js by pointing
|
|
<a href="modules.html#modules_require_id"><code>require()</code></a> to the built <code>addon.node</code> module:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// hello.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-built_in">console</span>.log(addon.hello());
|
|
<span class="hljs-comment">// Prints: 'world'</span></code></pre>
|
|
<p>Because the exact path to the compiled addon binary can vary depending on how
|
|
it is compiled (i.e. sometimes it may be in <code>./build/Debug/</code>), addons can use
|
|
the <a href="https://github.com/TooTallNate/node-bindings">bindings</a> package to load the compiled module.</p>
|
|
<p>While the <code>bindings</code> package implementation is more sophisticated in how it
|
|
locates addon modules, it is essentially using a <code>try…catch</code> pattern similar to:</p>
|
|
<pre><code class="language-js"><span class="hljs-keyword">try</span> {
|
|
<span class="hljs-keyword">return</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon.node'</span>);
|
|
} <span class="hljs-keyword">catch</span> (err) {
|
|
<span class="hljs-keyword">return</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Debug/addon.node'</span>);
|
|
}</code></pre>
|
|
<h3>Linking to libraries included with Node.js<span><a class="mark" href="#addons_linking_to_libraries_included_with_node_js" id="addons_linking_to_libraries_included_with_node_js">#</a></span></h3>
|
|
<p>Node.js uses statically linked libraries such as V8, libuv and OpenSSL. All
|
|
addons are required to link to V8 and may link to any of the other dependencies
|
|
as well. Typically, this is as simple as including the appropriate
|
|
<code>#include <...></code> statements (e.g. <code>#include <v8.h></code>) and <code>node-gyp</code> will locate
|
|
the appropriate headers automatically. However, there are a few caveats to be
|
|
aware of:</p>
|
|
<ul>
|
|
<li>
|
|
<p>When <code>node-gyp</code> runs, it will detect the specific release version of Node.js
|
|
and download either the full source tarball or just the headers. If the full
|
|
source is downloaded, addons will have complete access to the full set of
|
|
Node.js dependencies. However, if only the Node.js headers are downloaded, then
|
|
only the symbols exported by Node.js will be available.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>node-gyp</code> can be run using the <code>--nodedir</code> flag pointing at a local Node.js
|
|
source image. Using this option, the addon will have access to the full set of
|
|
dependencies.</p>
|
|
</li>
|
|
</ul>
|
|
<h3>Loading addons using <code>require()</code><span><a class="mark" href="#addons_loading_addons_using_require" id="addons_loading_addons_using_require">#</a></span></h3>
|
|
<p>The filename extension of the compiled addon binary is <code>.node</code> (as opposed
|
|
to <code>.dll</code> or <code>.so</code>). The <a href="modules.html#modules_require_id"><code>require()</code></a> function is written to look for
|
|
files with the <code>.node</code> file extension and initialize those as dynamically-linked
|
|
libraries.</p>
|
|
<p>When calling <a href="modules.html#modules_require_id"><code>require()</code></a>, the <code>.node</code> extension can usually be
|
|
omitted and Node.js will still find and initialize the addon. One caveat,
|
|
however, is that Node.js will first attempt to locate and load modules or
|
|
JavaScript files that happen to share the same base name. For instance, if
|
|
there is a file <code>addon.js</code> in the same directory as the binary <code>addon.node</code>,
|
|
then <a href="modules.html#modules_require_id"><code>require('addon')</code></a> will give precedence to the <code>addon.js</code> file
|
|
and load it instead.</p>
|
|
<h2>Native abstractions for Node.js<span><a class="mark" href="#addons_native_abstractions_for_node_js" id="addons_native_abstractions_for_node_js">#</a></span></h2>
|
|
<p>Each of the examples illustrated in this document directly use the
|
|
Node.js and V8 APIs for implementing addons. The V8 API can, and has, changed
|
|
dramatically from one V8 release to the next (and one major Node.js release to
|
|
the next). With each change, addons may need to be updated and recompiled in
|
|
order to continue functioning. The Node.js release schedule is designed to
|
|
minimize the frequency and impact of such changes but there is little that
|
|
Node.js can do to ensure stability of the V8 APIs.</p>
|
|
<p>The <a href="https://github.com/nodejs/nan">Native Abstractions for Node.js</a> (or <code>nan</code>) provide a set of tools that
|
|
addon developers are recommended to use to keep compatibility between past and
|
|
future releases of V8 and Node.js. See the <code>nan</code> <a href="https://github.com/nodejs/nan/tree/master/examples/">examples</a> for an
|
|
illustration of how it can be used.</p>
|
|
<h2>N-API<span><a class="mark" href="#addons_n_api" id="addons_n_api">#</a></span></h2>
|
|
<p></p><div class="api_stability api_stability_2"><a href="documentation.html#documentation_stability_index">Stability: 2</a> - Stable</div><p></p>
|
|
<p>N-API is an API for building native addons. It is independent from
|
|
the underlying JavaScript runtime (e.g. V8) and is maintained as part of
|
|
Node.js itself. This API will be Application Binary Interface (ABI) stable
|
|
across versions of Node.js. It is intended to insulate addons from
|
|
changes in the underlying JavaScript engine and allow modules
|
|
compiled for one version to run on later versions of Node.js without
|
|
recompilation. Addons are built/packaged with the same approach/tools
|
|
outlined in this document (node-gyp, etc.). The only difference is the
|
|
set of APIs that are used by the native code. Instead of using the V8
|
|
or <a href="https://github.com/nodejs/nan">Native Abstractions for Node.js</a> APIs, the functions available
|
|
in the N-API are used.</p>
|
|
<p>Creating and maintaining an addon that benefits from the ABI stability
|
|
provided by N-API carries with it certain
|
|
<a href="n-api.html#n_api_implications_of_abi_stability">implementation considerations</a>.</p>
|
|
<p>To use N-API in the above "Hello world" example, replace the content of
|
|
<code>hello.cc</code> with the following. All other instructions remain the same.</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// hello.cc using N-API</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node_api.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-function">napi_value <span class="hljs-title">Method</span><span class="hljs-params">(napi_env env, napi_callback_info args)</span> </span>{
|
|
napi_value greeting;
|
|
napi_status status;
|
|
|
|
status = napi_create_string_utf8(env, <span class="hljs-string">"world"</span>, NAPI_AUTO_LENGTH, &greeting);
|
|
<span class="hljs-keyword">if</span> (status != napi_ok) <span class="hljs-keyword">return</span> <span class="hljs-literal">nullptr</span>;
|
|
<span class="hljs-keyword">return</span> greeting;
|
|
}
|
|
|
|
<span class="hljs-function">napi_value <span class="hljs-title">init</span><span class="hljs-params">(napi_env env, napi_value exports)</span> </span>{
|
|
napi_status status;
|
|
napi_value fn;
|
|
|
|
status = napi_create_function(env, <span class="hljs-literal">nullptr</span>, <span class="hljs-number">0</span>, Method, <span class="hljs-literal">nullptr</span>, &fn);
|
|
<span class="hljs-keyword">if</span> (status != napi_ok) <span class="hljs-keyword">return</span> <span class="hljs-literal">nullptr</span>;
|
|
|
|
status = napi_set_named_property(env, exports, <span class="hljs-string">"hello"</span>, fn);
|
|
<span class="hljs-keyword">if</span> (status != napi_ok) <span class="hljs-keyword">return</span> <span class="hljs-literal">nullptr</span>;
|
|
<span class="hljs-keyword">return</span> exports;
|
|
}
|
|
|
|
NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>The functions available and how to use them are documented in
|
|
<a href="n-api.html">C/C++ addons with N-API</a>.</p>
|
|
<h2>Addon examples<span><a class="mark" href="#addons_addon_examples" id="addons_addon_examples">#</a></span></h2>
|
|
<p>Following are some example addons intended to help developers get started. The
|
|
examples use the V8 APIs. Refer to the online <a href="https://v8docs.nodesource.com/">V8 reference</a>
|
|
for help with the various V8 calls, and V8's <a href="https://github.com/v8/v8/wiki/Embedder's%20Guide">Embedder's Guide</a> for an
|
|
explanation of several concepts used such as handles, scopes, function
|
|
templates, etc.</p>
|
|
<p>Each of these examples using the following <code>binding.gyp</code> file:</p>
|
|
<pre><code class="language-json">{
|
|
<span class="hljs-attr">"targets"</span>: [
|
|
{
|
|
<span class="hljs-attr">"target_name"</span>: <span class="hljs-string">"addon"</span>,
|
|
<span class="hljs-attr">"sources"</span>: [ <span class="hljs-string">"addon.cc"</span> ]
|
|
}
|
|
]
|
|
}</code></pre>
|
|
<p>In cases where there is more than one <code>.cc</code> file, simply add the additional
|
|
filename to the <code>sources</code> array:</p>
|
|
<pre><code class="language-json">"sources": ["addon.cc", "myexample.cc"]</code></pre>
|
|
<p>Once the <code>binding.gyp</code> file is ready, the example addons can be configured and
|
|
built using <code>node-gyp</code>:</p>
|
|
<pre><code class="language-console"><span class="hljs-meta">$</span><span class="bash"> node-gyp configure build</span></code></pre>
|
|
<h3>Function arguments<span><a class="mark" href="#addons_function_arguments" id="addons_function_arguments">#</a></span></h3>
|
|
<p>Addons will typically expose objects and functions that can be accessed from
|
|
JavaScript running within Node.js. When functions are invoked from JavaScript,
|
|
the input arguments and return value must be mapped to and from the C/C++
|
|
code.</p>
|
|
<p>The following example illustrates how to read function arguments passed from
|
|
JavaScript and how to return a result:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Exception;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Number;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-comment">// This is the implementation of the "add" method</span>
|
|
<span class="hljs-comment">// Input arguments are passed using the</span>
|
|
<span class="hljs-comment">// const FunctionCallbackInfo<Value>& args struct</span>
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Add</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
<span class="hljs-comment">// Check the number of arguments passed.</span>
|
|
<span class="hljs-keyword">if</span> (args.Length() < <span class="hljs-number">2</span>) {
|
|
<span class="hljs-comment">// Throw an Error that is passed back to JavaScript</span>
|
|
isolate->ThrowException(Exception::TypeError(
|
|
String::NewFromUtf8(isolate,
|
|
<span class="hljs-string">"Wrong number of arguments"</span>,
|
|
NewStringType::kNormal).ToLocalChecked()));
|
|
<span class="hljs-keyword">return</span>;
|
|
}
|
|
|
|
<span class="hljs-comment">// Check the argument types</span>
|
|
<span class="hljs-keyword">if</span> (!args[<span class="hljs-number">0</span>]->IsNumber() || !args[<span class="hljs-number">1</span>]->IsNumber()) {
|
|
isolate->ThrowException(Exception::TypeError(
|
|
String::NewFromUtf8(isolate,
|
|
<span class="hljs-string">"Wrong arguments"</span>,
|
|
NewStringType::kNormal).ToLocalChecked()));
|
|
<span class="hljs-keyword">return</span>;
|
|
}
|
|
|
|
<span class="hljs-comment">// Perform the operation</span>
|
|
<span class="hljs-keyword">double</span> value =
|
|
args[<span class="hljs-number">0</span>].As<Number>()->Value() + args[<span class="hljs-number">1</span>].As<Number>()->Value();
|
|
Local<Number> num = Number::New(isolate, value);
|
|
|
|
<span class="hljs-comment">// Set the return value (using the passed in</span>
|
|
<span class="hljs-comment">// FunctionCallbackInfo<Value>&)</span>
|
|
args.GetReturnValue().Set(num);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(Local<Object> exports)</span> </span>{
|
|
NODE_SET_METHOD(exports, <span class="hljs-string">"add"</span>, Add);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>Once compiled, the example addon can be required and used from within Node.js:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'This should be eight:'</span>, addon.add(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>));</code></pre>
|
|
<h3>Callbacks<span><a class="mark" href="#addons_callbacks" id="addons_callbacks">#</a></span></h3>
|
|
<p>It is common practice within addons to pass JavaScript functions to a C++
|
|
function and execute them from there. The following example illustrates how
|
|
to invoke such callbacks:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::Function;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Null;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">RunCallback</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Local<Function> cb = Local<Function>::Cast(args[<span class="hljs-number">0</span>]);
|
|
<span class="hljs-keyword">const</span> <span class="hljs-keyword">unsigned</span> argc = <span class="hljs-number">1</span>;
|
|
Local<Value> argv[argc] = {
|
|
String::NewFromUtf8(isolate,
|
|
<span class="hljs-string">"hello world"</span>,
|
|
NewStringType::kNormal).ToLocalChecked() };
|
|
cb->Call(context, Null(isolate), argc, argv).ToLocalChecked();
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(Local<Object> exports, Local<Object> <span class="hljs-keyword">module</span>)</span> </span>{
|
|
NODE_SET_METHOD(<span class="hljs-keyword">module</span>, <span class="hljs-string">"exports"</span>, RunCallback);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>This example uses a two-argument form of <code>Init()</code> that receives the full
|
|
<code>module</code> object as the second argument. This allows the addon to completely
|
|
overwrite <code>exports</code> with a single function instead of adding the function as a
|
|
property of <code>exports</code>.</p>
|
|
<p>To test it, run the following JavaScript:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
addon(<span class="hljs-function">(<span class="hljs-params">msg</span>) =></span> {
|
|
<span class="hljs-built_in">console</span>.log(msg);
|
|
<span class="hljs-comment">// Prints: 'hello world'</span>
|
|
});</code></pre>
|
|
<p>In this example, the callback function is invoked synchronously.</p>
|
|
<h3>Object factory<span><a class="mark" href="#addons_object_factory" id="addons_object_factory">#</a></span></h3>
|
|
<p>Addons can create and return new objects from within a C++ function as
|
|
illustrated in the following example. An object is created and returned with a
|
|
property <code>msg</code> that echoes the string passed to <code>createObject()</code>:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">CreateObject</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
|
|
Local<Object> obj = Object::New(isolate);
|
|
obj->Set(context,
|
|
String::NewFromUtf8(isolate,
|
|
<span class="hljs-string">"msg"</span>,
|
|
NewStringType::kNormal).ToLocalChecked(),
|
|
args[<span class="hljs-number">0</span>]->ToString(context).ToLocalChecked())
|
|
.FromJust();
|
|
|
|
args.GetReturnValue().Set(obj);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(Local<Object> exports, Local<Object> <span class="hljs-keyword">module</span>)</span> </span>{
|
|
NODE_SET_METHOD(<span class="hljs-keyword">module</span>, <span class="hljs-string">"exports"</span>, CreateObject);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>To test it in JavaScript:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-keyword">const</span> obj1 = addon(<span class="hljs-string">'hello'</span>);
|
|
<span class="hljs-keyword">const</span> obj2 = addon(<span class="hljs-string">'world'</span>);
|
|
<span class="hljs-built_in">console</span>.log(obj1.msg, obj2.msg);
|
|
<span class="hljs-comment">// Prints: 'hello world'</span></code></pre>
|
|
<h3>Function factory<span><a class="mark" href="#addons_function_factory" id="addons_function_factory">#</a></span></h3>
|
|
<p>Another common scenario is creating JavaScript functions that wrap C++
|
|
functions and returning those back to JavaScript:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::Function;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::FunctionTemplate;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyFunction</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
args.GetReturnValue().Set(String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"hello world"</span>, NewStringType::kNormal).ToLocalChecked());
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">CreateFunction</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction);
|
|
Local<Function> fn = tpl->GetFunction(context).ToLocalChecked();
|
|
|
|
<span class="hljs-comment">// omit this to make it anonymous</span>
|
|
fn->SetName(String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"theFunction"</span>, NewStringType::kNormal).ToLocalChecked());
|
|
|
|
args.GetReturnValue().Set(fn);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(Local<Object> exports, Local<Object> <span class="hljs-keyword">module</span>)</span> </span>{
|
|
NODE_SET_METHOD(<span class="hljs-keyword">module</span>, <span class="hljs-string">"exports"</span>, CreateFunction);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>To test:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-keyword">const</span> fn = addon();
|
|
<span class="hljs-built_in">console</span>.log(fn());
|
|
<span class="hljs-comment">// Prints: 'hello world'</span></code></pre>
|
|
<h3>Wrapping C++ objects<span><a class="mark" href="#addons_wrapping_c_objects" id="addons_wrapping_c_objects">#</a></span></h3>
|
|
<p>It is also possible to wrap C++ objects/classes in a way that allows new
|
|
instances to be created using the JavaScript <code>new</code> operator:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"myobject.h"</span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">InitAll</span><span class="hljs-params">(Local<Object> exports)</span> </span>{
|
|
MyObject::Init(exports);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>Then, in <code>myobject.h</code>, the wrapper class inherits from <code>node::ObjectWrap</code>:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// myobject.h</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">ifndef</span> MYOBJECT_H</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MYOBJECT_H</span>
|
|
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node_object_wrap.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyObject</span> :</span> <span class="hljs-keyword">public</span> node::ObjectWrap {
|
|
<span class="hljs-keyword">public</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(v8::Local<v8::Object> exports)</span></span>;
|
|
|
|
<span class="hljs-keyword">private</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">MyObject</span><span class="hljs-params">(<span class="hljs-keyword">double</span> value = <span class="hljs-number">0</span>)</span></span>;
|
|
~MyObject();
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">New</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">PlusOne</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
|
|
<span class="hljs-keyword">double</span> value_;
|
|
};
|
|
|
|
} <span class="hljs-comment">// namespace demo</span>
|
|
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></code></pre>
|
|
<p>In <code>myobject.cc</code>, implement the various methods that are to be exposed.
|
|
Below, the method <code>plusOne()</code> is exposed by adding it to the constructor's
|
|
prototype:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// myobject.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"myobject.h"</span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::Function;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::FunctionTemplate;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Number;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::ObjectTemplate;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
MyObject::MyObject(<span class="hljs-keyword">double</span> value) : value_(value) {
|
|
}
|
|
|
|
MyObject::~MyObject() {
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::Init</span><span class="hljs-params">(Local<Object> exports)</span> </span>{
|
|
Isolate* isolate = exports->GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
|
|
Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate);
|
|
addon_data_tpl->SetInternalFieldCount(<span class="hljs-number">1</span>); <span class="hljs-comment">// 1 field for the MyObject::New()</span>
|
|
Local<Object> addon_data =
|
|
addon_data_tpl->NewInstance(context).ToLocalChecked();
|
|
|
|
<span class="hljs-comment">// Prepare constructor template</span>
|
|
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data);
|
|
tpl->SetClassName(String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"MyObject"</span>, NewStringType::kNormal).ToLocalChecked());
|
|
tpl->InstanceTemplate()->SetInternalFieldCount(<span class="hljs-number">1</span>);
|
|
|
|
<span class="hljs-comment">// Prototype</span>
|
|
NODE_SET_PROTOTYPE_METHOD(tpl, <span class="hljs-string">"plusOne"</span>, PlusOne);
|
|
|
|
Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
|
|
addon_data->SetInternalField(<span class="hljs-number">0</span>, constructor);
|
|
exports->Set(context, String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"MyObject"</span>, NewStringType::kNormal).ToLocalChecked(),
|
|
constructor).FromJust();
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::New</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
|
|
<span class="hljs-keyword">if</span> (args.IsConstructCall()) {
|
|
<span class="hljs-comment">// Invoked as constructor: `new MyObject(...)`</span>
|
|
<span class="hljs-keyword">double</span> value = args[<span class="hljs-number">0</span>]->IsUndefined() ?
|
|
<span class="hljs-number">0</span> : args[<span class="hljs-number">0</span>]->NumberValue(context).FromMaybe(<span class="hljs-number">0</span>);
|
|
MyObject* obj = <span class="hljs-keyword">new</span> MyObject(value);
|
|
obj->Wrap(args.This());
|
|
args.GetReturnValue().Set(args.This());
|
|
} <span class="hljs-keyword">else</span> {
|
|
<span class="hljs-comment">// Invoked as plain function `MyObject(...)`, turn into construct call.</span>
|
|
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> argc = <span class="hljs-number">1</span>;
|
|
Local<Value> argv[argc] = { args[<span class="hljs-number">0</span>] };
|
|
Local<Function> cons =
|
|
args.Data().As<Object>()->GetInternalField(<span class="hljs-number">0</span>).As<Function>();
|
|
Local<Object> result =
|
|
cons->NewInstance(context, argc, argv).ToLocalChecked();
|
|
args.GetReturnValue().Set(result);
|
|
}
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::PlusOne</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder());
|
|
obj->value_ += <span class="hljs-number">1</span>;
|
|
|
|
args.GetReturnValue().Set(Number::New(isolate, obj->value_));
|
|
}
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>To build this example, the <code>myobject.cc</code> file must be added to the
|
|
<code>binding.gyp</code>:</p>
|
|
<pre><code class="language-json">{
|
|
<span class="hljs-attr">"targets"</span>: [
|
|
{
|
|
<span class="hljs-attr">"target_name"</span>: <span class="hljs-string">"addon"</span>,
|
|
<span class="hljs-attr">"sources"</span>: [
|
|
<span class="hljs-string">"addon.cc"</span>,
|
|
<span class="hljs-string">"myobject.cc"</span>
|
|
]
|
|
}
|
|
]
|
|
}</code></pre>
|
|
<p>Test it with:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-keyword">const</span> obj = <span class="hljs-keyword">new</span> addon.MyObject(<span class="hljs-number">10</span>);
|
|
<span class="hljs-built_in">console</span>.log(obj.plusOne());
|
|
<span class="hljs-comment">// Prints: 11</span>
|
|
<span class="hljs-built_in">console</span>.log(obj.plusOne());
|
|
<span class="hljs-comment">// Prints: 12</span>
|
|
<span class="hljs-built_in">console</span>.log(obj.plusOne());
|
|
<span class="hljs-comment">// Prints: 13</span></code></pre>
|
|
<p>The destructor for a wrapper object will run when the object is
|
|
garbage-collected. For destructor testing, there are command-line flags that
|
|
can be used to make it possible to force garbage collection. These flags are
|
|
provided by the underlying V8 JavaScript engine. They are subject to change
|
|
or removal at any time. They are not documented by Node.js or V8, and they
|
|
should never be used outside of testing.</p>
|
|
<h3>Factory of wrapped objects<span><a class="mark" href="#addons_factory_of_wrapped_objects" id="addons_factory_of_wrapped_objects">#</a></span></h3>
|
|
<p>Alternatively, it is possible to use a factory pattern to avoid explicitly
|
|
creating object instances using the JavaScript <code>new</code> operator:</p>
|
|
<pre><code class="language-js"><span class="hljs-keyword">const</span> obj = addon.createObject();
|
|
<span class="hljs-comment">// instead of:</span>
|
|
<span class="hljs-comment">// const obj = new addon.Object();</span></code></pre>
|
|
<p>First, the <code>createObject()</code> method is implemented in <code>addon.cc</code>:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"myobject.h"</span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">CreateObject</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
MyObject::NewInstance(args);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">InitAll</span><span class="hljs-params">(Local<Object> exports, Local<Object> <span class="hljs-keyword">module</span>)</span> </span>{
|
|
MyObject::Init(exports->GetIsolate());
|
|
|
|
NODE_SET_METHOD(<span class="hljs-keyword">module</span>, <span class="hljs-string">"exports"</span>, CreateObject);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>In <code>myobject.h</code>, the static method <code>NewInstance()</code> is added to handle
|
|
instantiating the object. This method takes the place of using <code>new</code> in
|
|
JavaScript:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// myobject.h</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">ifndef</span> MYOBJECT_H</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MYOBJECT_H</span>
|
|
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node_object_wrap.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyObject</span> :</span> <span class="hljs-keyword">public</span> node::ObjectWrap {
|
|
<span class="hljs-keyword">public</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(v8::Isolate* isolate)</span></span>;
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">NewInstance</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
|
|
<span class="hljs-keyword">private</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">MyObject</span><span class="hljs-params">(<span class="hljs-keyword">double</span> value = <span class="hljs-number">0</span>)</span></span>;
|
|
~MyObject();
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">New</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">PlusOne</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
<span class="hljs-keyword">static</span> v8::Global<v8::Function> constructor;
|
|
<span class="hljs-keyword">double</span> value_;
|
|
};
|
|
|
|
} <span class="hljs-comment">// namespace demo</span>
|
|
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></code></pre>
|
|
<p>The implementation in <code>myobject.cc</code> is similar to the previous example:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// myobject.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"myobject.h"</span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> node::AddEnvironmentCleanupHook;
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::Function;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::FunctionTemplate;
|
|
<span class="hljs-keyword">using</span> v8::Global;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Number;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-comment">// Warning! This is not thread-safe, this addon cannot be used for worker</span>
|
|
<span class="hljs-comment">// threads.</span>
|
|
Global<Function> MyObject::constructor;
|
|
|
|
MyObject::MyObject(<span class="hljs-keyword">double</span> value) : value_(value) {
|
|
}
|
|
|
|
MyObject::~MyObject() {
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::Init</span><span class="hljs-params">(Isolate* isolate)</span> </span>{
|
|
<span class="hljs-comment">// Prepare constructor template</span>
|
|
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
|
|
tpl->SetClassName(String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"MyObject"</span>, NewStringType::kNormal).ToLocalChecked());
|
|
tpl->InstanceTemplate()->SetInternalFieldCount(<span class="hljs-number">1</span>);
|
|
|
|
<span class="hljs-comment">// Prototype</span>
|
|
NODE_SET_PROTOTYPE_METHOD(tpl, <span class="hljs-string">"plusOne"</span>, PlusOne);
|
|
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked());
|
|
|
|
AddEnvironmentCleanupHook(isolate, [](<span class="hljs-keyword">void</span>*) {
|
|
constructor.Reset();
|
|
}, <span class="hljs-literal">nullptr</span>);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::New</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
|
|
<span class="hljs-keyword">if</span> (args.IsConstructCall()) {
|
|
<span class="hljs-comment">// Invoked as constructor: `new MyObject(...)`</span>
|
|
<span class="hljs-keyword">double</span> value = args[<span class="hljs-number">0</span>]->IsUndefined() ?
|
|
<span class="hljs-number">0</span> : args[<span class="hljs-number">0</span>]->NumberValue(context).FromMaybe(<span class="hljs-number">0</span>);
|
|
MyObject* obj = <span class="hljs-keyword">new</span> MyObject(value);
|
|
obj->Wrap(args.This());
|
|
args.GetReturnValue().Set(args.This());
|
|
} <span class="hljs-keyword">else</span> {
|
|
<span class="hljs-comment">// Invoked as plain function `MyObject(...)`, turn into construct call.</span>
|
|
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> argc = <span class="hljs-number">1</span>;
|
|
Local<Value> argv[argc] = { args[<span class="hljs-number">0</span>] };
|
|
Local<Function> cons = Local<Function>::New(isolate, constructor);
|
|
Local<Object> instance =
|
|
cons->NewInstance(context, argc, argv).ToLocalChecked();
|
|
args.GetReturnValue().Set(instance);
|
|
}
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::NewInstance</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
<span class="hljs-keyword">const</span> <span class="hljs-keyword">unsigned</span> argc = <span class="hljs-number">1</span>;
|
|
Local<Value> argv[argc] = { args[<span class="hljs-number">0</span>] };
|
|
Local<Function> cons = Local<Function>::New(isolate, constructor);
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Local<Object> instance =
|
|
cons->NewInstance(context, argc, argv).ToLocalChecked();
|
|
|
|
args.GetReturnValue().Set(instance);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::PlusOne</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder());
|
|
obj->value_ += <span class="hljs-number">1</span>;
|
|
|
|
args.GetReturnValue().Set(Number::New(isolate, obj->value_));
|
|
}
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>Once again, to build this example, the <code>myobject.cc</code> file must be added to the
|
|
<code>binding.gyp</code>:</p>
|
|
<pre><code class="language-json">{
|
|
<span class="hljs-attr">"targets"</span>: [
|
|
{
|
|
<span class="hljs-attr">"target_name"</span>: <span class="hljs-string">"addon"</span>,
|
|
<span class="hljs-attr">"sources"</span>: [
|
|
<span class="hljs-string">"addon.cc"</span>,
|
|
<span class="hljs-string">"myobject.cc"</span>
|
|
]
|
|
}
|
|
]
|
|
}</code></pre>
|
|
<p>Test it with:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> createObject = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-keyword">const</span> obj = createObject(<span class="hljs-number">10</span>);
|
|
<span class="hljs-built_in">console</span>.log(obj.plusOne());
|
|
<span class="hljs-comment">// Prints: 11</span>
|
|
<span class="hljs-built_in">console</span>.log(obj.plusOne());
|
|
<span class="hljs-comment">// Prints: 12</span>
|
|
<span class="hljs-built_in">console</span>.log(obj.plusOne());
|
|
<span class="hljs-comment">// Prints: 13</span>
|
|
|
|
<span class="hljs-keyword">const</span> obj2 = createObject(<span class="hljs-number">20</span>);
|
|
<span class="hljs-built_in">console</span>.log(obj2.plusOne());
|
|
<span class="hljs-comment">// Prints: 21</span>
|
|
<span class="hljs-built_in">console</span>.log(obj2.plusOne());
|
|
<span class="hljs-comment">// Prints: 22</span>
|
|
<span class="hljs-built_in">console</span>.log(obj2.plusOne());
|
|
<span class="hljs-comment">// Prints: 23</span></code></pre>
|
|
<h3>Passing wrapped objects around<span><a class="mark" href="#addons_passing_wrapped_objects_around" id="addons_passing_wrapped_objects_around">#</a></span></h3>
|
|
<p>In addition to wrapping and returning C++ objects, it is possible to pass
|
|
wrapped objects around by unwrapping them with the Node.js helper function
|
|
<code>node::ObjectWrap::Unwrap</code>. The following examples shows a function <code>add()</code>
|
|
that can take two <code>MyObject</code> objects as input arguments:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// addon.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node_object_wrap.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"myobject.h"</span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::Number;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">CreateObject</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
MyObject::NewInstance(args);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Add</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
|
|
MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(
|
|
args[<span class="hljs-number">0</span>]->ToObject(context).ToLocalChecked());
|
|
MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(
|
|
args[<span class="hljs-number">1</span>]->ToObject(context).ToLocalChecked());
|
|
|
|
<span class="hljs-keyword">double</span> sum = obj1->value() + obj2->value();
|
|
args.GetReturnValue().Set(Number::New(isolate, sum));
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">InitAll</span><span class="hljs-params">(Local<Object> exports)</span> </span>{
|
|
MyObject::Init(exports->GetIsolate());
|
|
|
|
NODE_SET_METHOD(exports, <span class="hljs-string">"createObject"</span>, CreateObject);
|
|
NODE_SET_METHOD(exports, <span class="hljs-string">"add"</span>, Add);
|
|
}
|
|
|
|
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>In <code>myobject.h</code>, a new public method is added to allow access to private values
|
|
after unwrapping the object.</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// myobject.h</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">ifndef</span> MYOBJECT_H</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> MYOBJECT_H</span>
|
|
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node_object_wrap.h></span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyObject</span> :</span> <span class="hljs-keyword">public</span> node::ObjectWrap {
|
|
<span class="hljs-keyword">public</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Init</span><span class="hljs-params">(v8::Isolate* isolate)</span></span>;
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">NewInstance</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
<span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">double</span> <span class="hljs-title">value</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> value_; }
|
|
|
|
<span class="hljs-keyword">private</span>:
|
|
<span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">MyObject</span><span class="hljs-params">(<span class="hljs-keyword">double</span> value = <span class="hljs-number">0</span>)</span></span>;
|
|
~MyObject();
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">New</span><span class="hljs-params">(<span class="hljs-keyword">const</span> v8::FunctionCallbackInfo<v8::Value>& args)</span></span>;
|
|
<span class="hljs-keyword">static</span> v8::Global<v8::Function> constructor;
|
|
<span class="hljs-keyword">double</span> value_;
|
|
};
|
|
|
|
} <span class="hljs-comment">// namespace demo</span>
|
|
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></code></pre>
|
|
<p>The implementation of <code>myobject.cc</code> is similar to before:</p>
|
|
<pre><code class="language-cpp"><span class="hljs-comment">// myobject.cc</span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><node.h></span></span>
|
|
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"myobject.h"</span></span>
|
|
|
|
<span class="hljs-keyword">namespace</span> demo {
|
|
|
|
<span class="hljs-keyword">using</span> node::AddEnvironmentCleanupHook;
|
|
<span class="hljs-keyword">using</span> v8::Context;
|
|
<span class="hljs-keyword">using</span> v8::Function;
|
|
<span class="hljs-keyword">using</span> v8::FunctionCallbackInfo;
|
|
<span class="hljs-keyword">using</span> v8::FunctionTemplate;
|
|
<span class="hljs-keyword">using</span> v8::Global;
|
|
<span class="hljs-keyword">using</span> v8::Isolate;
|
|
<span class="hljs-keyword">using</span> v8::Local;
|
|
<span class="hljs-keyword">using</span> v8::NewStringType;
|
|
<span class="hljs-keyword">using</span> v8::Object;
|
|
<span class="hljs-keyword">using</span> v8::String;
|
|
<span class="hljs-keyword">using</span> v8::Value;
|
|
|
|
<span class="hljs-comment">// Warning! This is not thread-safe, this addon cannot be used for worker</span>
|
|
<span class="hljs-comment">// threads.</span>
|
|
Global<Function> MyObject::constructor;
|
|
|
|
MyObject::MyObject(<span class="hljs-keyword">double</span> value) : value_(value) {
|
|
}
|
|
|
|
MyObject::~MyObject() {
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::Init</span><span class="hljs-params">(Isolate* isolate)</span> </span>{
|
|
<span class="hljs-comment">// Prepare constructor template</span>
|
|
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
|
|
tpl->SetClassName(String::NewFromUtf8(
|
|
isolate, <span class="hljs-string">"MyObject"</span>, NewStringType::kNormal).ToLocalChecked());
|
|
tpl->InstanceTemplate()->SetInternalFieldCount(<span class="hljs-number">1</span>);
|
|
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked());
|
|
|
|
AddEnvironmentCleanupHook(isolate, [](<span class="hljs-keyword">void</span>*) {
|
|
constructor.Reset();
|
|
}, <span class="hljs-literal">nullptr</span>);
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::New</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
|
|
<span class="hljs-keyword">if</span> (args.IsConstructCall()) {
|
|
<span class="hljs-comment">// Invoked as constructor: `new MyObject(...)`</span>
|
|
<span class="hljs-keyword">double</span> value = args[<span class="hljs-number">0</span>]->IsUndefined() ?
|
|
<span class="hljs-number">0</span> : args[<span class="hljs-number">0</span>]->NumberValue(context).FromMaybe(<span class="hljs-number">0</span>);
|
|
MyObject* obj = <span class="hljs-keyword">new</span> MyObject(value);
|
|
obj->Wrap(args.This());
|
|
args.GetReturnValue().Set(args.This());
|
|
} <span class="hljs-keyword">else</span> {
|
|
<span class="hljs-comment">// Invoked as plain function `MyObject(...)`, turn into construct call.</span>
|
|
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> argc = <span class="hljs-number">1</span>;
|
|
Local<Value> argv[argc] = { args[<span class="hljs-number">0</span>] };
|
|
Local<Function> cons = Local<Function>::New(isolate, constructor);
|
|
Local<Object> instance =
|
|
cons->NewInstance(context, argc, argv).ToLocalChecked();
|
|
args.GetReturnValue().Set(instance);
|
|
}
|
|
}
|
|
|
|
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">MyObject::NewInstance</span><span class="hljs-params">(<span class="hljs-keyword">const</span> FunctionCallbackInfo<Value>& args)</span> </span>{
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
<span class="hljs-keyword">const</span> <span class="hljs-keyword">unsigned</span> argc = <span class="hljs-number">1</span>;
|
|
Local<Value> argv[argc] = { args[<span class="hljs-number">0</span>] };
|
|
Local<Function> cons = Local<Function>::New(isolate, constructor);
|
|
Local<Context> context = isolate->GetCurrentContext();
|
|
Local<Object> instance =
|
|
cons->NewInstance(context, argc, argv).ToLocalChecked();
|
|
|
|
args.GetReturnValue().Set(instance);
|
|
}
|
|
|
|
} <span class="hljs-comment">// namespace demo</span></code></pre>
|
|
<p>Test it with:</p>
|
|
<pre><code class="language-js"><span class="hljs-comment">// test.js</span>
|
|
<span class="hljs-keyword">const</span> addon = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./build/Release/addon'</span>);
|
|
|
|
<span class="hljs-keyword">const</span> obj1 = addon.createObject(<span class="hljs-number">10</span>);
|
|
<span class="hljs-keyword">const</span> obj2 = addon.createObject(<span class="hljs-number">20</span>);
|
|
<span class="hljs-keyword">const</span> result = addon.add(obj1, obj2);
|
|
|
|
<span class="hljs-built_in">console</span>.log(result);
|
|
<span class="hljs-comment">// Prints: 30</span></code></pre>
|
|
<!-- API END -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|