Skip to content

Commit 9c9accf

Browse files
author
Gabriel Schulhof
committed
src: add support for addon instance data
Support `napi_get_instance_data()` and `napi_set_instance_data()`. Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Fixes: #654 PR-URL: #663 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 82a9650 commit 9c9accf

7 files changed

Lines changed: 212 additions & 0 deletions

File tree

‎napi-inl.h‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,48 @@ inline Value Env::RunScript(String script) {
322322
returnValue(_env, result);
323323
}
324324

325+
#if NAPI_VERSION > 5
326+
template <typename T, Env::Finalizer<T> fini>
327+
inlinevoidEnv::SetInstanceData(T* data) {
328+
napi_status status =
329+
napi_set_instance_data(_env, data, [](napi_env env, void* data, void*) {
330+
fini(env, static_cast<T*>(data));
331+
}, nullptr);
332+
NAPI_THROW_IF_FAILED_VOID(_env, status);
333+
}
334+
335+
template <typename DataType,
336+
typename HintType,
337+
Napi::Env::FinalizerWithHint<DataType, HintType> fini>
338+
inlinevoidEnv::SetInstanceData(DataType* data, HintType* hint) {
339+
napi_status status =
340+
napi_set_instance_data(_env, data,
341+
[](napi_env env, void* data, void* hint) {
342+
fini(env, static_cast<DataType*>(data), static_cast<HintType*>(hint));
343+
}, hint);
344+
NAPI_THROW_IF_FAILED_VOID(_env, status);
345+
}
346+
347+
template <typename T>
348+
inline T* Env::GetInstanceData() {
349+
void* data = nullptr;
350+
351+
napi_status status = napi_get_instance_data(_env, &data);
352+
NAPI_THROW_IF_FAILED(_env, status, nullptr);
353+
354+
returnstatic_cast<T*>(data);
355+
}
356+
357+
template <typename T> voidEnv::DefaultFini(Env, T* data) {
358+
delete data;
359+
}
360+
361+
template <typename DataType, typename HintType>
362+
voidEnv::DefaultFiniWithHint(Env, DataType* data, HintType*) {
363+
delete data;
364+
}
365+
#endif// NAPI_VERSION > 5
366+
325367
////////////////////////////////////////////////////////////////////////////////
326368
// Value class
327369
////////////////////////////////////////////////////////////////////////////////

