Skip to content

Commit c64d8cf

Browse files
committed
src: introduce NogcEnv; support for nogc finalizers
1 parent bf49519 commit c64d8cf

File tree

9 files changed

+323
-144
lines changed

9 files changed

+323
-144
lines changed

napi-inl.h

Lines changed: 168 additions & 94 deletions
Large diffs are not rendered by default.

napi.h

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ namespace NAPI_CPP_CUSTOM_NAMESPACE {
187187
#endif
188188

189189
// Forward declarations
190+
class NogcEnv;
190191
class Env;
191192
class Value;
192193
class Boolean;
@@ -299,6 +300,16 @@ template <typename T>
299300
using MaybeOrValue = T;
300301
#endif
301302

303+
#if defined(NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER)
304+
#define NODE_ADDON_API_NOGC_ENV node_api_nogc_env
305+
#define NODE_ADDON_API_NOGC_ENV_CLASS Napi::NogcEnv
306+
#define NODE_ADDON_API_NOGC_FINALIZER node_api_nogc_finalize
307+
#else
308+
#define NODE_ADDON_API_NOGC_ENV napi_env
309+
#define NODE_ADDON_API_NOGC_ENV_CLASS Napi::Env
310+
#define NODE_ADDON_API_NOGC_FINALIZER napi_finalize
311+
#endif
312+
302313
/// Environment for Node-API values and operations.
303314
///
304315
/// All Node-API values and operations must be associated with an environment.
@@ -312,30 +323,31 @@ using MaybeOrValue = T;
312323
///
313324
/// In the V8 JavaScript engine, a Node-API environment approximately
314325
/// corresponds to an Isolate.
315-
class Env {
316-
private:
317-
napi_env _env;
326+
class NogcEnv {
327+
protected:
328+
NODE_ADDON_API_NOGC_ENV _env;
318329
#if NAPI_VERSION > 5
319330
template <typename T>
320-
static void DefaultFini(Env, T* data);
331+
static void DefaultGcFini(Env, T* data);
321332
template <typename DataType, typename HintType>
322-
static void DefaultFiniWithHint(Env, DataType* data, HintType* hint);
333+
static void DefaultGcFiniWithHint(Env, DataType* data, HintType* hint);
323334
#endif // NAPI_VERSION > 5
324335
public:
325-
Env(napi_env env);
326-
327-
operator napi_env() const;
328-
329-
Object Global() const;
330-
Value Undefined() const;
331-
Value Null() const;
332-
333-
bool IsExceptionPending() const;
334-
Error GetAndClearPendingException() const;
335-
336-
MaybeOrValue<Value> RunScript(const char* utf8script) const;
337-
MaybeOrValue<Value> RunScript(const std::string& utf8script) const;
338-
MaybeOrValue<Value> RunScript(String script) const;
336+
NogcEnv(NODE_ADDON_API_NOGC_ENV env);
337+
operator NODE_ADDON_API_NOGC_ENV() const;
338+
339+
// Without these operator overloads, the error:
340+
//
341+
// Use of overloaded operator '==' is ambiguous (with operand types
342+
// 'Napi::Env' and 'Napi::Env')
343+
//
344+
// ... occurs when comparing foo.Env() == bar.Env() or foo.Env() == nullptr
345+
bool operator==(const NogcEnv& other) const {
346+
return _env == other._env;
347+
};
348+
bool operator==(const std::nullptr_t& /*other*/) const {
349+
return _env == nullptr;
350+
};
339351

340352
#if NAPI_VERSION > 2
341353
template <typename Hook, typename Arg = void>
@@ -353,16 +365,16 @@ class Env {
353365
T* GetInstanceData() const;
354366

355367
template <typename T>
356-
using Finalizer = void (*)(Env, T*);
357-
template <typename T, Finalizer<T> fini = Env::DefaultFini<T>>
368+
using GcFinalizer = void (*)(Env, T*);
369+
template <typename T, GcFinalizer<T> gc_fini = NogcEnv::DefaultGcFini<T>>
358370
void SetInstanceData(T* data) const;
359371

360372
template <typename DataType, typename HintType>
361373
using FinalizerWithHint = void (*)(Env, DataType*, HintType*);
362374
template <typename DataType,
363375
typename HintType,
364376
FinalizerWithHint<DataType, HintType> fini =
365-
Env::DefaultFiniWithHint<DataType, HintType>>
377+
NogcEnv::DefaultGcFiniWithHint<DataType, HintType>>
366378
void SetInstanceData(DataType* data, HintType* hint) const;
367379
#endif // NAPI_VERSION > 5
368380

@@ -371,9 +383,9 @@ class Env {
371383
class CleanupHook {
372384
public:
373385
CleanupHook();
374-
CleanupHook(Env env, Hook hook, Arg* arg);
375-
CleanupHook(Env env, Hook hook);
376-
bool Remove(Env env);
386+
CleanupHook(NogcEnv env, Hook hook, Arg* arg);
387+
CleanupHook(NogcEnv env, Hook hook);
388+
bool Remove(NogcEnv env);
377389
bool IsEmpty() const;
378390

379391
private:
@@ -391,6 +403,37 @@ class Env {
391403
#if NAPI_VERSION > 8
392404
const char* GetModuleFileName() const;
393405
#endif // NAPI_VERSION > 8
406+
407+
#ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
408+
template <typename Finalizer>
409+
inline void AddPostFinalizer(Finalizer finalizeCallback) const;
410+
411+
template <typename Finalizer, typename T>
412+
inline void AddPostFinalizer(Finalizer finalizeCallback, T* data) const;
413+
414+
template <typename Finalizer, typename T, typename Hint>
415+
inline void AddPostFinalizer(Finalizer finalizeCallback,
416+
T* data,
417+
Hint* finalizeHint) const;
418+
#endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
419+
};
420+
421+
class Env : public NogcEnv {
422+
public:
423+
Env(napi_env env);
424+
425+
operator napi_env() const;
426+
427+
Object Global() const;
428+
Value Undefined() const;
429+
Value Null() const;
430+
431+
bool IsExceptionPending() const;
432+
Error GetAndClearPendingException() const;
433+
434+
MaybeOrValue<Value> RunScript(const char* utf8script) const;
435+
MaybeOrValue<Value> RunScript(const std::string& utf8script) const;
436+
MaybeOrValue<Value> RunScript(String script) const;
394437
};
395438

396439
/// A JavaScript value of unknown type.
@@ -2414,7 +2457,7 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
24142457
Napi::Value value,
24152458
napi_property_attributes attributes = napi_default);
24162459
static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo);
2417-
virtual void Finalize(Napi::Env env);
2460+
virtual void Finalize(NODE_ADDON_API_NOGC_ENV_CLASS env);
24182461

24192462
private:
24202463
using This = ObjectWrap<T>;
@@ -2429,7 +2472,9 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
24292472
napi_callback_info info);
24302473
static napi_value StaticSetterCallbackWrapper(napi_env env,
24312474
napi_callback_info info);
2432-
static void FinalizeCallback(napi_env env, void* data, void* hint);
2475+
static void FinalizeCallback(NODE_ADDON_API_NOGC_ENV env,
2476+
void* data,
2477+
void* hint);
24332478
static Function DefineClass(Napi::Env env,
24342479
const char* utf8name,
24352480
const size_t props_count,

test/array_buffer.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Value CreateExternalBufferWithFinalize(const CallbackInfo& info) {
6363
uint8_t* data = new uint8_t[testLength];
6464

6565
ArrayBuffer buffer = ArrayBuffer::New(
66-
info.Env(), data, testLength, [](Env /*env*/, void* finalizeData) {
66+
info.Env(), data, testLength, [](NogcEnv /*env*/, void* finalizeData) {
6767
delete[] static_cast<uint8_t*>(finalizeData);
6868
finalizeCount++;
6969
});
@@ -94,7 +94,7 @@ Value CreateExternalBufferWithFinalizeHint(const CallbackInfo& info) {
9494
info.Env(),
9595
data,
9696
testLength,
97-
[](Env /*env*/, void* finalizeData, char* /*finalizeHint*/) {
97+
[](NogcEnv /*env*/, void* finalizeData, char* /*finalizeHint*/) {
9898
delete[] static_cast<uint8_t*>(finalizeData);
9999
finalizeCount++;
100100
},

test/buffer.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,14 @@ Value CreateExternalBufferWithFinalize(const CallbackInfo& info) {
5151

5252
uint16_t* data = new uint16_t[testLength];
5353

54-
Buffer<uint16_t> buffer = Buffer<uint16_t>::New(
55-
info.Env(), data, testLength, [](Env /*env*/, uint16_t* finalizeData) {
56-
delete[] finalizeData;
57-
finalizeCount++;
58-
});
54+
Buffer<uint16_t> buffer =
55+
Buffer<uint16_t>::New(info.Env(),
56+
data,
57+
testLength,
58+
[](NogcEnv /*env*/, uint16_t* finalizeData) {
59+
delete[] finalizeData;
60+
finalizeCount++;
61+
});
5962

6063
if (buffer.Length() != testLength) {
6164
Error::New(info.Env(), "Incorrect buffer length.")
@@ -83,7 +86,7 @@ Value CreateExternalBufferWithFinalizeHint(const CallbackInfo& info) {
8386
info.Env(),
8487
data,
8588
testLength,
86-
[](Env /*env*/, uint16_t* finalizeData, char* /*finalizeHint*/) {
89+
[](NogcEnv /*env*/, uint16_t* finalizeData, char* /*finalizeHint*/) {
8790
delete[] finalizeData;
8891
finalizeCount++;
8992
},

test/buffer_new_or_copy-inl.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ Value CreateOrCopyExternalBufferWithFinalize(const CallbackInfo& info) {
2424
uint16_t* data = new uint16_t[testLength];
2525
InitData(data, testLength);
2626

27-
Buffer<uint16_t> buffer = Buffer<uint16_t>::NewOrCopy(
28-
info.Env(), data, testLength, [](Env /*env*/, uint16_t* finalizeData) {
29-
delete[] finalizeData;
30-
finalizeCount++;
31-
});
27+
Buffer<uint16_t> buffer =
28+
Buffer<uint16_t>::NewOrCopy(info.Env(),
29+
data,
30+
testLength,
31+
[](NogcEnv /*env*/, uint16_t* finalizeData) {
32+
delete[] finalizeData;
33+
finalizeCount++;
34+
});
3235

3336
if (buffer.Length() != testLength) {
3437
Error::New(info.Env(), "Incorrect buffer length.")
@@ -51,7 +54,7 @@ Value CreateOrCopyExternalBufferWithFinalizeHint(const CallbackInfo& info) {
5154
info.Env(),
5255
data,
5356
testLength,
54-
[](Env /*env*/, uint16_t* finalizeData, char* /*finalizeHint*/) {
57+
[](NogcEnv /*env*/, uint16_t* finalizeData, char* /*finalizeHint*/) {
5558
delete[] finalizeData;
5659
finalizeCount++;
5760
},

test/external.cc

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ Value CreateExternal(const CallbackInfo& info) {
1414

1515
Value CreateExternalWithFinalize(const CallbackInfo& info) {
1616
finalizeCount = 0;
17-
return External<int>::New(info.Env(), new int(1), [](Env /*env*/, int* data) {
18-
delete data;
19-
finalizeCount++;
20-
});
17+
return External<int>::New(
18+
info.Env(), new int(1), [](NogcEnv /*env*/, int* data) {
19+
delete data;
20+
finalizeCount++;
21+
});
2122
}
2223

2324
Value CreateExternalWithFinalizeHint(const CallbackInfo& info) {
@@ -26,7 +27,7 @@ Value CreateExternalWithFinalizeHint(const CallbackInfo& info) {
2627
return External<int>::New(
2728
info.Env(),
2829
new int(1),
29-
[](Env /*env*/, int* data, char* /*hint*/) {
30+
[](NogcEnv /*env*/, int* data, char* /*hint*/) {
3031
delete data;
3132
finalizeCount++;
3233
},
@@ -55,6 +56,21 @@ Value GetFinalizeCount(const CallbackInfo& info) {
5556
}
5657

5758
Value CreateExternalWithFinalizeException(const CallbackInfo& info) {
59+
#ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
60+
auto o = External<int>::New(
61+
info.Env(), new int(1), [](NogcEnv /*env*/, int* data) { delete data; });
62+
63+
info.Env().AddPostFinalizer([](Env env) {
64+
Error error = Error::New(env, "Finalizer exception");
65+
#ifdef NAPI_CPP_EXCEPTIONS
66+
throw error;
67+
#else
68+
error.ThrowAsJavaScriptException();
69+
#endif
70+
});
71+
72+
return o;
73+
#else
5874
return External<int>::New(info.Env(), new int(1), [](Env env, int* data) {
5975
Error error = Error::New(env, "Finalizer exception");
6076
delete data;
@@ -64,8 +80,8 @@ Value CreateExternalWithFinalizeException(const CallbackInfo& info) {
6480
error.ThrowAsJavaScriptException();
6581
#endif
6682
});
83+
#endif
6784
}
68-
6985
} // end anonymous namespace
7086

7187
Object InitExternal(Env env) {

test/movable_callbacks.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1+
#include <iostream>
12
#include "napi.h"
23

34
using namespace Napi;
45

56
Value createExternal(const CallbackInfo& info) {
67
FunctionReference ref = Reference<Function>::New(info[0].As<Function>(), 1);
8+
9+
#ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
10+
auto env = info.Env();
11+
auto ret = External<char>::New(env, nullptr);
12+
13+
env.AddPostFinalizer(
14+
[ref = std::move(ref)](Napi::Env /*env*/) { ref.Call({}); });
15+
#else
716
auto ret = External<char>::New(
817
info.Env(),
918
nullptr,
1019
[ref = std::move(ref)](Napi::Env /*env*/, char* /*data*/) {
1120
ref.Call({});
1221
});
13-
22+
#endif
1423
return ret;
1524
}
1625

test/object/finalizer.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ static int dummy;
77
Value AddFinalizer(const CallbackInfo& info) {
88
ObjectReference* ref = new ObjectReference;
99
*ref = Persistent(Object::New(info.Env()));
10+
#ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
11+
info.Env().AddPostFinalizer(
12+
#else
1013
info[0].As<Object>().AddFinalizer(
14+
#endif
1115
[](Napi::Env /*env*/, ObjectReference* ref) {
1216
ref->Set("finalizerCalled", true);
1317
delete ref;
@@ -19,7 +23,11 @@ Value AddFinalizer(const CallbackInfo& info) {
1923
Value AddFinalizerWithHint(const CallbackInfo& info) {
2024
ObjectReference* ref = new ObjectReference;
2125
*ref = Persistent(Object::New(info.Env()));
26+
#ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
27+
info.Env().AddPostFinalizer(
28+
#else
2229
info[0].As<Object>().AddFinalizer(
30+
#endif
2331
[](Napi::Env /*env*/, ObjectReference* ref, int* dummy_p) {
2432
ref->Set("finalizerCalledWithCorrectHint", dummy_p == &dummy);
2533
delete ref;

0 commit comments

Comments
 (0)