Skip to content

Commit ed7042c

Browse files
author
Gabriel Schulhof
committed
add hinted finalizer
1 parent 131e252 commit ed7042c

File tree

4 files changed

+73
-22
lines changed

4 files changed

+73
-22
lines changed

napi-inl.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,21 @@ inline Value Env::RunScript(String script) {
326326
template <typename T, Env::Finalizer<T> fini>
327327
inline void Env::SetInstanceData(T* data) {
328328
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);
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+
inline void Env::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);
332344
NAPI_THROW_IF_FAILED_VOID(_env, status);
333345
}
334346

@@ -345,6 +357,11 @@ inline T* Env::GetInstanceData() {
345357
template <typename T> void Env::DefaultFini(Env, T* data) {
346358
delete data;
347359
}
360+
361+
template <typename DataType, typename HintType>
362+
void Env::DefaultFiniWithHint(Env, DataType* data, HintType*) {
363+
delete data;
364+
}
348365
#endif // NAPI_VERSION > 5
349366

350367
////////////////////////////////////////////////////////////////////////////////

napi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ namespace Napi {
200200
template <typename T> using Finalizer = void (*)(Env, T*);
201201
template <typename T, Finalizer<T> fini = Env::DefaultFini<T>>
202202
void SetInstanceData(T* data);
203+
204+
template <typename DataType, typename HintType>
205+
using FinalizerWithHint = void (*)(Env, DataType*, HintType*);
206+
template <typename DataType,
207+
typename HintType,
208+
FinalizerWithHint<DataType, HintType> fini =
209+
Env::DefaultFiniWithHint<DataType, HintType>>
210+
void SetInstanceData(DataType* data, HintType* hint);
203211
#endif // NAPI_VERSION > 5
204212

205213
private:

test/addon_data.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,22 @@ class Addon {
5656
}
5757
}
5858

59-
static Napi::Object Init(Napi::Env env) {
60-
env.SetInstanceData(new Addon(env));
59+
static void DeleteAddon(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(new Addon(env));
72+
else
73+
env.SetInstanceData<Addon, uint32_t, DeleteAddon>(new Addon(env),
74+
new uint32_t(hint));
6175
Napi::Object result = Napi::Object::New(env);
6276
result.DefineProperties({
6377
Napi::PropertyDescriptor::Accessor<Getter, Setter>("verbose"),
@@ -71,7 +85,13 @@ class Addon {
7185
Napi::FunctionReference VerboseIndicator;
7286
};
7387

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+
return Addon::Init(info.Env(), info[0]);
92+
}
93+
7494
Napi::Object InitAddonData(Napi::Env env) {
75-
return Addon::Init(env);
95+
return Napi::Function::New(env, AddonFactory);
7696
}
7797
#endif // (NAPI_VERSION > 5)

test/addon_data.js

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,34 @@ const path = require('path');
88
test(path.resolve(__dirname, `./build/${buildType}/binding.node`));
99
test(path.resolve(__dirname, `./build/${buildType}/binding_noexcept.node`));
1010

11-
function test(bindingName) {
12-
const binding = require(bindingName);
13-
14-
// Make sure it is possible to get/set instance data.
15-
assert.strictEqual(binding.addon_data.verbose.verbose, false);
16-
binding.addon_data.verbose = true;
17-
assert.strictEqual(binding.addon_data.verbose.verbose, true);
18-
binding.addon_data.verbose = false;
19-
assert.strictEqual(binding.addon_data.verbose.verbose, false);
20-
21-
// Make sure the instance data finalizer is called at process exit.
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+
function testFinalizer(bindingName, hint, expected) {
2214
const child = spawn(process.execPath, [
2315
'-e',
24-
`require('${bindingName}').addon_data.verbose = true;`
16+
`require('${bindingName}').addon_data(${hint}).verbose = true;`
2517
]);
26-
let foundMessage = false;
18+
const actual = [];
2719
readline
2820
.createInterface({ input: child.stderr })
2921
.on('line', (line) => {
30-
if (line.match('addon_data: Addon::~Addon')) {
31-
foundMessage = true;
22+
if (expected.indexOf(line) >= 0) {
23+
actual.push(line);
3224
}
3325
})
34-
.on('close', () => assert.strictEqual(foundMessage, true));
26+
.on('close', () => assert.deepStrictEqual(expected, actual));
27+
}
28+
29+
function test(bindingName) {
30+
const binding = require(bindingName).addon_data(0);
31+
32+
// Make sure it is possible to get/set instance data.
33+
assert.strictEqual(binding.verbose.verbose, false);
34+
binding.verbose = true;
35+
assert.strictEqual(binding.verbose.verbose, true);
36+
binding.verbose = false;
37+
assert.strictEqual(binding.verbose.verbose, false);
38+
39+
testFinalizer(bindingName, 0, ['addon_data: Addon::~Addon']);
40+
testFinalizer(bindingName, 42, ['addon_data: Addon::~Addon', 'hint: 42']);
3541
}

0 commit comments

Comments
 (0)