‎napi.h‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ namespace Napi {
166166
///
167167
/// In the V8 JavaScript engine, a N-API environment approximately corresponds to an Isolate.
168168
classEnv {
169+
#if NAPI_VERSION > 5
170+
private:
171+
template <typename T> staticvoidDefaultFini(Env, T* data);
172+
template <typename DataType, typename HintType>
173+
staticvoidDefaultFiniWithHint(Env, DataType* data, HintType* hint);
174+
#endif// NAPI_VERSION > 5
169175
public:
170176
Env(napi_env env);
171177

@@ -182,6 +188,22 @@ namespace Napi {
182188
Value RunScript(const std::string& utf8script);
183189
Value RunScript(String script);
184190

191+
#if NAPI_VERSION > 5
192+
template <typename T> T* GetInstanceData();
193+
194+
template <typename T> using Finalizer = void (*)(Env, T*);
195+
template <typename T, Finalizer<T> fini = Env::DefaultFini<T>>
196+
voidSetInstanceData(T* data);
197+
198+
template <typename DataType, typename HintType>
199+
using FinalizerWithHint = void (*)(Env, DataType*, HintType*);
200+
template <typename DataType,
201+
typename HintType,
202+
FinalizerWithHint<DataType, HintType> fini =
203+
Env::DefaultFiniWithHint<DataType, HintType>>
204+
voidSetInstanceData(DataType* data, HintType* hint);
205+
#endif// NAPI_VERSION > 5
206+
185207
private:
186208
napi_env _env;
187209
};

‎test/addon_data.cc‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#if (NAPI_VERSION > 5)
2+
#include<stdio.h>
3+
#include"napi.h"
4+
5+
// An overly elaborate way to get/set a boolean stored in the instance data:
6+
// 0. A boolean named "verbose" is stored in the instance data. The constructor
7+
// for JS `VerboseIndicator` instances is also stored in the instance data.
8+
// 1. Add a property named "verbose" onto exports served by a getter/setter.
9+
// 2. The getter returns a object of type VerboseIndicator, which itself has a
10+
// property named "verbose", also served by a getter/setter:
11+
// * The getter returns a boolean, indicating whether "verbose" is set.
12+
// * The setter sets "verbose" on the instance data.
13+
// 3. The setter sets "verbose" on the instance data.
14+
15+
classAddon {
16+
public:
17+
classVerboseIndicator : publicNapi::ObjectWrap<VerboseIndicator> {
18+
public:
19+
VerboseIndicator(const Napi::CallbackInfo& info):
20+
Napi::ObjectWrap<VerboseIndicator>(info) {
21+
info.This().As<Napi::Object>()["verbose"] =
22+
Napi::Boolean::New(info.Env(),
23+
info.Env().GetInstanceData<Addon>()->verbose);
24+
}
25+
26+
Napi::Value Getter(const Napi::CallbackInfo& info) {
27+
returnNapi::Boolean::New(info.Env(),
28+
info.Env().GetInstanceData<Addon>()->verbose);
29+
}
30+
31+
voidSetter(const Napi::CallbackInfo& info, const Napi::Value& val) {
32+
info.Env().GetInstanceData<Addon>()->verbose = val.As<Napi::Boolean>();
33+
}
34+
35+
static Napi::FunctionReference Init(Napi::Env env) {
36+
returnNapi::Persistent(DefineClass(env, "VerboseIndicator", {
37+
InstanceAccessor<
38+
&VerboseIndicator::Getter,
39+
&VerboseIndicator::Setter>("verbose")
40+
}));
41+
}
42+
};
43+
44+
static Napi::Value Getter(const Napi::CallbackInfo& info) {
45+
return info.Env().GetInstanceData<Addon>()->VerboseIndicator.New({});
46+
}
47+
48+
staticvoidSetter(const Napi::CallbackInfo& info) {
49+
info.Env().GetInstanceData<Addon>()->verbose = info[0].As<Napi::Boolean>();
50+
}
51+
52+
Addon(Napi::Env env): VerboseIndicator(VerboseIndicator::Init(env)) {}
53+
~Addon() {
54+
if (verbose) {
55+
fprintf(stderr, "addon_data: Addon::~Addon\n");
56+
}
57+
}
58+
59+
staticvoidDeleteAddon(Napi::Env, Addon* addon, uint32_t* hint) {
60+
delete addon;
61+
fprintf(stderr, "hint: %d\n", *hint);
62+
delete hint;
63+
}
64+
65+
static Napi::Object Init(Napi::Env env, Napi::Value jshint) {
66+
if (!jshint.IsNumber()) {
67+
NAPI_THROW(Napi::Error::New(env, "Expected number"), Napi::Object());
68+
}
69+
uint32_t hint = jshint.As<Napi::Number>();
70+
if (hint == 0)
71+
env.SetInstanceData(newAddon(env));
72+
else
73+
env.SetInstanceData<Addon, uint32_t, DeleteAddon>(newAddon(env),
74+
newuint32_t(hint));
75+
Napi::Object result = Napi::Object::New(env);
76+
result.DefineProperties({
77+
Napi::PropertyDescriptor::Accessor<Getter, Setter>("verbose"),
78+
});
79+
80+
return result;
81+
}
82+
83+
private:
84+
bool verbose = false;
85+
Napi::FunctionReference VerboseIndicator;
86+
};
87+
88+
// We use an addon factory so we can cover both the case where there is an
89+
// instance data hint and the case where there isn't.
90+
static Napi::Value AddonFactory(const Napi::CallbackInfo& info) {
91+
returnAddon::Init(info.Env(), info[0]);
92+
}
93+
94+
Napi::Object InitAddonData(Napi::Env env) {
95+
returnNapi::Function::New(env, AddonFactory);
96+
}
97+
#endif// (NAPI_VERSION > 5)

‎test/addon_data.js‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
constbuildType=process.config.target_defaults.default_configuration;
3+
constassert=require('assert');
4+
const{ spawn }=require('child_process');
5+
constreadline=require('readline');
6+
constpath=require('path');
7+
8+
test(path.resolve(__dirname,`./build/${buildType}/binding.node`));
9+
test(path.resolve(__dirname,`./build/${buildType}/binding_noexcept.node`));
10+
11+
// Make sure the instance data finalizer is called at process exit. If the hint
12+
// is non-zero, it will be printed out by the child process.
13+
functiontestFinalizer(bindingName,hint,expected){
14+
bindingName=bindingName.split('\\').join('\\\\');
15+
constchild=spawn(process.execPath,[
16+
'-e',
17+
`require('${bindingName}').addon_data(${hint}).verbose = true;`
18+
]);
19+
constactual=[];
20+
readline
21+
.createInterface({input: child.stderr})
22+
.on('line',(line)=>{
23+
if(expected.indexOf(line)>=0){
24+
actual.push(line);
25+
}
26+
})
27+
.on('close',()=>assert.deepStrictEqual(expected,actual));
28+
}
29+
30+
functiontest(bindingName){
31+
constbinding=require(bindingName).addon_data(0);
32+
33+
// Make sure it is possible to get/set instance data.
34+
assert.strictEqual(binding.verbose.verbose,false);
35+
binding.verbose=true;
36+
assert.strictEqual(binding.verbose.verbose,true);
37+
binding.verbose=false;
38+
assert.strictEqual(binding.verbose.verbose,false);
39+
40+
testFinalizer(bindingName,0,['addon_data: Addon::~Addon']);
41+
testFinalizer(bindingName,42,['addon_data: Addon::~Addon','hint: 42']);
42+
}

‎test/binding.cc‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
usingnamespaceNapi;
44

5+
#if (NAPI_VERSION > 5)
6+
Object InitAddonData(Env env);
7+
#endif
58
Object InitArrayBuffer(Env env);
69
Object InitAsyncContext(Env env);
710
#if (NAPI_VERSION > 3)
@@ -55,6 +58,9 @@ Object InitVersionManagement(Env env);
5558
Object InitThunkingManual(Env env);
5659

5760
Object Init(Env env, Object exports) {
61+
#if (NAPI_VERSION > 5)
62+
exports.Set("addon_data", InitAddonData(env));
63+
#endif
5864
exports.Set("arraybuffer", InitArrayBuffer(env));
5965
exports.Set("asynccontext", InitAsyncContext(env));
6066
#if (NAPI_VERSION > 3)

‎test/binding.gyp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
'target_defaults': {
33
'includes': ['../common.gypi'],
44
'sources': [
5+
'addon_data.cc',
56
'arraybuffer.cc',
67
'asynccontext.cc',
78
'asyncprogressqueueworker.cc',

‎test/index.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ process.config.target_defaults.default_configuration =
88
// FIXME: We might need a way to load test modules automatically without
99
// explicit declaration as follows.
1010
lettestModules=[
11+
'addon_data',
1112
'arraybuffer',
1213
'asynccontext',
1314
'asyncprogressqueueworker',
@@ -81,6 +82,7 @@ if (napiVersion < 5) {
8182
if(napiVersion<6){
8283
testModules.splice(testModules.indexOf('bigint'),1);
8384
testModules.splice(testModules.indexOf('typedarray-bigint'),1);
85+
testModules.splice(testModules.indexOf('addon_data'),1);
8486
}
8587

8688
if(typeofglobal.gc==='function'){

0 commit comments

Comments
 (0)