node-addon-api 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +1 -1
  2. package/napi-inl.h +304 -98
  3. package/napi.h +60 -22
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -19,7 +19,7 @@ and exception handling semantics with low overhead.
19
19
  API references are available in the [doc](doc/README.md) directory.
20
20
 
21
21
  <!-- x-release-please-start-version -->
22
- ## Current version: 8.1.0
22
+ ## Current version: 8.2.0
23
23
  <!-- x-release-please-end -->
24
24
 
25
25
  (See [CHANGELOG.md](CHANGELOG.md) for complete Changelog)
package/napi-inl.h CHANGED
@@ -34,25 +34,10 @@ namespace details {
34
34
  // Node.js releases. Only necessary when they are used in napi.h and napi-inl.h.
35
35
  constexpr int napi_no_external_buffers_allowed = 22;
36
36
 
37
- #if (defined(NAPI_EXPERIMENTAL) && \
38
- defined(NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER))
39
- template <napi_finalize finalizer>
40
- inline void PostFinalizerWrapper(node_api_nogc_env nogc_env,
41
- void* data,
42
- void* hint) {
43
- napi_status status = node_api_post_finalizer(nogc_env, finalizer, data, hint);
44
- NAPI_FATAL_IF_FAILED(
45
- status, "PostFinalizerWrapper", "node_api_post_finalizer failed");
46
- }
47
- #else
48
- template <napi_finalize finalizer>
49
- inline void PostFinalizerWrapper(napi_env env, void* data, void* hint) {
50
- finalizer(env, data, hint);
51
- }
52
- #endif
53
-
54
37
  template <typename FreeType>
55
- inline void default_finalizer(napi_env /*env*/, void* data, void* /*hint*/) {
38
+ inline void default_basic_finalizer(node_api_nogc_env /*env*/,
39
+ void* data,
40
+ void* /*hint*/) {
56
41
  delete static_cast<FreeType*>(data);
57
42
  }
58
43
 
@@ -61,7 +46,7 @@ inline void default_finalizer(napi_env /*env*/, void* data, void* /*hint*/) {
61
46
  // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
62
47
  // available on all supported versions of Node.js.
63
48
  template <typename FreeType,
64
- napi_finalize finalizer = default_finalizer<FreeType>>
49
+ node_api_nogc_finalize finalizer = default_basic_finalizer<FreeType>>
65
50
  inline napi_status AttachData(napi_env env,
66
51
  napi_value obj,
67
52
  FreeType* data,
@@ -85,8 +70,7 @@ inline napi_status AttachData(napi_env env,
85
70
  }
86
71
  }
87
72
  #else // NAPI_VERSION >= 5
88
- status = napi_add_finalizer(
89
- env, obj, data, details::PostFinalizerWrapper<finalizer>, hint, nullptr);
73
+ status = napi_add_finalizer(env, obj, data, finalizer, hint, nullptr);
90
74
  #endif
91
75
  return status;
92
76
  }
@@ -206,23 +190,102 @@ napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info)
206
190
 
207
191
  template <typename T, typename Finalizer, typename Hint = void>
208
192
  struct FinalizeData {
209
- static inline void Wrapper(napi_env env,
193
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
194
+ template <typename F = Finalizer,
195
+ typename =
196
+ std::enable_if_t<std::is_invocable_v<F, node_api_nogc_env, T*>>>
197
+ #endif
198
+ static inline void Wrapper(node_api_nogc_env env,
199
+ void* data,
200
+ void* finalizeHint) NAPI_NOEXCEPT {
201
+ WrapVoidCallback([&] {
202
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
203
+ finalizeData->callback(env, static_cast<T*>(data));
204
+ delete finalizeData;
205
+ });
206
+ }
207
+
208
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
209
+ template <typename F = Finalizer,
210
+ typename = std::enable_if_t<
211
+ !std::is_invocable_v<F, node_api_nogc_env, T*>>,
212
+ typename = void>
213
+ static inline void Wrapper(node_api_nogc_env env,
210
214
  void* data,
211
215
  void* finalizeHint) NAPI_NOEXCEPT {
216
+ #ifdef NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS
217
+ static_assert(false,
218
+ "NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS defined: Finalizer "
219
+ "must be basic.");
220
+ #endif
221
+ napi_status status =
222
+ node_api_post_finalizer(env, WrapperGC, data, finalizeHint);
223
+ NAPI_FATAL_IF_FAILED(
224
+ status, "FinalizeData::Wrapper", "node_api_post_finalizer failed");
225
+ }
226
+ #endif
227
+
228
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
229
+ template <typename F = Finalizer,
230
+ typename = std::enable_if_t<
231
+ std::is_invocable_v<F, node_api_nogc_env, T*, Hint*>>>
232
+ #endif
233
+ static inline void WrapperWithHint(node_api_nogc_env env,
234
+ void* data,
235
+ void* finalizeHint) NAPI_NOEXCEPT {
212
236
  WrapVoidCallback([&] {
213
237
  FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
214
- finalizeData->callback(Env(env), static_cast<T*>(data));
238
+ finalizeData->callback(env, static_cast<T*>(data), finalizeData->hint);
215
239
  delete finalizeData;
216
240
  });
217
241
  }
218
242
 
219
- static inline void WrapperWithHint(napi_env env,
243
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
244
+ template <typename F = Finalizer,
245
+ typename = std::enable_if_t<
246
+ !std::is_invocable_v<F, node_api_nogc_env, T*, Hint*>>,
247
+ typename = void>
248
+ static inline void WrapperWithHint(node_api_nogc_env env,
220
249
  void* data,
221
250
  void* finalizeHint) NAPI_NOEXCEPT {
251
+ #ifdef NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS
252
+ static_assert(false,
253
+ "NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS defined: Finalizer "
254
+ "must be basic.");
255
+ #endif
256
+ napi_status status =
257
+ node_api_post_finalizer(env, WrapperGCWithHint, data, finalizeHint);
258
+ NAPI_FATAL_IF_FAILED(
259
+ status, "FinalizeData::Wrapper", "node_api_post_finalizer failed");
260
+ }
261
+ #endif
262
+
263
+ static inline void WrapperGCWithoutData(napi_env env,
264
+ void* /*data*/,
265
+ void* finalizeHint) NAPI_NOEXCEPT {
222
266
  WrapVoidCallback([&] {
223
267
  FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
224
- finalizeData->callback(
225
- Env(env), static_cast<T*>(data), finalizeData->hint);
268
+ finalizeData->callback(env);
269
+ delete finalizeData;
270
+ });
271
+ }
272
+
273
+ static inline void WrapperGC(napi_env env,
274
+ void* data,
275
+ void* finalizeHint) NAPI_NOEXCEPT {
276
+ WrapVoidCallback([&] {
277
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
278
+ finalizeData->callback(env, static_cast<T*>(data));
279
+ delete finalizeData;
280
+ });
281
+ }
282
+
283
+ static inline void WrapperGCWithHint(napi_env env,
284
+ void* data,
285
+ void* finalizeHint) NAPI_NOEXCEPT {
286
+ WrapVoidCallback([&] {
287
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
288
+ finalizeData->callback(env, static_cast<T*>(data), finalizeData->hint);
226
289
  delete finalizeData;
227
290
  });
228
291
  }
@@ -373,6 +436,34 @@ inline std::string StringFormat(const char* format, ...) {
373
436
  return result;
374
437
  }
375
438
 
439
+ template <typename T>
440
+ class HasExtendedFinalizer {
441
+ private:
442
+ template <typename U, void (U::*)(Napi::Env)>
443
+ struct SFINAE {};
444
+ template <typename U>
445
+ static char test(SFINAE<U, &U::Finalize>*);
446
+ template <typename U>
447
+ static int test(...);
448
+
449
+ public:
450
+ static constexpr bool value = sizeof(test<T>(0)) == sizeof(char);
451
+ };
452
+
453
+ template <typename T>
454
+ class HasBasicFinalizer {
455
+ private:
456
+ template <typename U, void (U::*)(Napi::BasicEnv)>
457
+ struct SFINAE {};
458
+ template <typename U>
459
+ static char test(SFINAE<U, &U::Finalize>*);
460
+ template <typename U>
461
+ static int test(...);
462
+
463
+ public:
464
+ static constexpr bool value = sizeof(test<T>(0)) == sizeof(char);
465
+ };
466
+
376
467
  } // namespace details
377
468
 
378
469
  #ifndef NODE_ADDON_API_DISABLE_DEPRECATED
@@ -482,15 +573,21 @@ inline Maybe<T> Just(const T& t) {
482
573
  }
483
574
 
484
575
  ////////////////////////////////////////////////////////////////////////////////
485
- // Env class
576
+ // BasicEnv / Env class
486
577
  ////////////////////////////////////////////////////////////////////////////////
487
578
 
488
- inline Env::Env(napi_env env) : _env(env) {}
579
+ inline BasicEnv::BasicEnv(node_api_nogc_env env) : _env(env) {}
489
580
 
490
- inline Env::operator napi_env() const {
581
+ inline BasicEnv::operator node_api_nogc_env() const {
491
582
  return _env;
492
583
  }
493
584
 
585
+ inline Env::Env(napi_env env) : BasicEnv(env) {}
586
+
587
+ inline Env::operator napi_env() const {
588
+ return const_cast<napi_env>(_env);
589
+ }
590
+
494
591
  inline Object Env::Global() const {
495
592
  napi_value value;
496
593
  napi_status status = napi_get_global(*this, &value);
@@ -514,7 +611,7 @@ inline Value Env::Null() const {
514
611
 
515
612
  inline bool Env::IsExceptionPending() const {
516
613
  bool result;
517
- napi_status status = napi_is_exception_pending(_env, &result);
614
+ napi_status status = napi_is_exception_pending(*this, &result);
518
615
  if (status != napi_ok)
519
616
  result = false; // Checking for a pending exception shouldn't throw.
520
617
  return result;
@@ -522,16 +619,16 @@ inline bool Env::IsExceptionPending() const {
522
619
 
523
620
  inline Error Env::GetAndClearPendingException() const {
524
621
  napi_value value;
525
- napi_status status = napi_get_and_clear_last_exception(_env, &value);
622
+ napi_status status = napi_get_and_clear_last_exception(*this, &value);
526
623
  if (status != napi_ok) {
527
624
  // Don't throw another exception when failing to get the exception!
528
625
  return Error();
529
626
  }
530
- return Error(_env, value);
627
+ return Error(*this, value);
531
628
  }
532
629
 
533
630
  inline MaybeOrValue<Value> Env::RunScript(const char* utf8script) const {
534
- String script = String::New(_env, utf8script);
631
+ String script = String::New(*this, utf8script);
535
632
  return RunScript(script);
536
633
  }
537
634
 
@@ -541,46 +638,46 @@ inline MaybeOrValue<Value> Env::RunScript(const std::string& utf8script) const {
541
638
 
542
639
  inline MaybeOrValue<Value> Env::RunScript(String script) const {
543
640
  napi_value result;
544
- napi_status status = napi_run_script(_env, script, &result);
641
+ napi_status status = napi_run_script(*this, script, &result);
545
642
  NAPI_RETURN_OR_THROW_IF_FAILED(
546
- _env, status, Napi::Value(_env, result), Napi::Value);
643
+ *this, status, Napi::Value(*this, result), Napi::Value);
547
644
  }
548
645
 
549
646
  #if NAPI_VERSION > 2
550
647
  template <typename Hook, typename Arg>
551
- void Env::CleanupHook<Hook, Arg>::Wrapper(void* data) NAPI_NOEXCEPT {
552
- auto* cleanupData =
553
- static_cast<typename Napi::Env::CleanupHook<Hook, Arg>::CleanupData*>(
554
- data);
648
+ void BasicEnv::CleanupHook<Hook, Arg>::Wrapper(void* data) NAPI_NOEXCEPT {
649
+ auto* cleanupData = static_cast<
650
+ typename Napi::BasicEnv::CleanupHook<Hook, Arg>::CleanupData*>(data);
555
651
  cleanupData->hook();
556
652
  delete cleanupData;
557
653
  }
558
654
 
559
655
  template <typename Hook, typename Arg>
560
- void Env::CleanupHook<Hook, Arg>::WrapperWithArg(void* data) NAPI_NOEXCEPT {
561
- auto* cleanupData =
562
- static_cast<typename Napi::Env::CleanupHook<Hook, Arg>::CleanupData*>(
563
- data);
656
+ void BasicEnv::CleanupHook<Hook, Arg>::WrapperWithArg(void* data)
657
+ NAPI_NOEXCEPT {
658
+ auto* cleanupData = static_cast<
659
+ typename Napi::BasicEnv::CleanupHook<Hook, Arg>::CleanupData*>(data);
564
660
  cleanupData->hook(static_cast<Arg*>(cleanupData->arg));
565
661
  delete cleanupData;
566
662
  }
567
663
  #endif // NAPI_VERSION > 2
568
664
 
569
665
  #if NAPI_VERSION > 5
570
- template <typename T, Env::Finalizer<T> fini>
571
- inline void Env::SetInstanceData(T* data) const {
666
+ template <typename T, BasicEnv::Finalizer<T> fini>
667
+ inline void BasicEnv::SetInstanceData(T* data) const {
572
668
  napi_status status = napi_set_instance_data(
573
669
  _env,
574
670
  data,
575
671
  [](napi_env env, void* data, void*) { fini(env, static_cast<T*>(data)); },
576
672
  nullptr);
577
- NAPI_THROW_IF_FAILED_VOID(_env, status);
673
+ NAPI_FATAL_IF_FAILED(
674
+ status, "BasicEnv::SetInstanceData", "invalid arguments");
578
675
  }
579
676
 
580
677
  template <typename DataType,
581
678
  typename HintType,
582
- Napi::Env::FinalizerWithHint<DataType, HintType> fini>
583
- inline void Env::SetInstanceData(DataType* data, HintType* hint) const {
679
+ Napi::BasicEnv::FinalizerWithHint<DataType, HintType> fini>
680
+ inline void BasicEnv::SetInstanceData(DataType* data, HintType* hint) const {
584
681
  napi_status status = napi_set_instance_data(
585
682
  _env,
586
683
  data,
@@ -588,35 +685,38 @@ inline void Env::SetInstanceData(DataType* data, HintType* hint) const {
588
685
  fini(env, static_cast<DataType*>(data), static_cast<HintType*>(hint));
589
686
  },
590
687
  hint);
591
- NAPI_THROW_IF_FAILED_VOID(_env, status);
688
+ NAPI_FATAL_IF_FAILED(
689
+ status, "BasicEnv::SetInstanceData", "invalid arguments");
592
690
  }
593
691
 
594
692
  template <typename T>
595
- inline T* Env::GetInstanceData() const {
693
+ inline T* BasicEnv::GetInstanceData() const {
596
694
  void* data = nullptr;
597
695
 
598
696
  napi_status status = napi_get_instance_data(_env, &data);
599
- NAPI_THROW_IF_FAILED(_env, status, nullptr);
697
+ NAPI_FATAL_IF_FAILED(
698
+ status, "BasicEnv::GetInstanceData", "invalid arguments");
600
699
 
601
700
  return static_cast<T*>(data);
602
701
  }
603
702
 
604
703
  template <typename T>
605
- void Env::DefaultFini(Env, T* data) {
704
+ void BasicEnv::DefaultFini(Env, T* data) {
606
705
  delete data;
607
706
  }
608
707
 
609
708
  template <typename DataType, typename HintType>
610
- void Env::DefaultFiniWithHint(Env, DataType* data, HintType*) {
709
+ void BasicEnv::DefaultFiniWithHint(Env, DataType* data, HintType*) {
611
710
  delete data;
612
711
  }
613
712
  #endif // NAPI_VERSION > 5
614
713
 
615
714
  #if NAPI_VERSION > 8
616
- inline const char* Env::GetModuleFileName() const {
715
+ inline const char* BasicEnv::GetModuleFileName() const {
617
716
  const char* result;
618
717
  napi_status status = node_api_get_module_file_name(_env, &result);
619
- NAPI_THROW_IF_FAILED(*this, status, nullptr);
718
+ NAPI_FATAL_IF_FAILED(
719
+ status, "BasicEnv::GetModuleFileName", "invalid arguments");
620
720
  return result;
621
721
  }
622
722
  #endif // NAPI_VERSION > 8
@@ -1805,8 +1905,7 @@ inline External<T> External<T>::New(napi_env env,
1805
1905
  napi_status status =
1806
1906
  napi_create_external(env,
1807
1907
  data,
1808
- details::PostFinalizerWrapper<
1809
- details::FinalizeData<T, Finalizer>::Wrapper>,
1908
+ details::FinalizeData<T, Finalizer>::Wrapper,
1810
1909
  finalizeData,
1811
1910
  &value);
1812
1911
  if (status != napi_ok) {
@@ -1829,8 +1928,7 @@ inline External<T> External<T>::New(napi_env env,
1829
1928
  napi_status status = napi_create_external(
1830
1929
  env,
1831
1930
  data,
1832
- details::PostFinalizerWrapper<
1833
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint>,
1931
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
1834
1932
  finalizeData,
1835
1933
  &value);
1836
1934
  if (status != napi_ok) {
@@ -1941,8 +2039,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
1941
2039
  env,
1942
2040
  externalData,
1943
2041
  byteLength,
1944
- details::PostFinalizerWrapper<
1945
- details::FinalizeData<void, Finalizer>::Wrapper>,
2042
+ details::FinalizeData<void, Finalizer>::Wrapper,
1946
2043
  finalizeData,
1947
2044
  &value);
1948
2045
  if (status != napi_ok) {
@@ -1967,8 +2064,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
1967
2064
  env,
1968
2065
  externalData,
1969
2066
  byteLength,
1970
- details::PostFinalizerWrapper<
1971
- details::FinalizeData<void, Finalizer, Hint>::WrapperWithHint>,
2067
+ details::FinalizeData<void, Finalizer, Hint>::WrapperWithHint,
1972
2068
  finalizeData,
1973
2069
  &value);
1974
2070
  if (status != napi_ok) {
@@ -2684,14 +2780,13 @@ inline Buffer<T> Buffer<T>::New(napi_env env,
2684
2780
  details::FinalizeData<T, Finalizer>* finalizeData =
2685
2781
  new details::FinalizeData<T, Finalizer>(
2686
2782
  {std::move(finalizeCallback), nullptr});
2687
- napi_status status = napi_create_external_buffer(
2688
- env,
2689
- length * sizeof(T),
2690
- data,
2691
- details::PostFinalizerWrapper<
2692
- details::FinalizeData<T, Finalizer>::Wrapper>,
2693
- finalizeData,
2694
- &value);
2783
+ napi_status status =
2784
+ napi_create_external_buffer(env,
2785
+ length * sizeof(T),
2786
+ data,
2787
+ details::FinalizeData<T, Finalizer>::Wrapper,
2788
+ finalizeData,
2789
+ &value);
2695
2790
  if (status != napi_ok) {
2696
2791
  delete finalizeData;
2697
2792
  NAPI_THROW_IF_FAILED(env, status, Buffer());
@@ -2714,8 +2809,7 @@ inline Buffer<T> Buffer<T>::New(napi_env env,
2714
2809
  env,
2715
2810
  length * sizeof(T),
2716
2811
  data,
2717
- details::PostFinalizerWrapper<
2718
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint>,
2812
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
2719
2813
  finalizeData,
2720
2814
  &value);
2721
2815
  if (status != napi_ok) {
@@ -2754,19 +2848,18 @@ inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2754
2848
  {std::move(finalizeCallback), nullptr});
2755
2849
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2756
2850
  napi_value value;
2757
- napi_status status = napi_create_external_buffer(
2758
- env,
2759
- length * sizeof(T),
2760
- data,
2761
- details::PostFinalizerWrapper<
2762
- details::FinalizeData<T, Finalizer>::Wrapper>,
2763
- finalizeData,
2764
- &value);
2851
+ napi_status status =
2852
+ napi_create_external_buffer(env,
2853
+ length * sizeof(T),
2854
+ data,
2855
+ details::FinalizeData<T, Finalizer>::Wrapper,
2856
+ finalizeData,
2857
+ &value);
2765
2858
  if (status == details::napi_no_external_buffers_allowed) {
2766
2859
  #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2767
2860
  // If we can't create an external buffer, we'll just copy the data.
2768
2861
  Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2769
- details::FinalizeData<T, Finalizer>::Wrapper(env, data, finalizeData);
2862
+ details::FinalizeData<T, Finalizer>::WrapperGC(env, data, finalizeData);
2770
2863
  return ret;
2771
2864
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2772
2865
  }
@@ -2794,15 +2887,14 @@ inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2794
2887
  env,
2795
2888
  length * sizeof(T),
2796
2889
  data,
2797
- details::PostFinalizerWrapper<
2798
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint>,
2890
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
2799
2891
  finalizeData,
2800
2892
  &value);
2801
2893
  if (status == details::napi_no_external_buffers_allowed) {
2802
2894
  #endif
2803
2895
  // If we can't create an external buffer, we'll just copy the data.
2804
2896
  Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2805
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint(
2897
+ details::FinalizeData<T, Finalizer, Hint>::WrapperGCWithHint(
2806
2898
  env, data, finalizeData);
2807
2899
  return ret;
2808
2900
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
@@ -3232,7 +3324,13 @@ template <typename T>
3232
3324
  inline Reference<T>::~Reference() {
3233
3325
  if (_ref != nullptr) {
3234
3326
  if (!_suppressDestruct) {
3327
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
3328
+ Env().PostFinalizer(
3329
+ [](Napi::Env env, napi_ref ref) { napi_delete_reference(env, ref); },
3330
+ _ref);
3331
+ #else
3235
3332
  napi_delete_reference(_env, _ref);
3333
+ #endif
3236
3334
  }
3237
3335
 
3238
3336
  _ref = nullptr;
@@ -4469,12 +4567,7 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
4469
4567
  napi_status status;
4470
4568
  napi_ref ref;
4471
4569
  T* instance = static_cast<T*>(this);
4472
- status = napi_wrap(env,
4473
- wrapper,
4474
- instance,
4475
- details::PostFinalizerWrapper<FinalizeCallback>,
4476
- nullptr,
4477
- &ref);
4570
+ status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref);
4478
4571
  NAPI_THROW_IF_FAILED_VOID(env, status);
4479
4572
 
4480
4573
  Reference<Object>* instanceRef = instance;
@@ -4837,6 +4930,9 @@ inline Value ObjectWrap<T>::OnCalledAsFunction(
4837
4930
  template <typename T>
4838
4931
  inline void ObjectWrap<T>::Finalize(Napi::Env /*env*/) {}
4839
4932
 
4933
+ template <typename T>
4934
+ inline void ObjectWrap<T>::Finalize(BasicEnv /*env*/) {}
4935
+
4840
4936
  template <typename T>
4841
4937
  inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
4842
4938
  napi_env env, napi_callback_info info) {
@@ -4922,10 +5018,59 @@ inline napi_value ObjectWrap<T>::StaticSetterCallbackWrapper(
4922
5018
  }
4923
5019
 
4924
5020
  template <typename T>
4925
- inline void ObjectWrap<T>::FinalizeCallback(napi_env env,
5021
+ inline void ObjectWrap<T>::FinalizeCallback(node_api_nogc_env env,
4926
5022
  void* data,
4927
5023
  void* /*hint*/) {
4928
- HandleScope scope(env);
5024
+ // If the child class does not override _any_ Finalize() method, `env` will be
5025
+ // unused because of the constexpr guards. Explicitly reference it here to
5026
+ // bypass compiler warnings.
5027
+ (void)env;
5028
+ T* instance = static_cast<T*>(data);
5029
+
5030
+ // Prevent ~ObjectWrap from calling napi_remove_wrap
5031
+ instance->_ref = nullptr;
5032
+
5033
+ // If class overrides the basic finalizer, execute it.
5034
+ if constexpr (details::HasBasicFinalizer<T>::value) {
5035
+ #ifndef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
5036
+ HandleScope scope(env);
5037
+ #endif
5038
+
5039
+ instance->Finalize(Napi::BasicEnv(env));
5040
+ }
5041
+
5042
+ // If class overrides the (extended) finalizer, either schedule it or
5043
+ // execute it immediately (depending on experimental features enabled).
5044
+ if constexpr (details::HasExtendedFinalizer<T>::value) {
5045
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
5046
+ // In experimental, attach via node_api_post_finalizer.
5047
+ // `PostFinalizeCallback` is responsible for deleting the `T* instance`,
5048
+ // after calling the user-provided finalizer.
5049
+ napi_status status =
5050
+ node_api_post_finalizer(env, PostFinalizeCallback, data, nullptr);
5051
+ NAPI_FATAL_IF_FAILED(status,
5052
+ "ObjectWrap<T>::FinalizeCallback",
5053
+ "node_api_post_finalizer failed");
5054
+ #else
5055
+ // In non-experimental, this `FinalizeCallback` already executes from a
5056
+ // non-basic environment. Execute the override directly.
5057
+ // `PostFinalizeCallback` is responsible for deleting the `T* instance`,
5058
+ // after calling the user-provided finalizer.
5059
+ HandleScope scope(env);
5060
+ PostFinalizeCallback(env, data, static_cast<void*>(nullptr));
5061
+ #endif
5062
+ }
5063
+ // If the instance does _not_ override the (extended) finalizer, delete the
5064
+ // `T* instance` immediately.
5065
+ else {
5066
+ delete instance;
5067
+ }
5068
+ }
5069
+
5070
+ template <typename T>
5071
+ inline void ObjectWrap<T>::PostFinalizeCallback(napi_env env,
5072
+ void* data,
5073
+ void* /*hint*/) {
4929
5074
  T* instance = static_cast<T*>(data);
4930
5075
  instance->Finalize(Napi::Env(env));
4931
5076
  delete instance;
@@ -6605,12 +6750,12 @@ inline Napi::Object Addon<T>::DefineProperties(
6605
6750
 
6606
6751
  #if NAPI_VERSION > 2
6607
6752
  template <typename Hook, typename Arg>
6608
- Env::CleanupHook<Hook, Arg> Env::AddCleanupHook(Hook hook, Arg* arg) {
6753
+ Env::CleanupHook<Hook, Arg> BasicEnv::AddCleanupHook(Hook hook, Arg* arg) {
6609
6754
  return CleanupHook<Hook, Arg>(*this, hook, arg);
6610
6755
  }
6611
6756
 
6612
6757
  template <typename Hook>
6613
- Env::CleanupHook<Hook> Env::AddCleanupHook(Hook hook) {
6758
+ Env::CleanupHook<Hook> BasicEnv::AddCleanupHook(Hook hook) {
6614
6759
  return CleanupHook<Hook>(*this, hook);
6615
6760
  }
6616
6761
 
@@ -6620,7 +6765,7 @@ Env::CleanupHook<Hook, Arg>::CleanupHook() {
6620
6765
  }
6621
6766
 
6622
6767
  template <typename Hook, typename Arg>
6623
- Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook)
6768
+ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::BasicEnv env, Hook hook)
6624
6769
  : wrapper(Env::CleanupHook<Hook, Arg>::Wrapper) {
6625
6770
  data = new CleanupData{std::move(hook), nullptr};
6626
6771
  napi_status status = napi_add_env_cleanup_hook(env, wrapper, data);
@@ -6631,7 +6776,9 @@ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook)
6631
6776
  }
6632
6777
 
6633
6778
  template <typename Hook, typename Arg>
6634
- Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook, Arg* arg)
6779
+ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::BasicEnv env,
6780
+ Hook hook,
6781
+ Arg* arg)
6635
6782
  : wrapper(Env::CleanupHook<Hook, Arg>::WrapperWithArg) {
6636
6783
  data = new CleanupData{std::move(hook), arg};
6637
6784
  napi_status status = napi_add_env_cleanup_hook(env, wrapper, data);
@@ -6642,7 +6789,7 @@ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook, Arg* arg)
6642
6789
  }
6643
6790
 
6644
6791
  template <class Hook, class Arg>
6645
- bool Env::CleanupHook<Hook, Arg>::Remove(Env env) {
6792
+ bool Env::CleanupHook<Hook, Arg>::Remove(BasicEnv env) {
6646
6793
  napi_status status = napi_remove_env_cleanup_hook(env, wrapper, data);
6647
6794
  delete data;
6648
6795
  data = nullptr;
@@ -6655,6 +6802,65 @@ bool Env::CleanupHook<Hook, Arg>::IsEmpty() const {
6655
6802
  }
6656
6803
  #endif // NAPI_VERSION > 2
6657
6804
 
6805
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
6806
+ template <typename FinalizerType>
6807
+ inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback) const {
6808
+ using T = void*;
6809
+ details::FinalizeData<T, FinalizerType>* finalizeData =
6810
+ new details::FinalizeData<T, FinalizerType>(
6811
+ {std::move(finalizeCallback), nullptr});
6812
+
6813
+ napi_status status = node_api_post_finalizer(
6814
+ _env,
6815
+ details::FinalizeData<T, FinalizerType>::WrapperGCWithoutData,
6816
+ static_cast<void*>(nullptr),
6817
+ finalizeData);
6818
+ if (status != napi_ok) {
6819
+ delete finalizeData;
6820
+ NAPI_FATAL_IF_FAILED(
6821
+ status, "BasicEnv::PostFinalizer", "invalid arguments");
6822
+ }
6823
+ }
6824
+
6825
+ template <typename FinalizerType, typename T>
6826
+ inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback,
6827
+ T* data) const {
6828
+ details::FinalizeData<T, FinalizerType>* finalizeData =
6829
+ new details::FinalizeData<T, FinalizerType>(
6830
+ {std::move(finalizeCallback), nullptr});
6831
+
6832
+ napi_status status = node_api_post_finalizer(
6833
+ _env,
6834
+ details::FinalizeData<T, FinalizerType>::WrapperGC,
6835
+ data,
6836
+ finalizeData);
6837
+ if (status != napi_ok) {
6838
+ delete finalizeData;
6839
+ NAPI_FATAL_IF_FAILED(
6840
+ status, "BasicEnv::PostFinalizer", "invalid arguments");
6841
+ }
6842
+ }
6843
+
6844
+ template <typename FinalizerType, typename T, typename Hint>
6845
+ inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback,
6846
+ T* data,
6847
+ Hint* finalizeHint) const {
6848
+ details::FinalizeData<T, FinalizerType, Hint>* finalizeData =
6849
+ new details::FinalizeData<T, FinalizerType, Hint>(
6850
+ {std::move(finalizeCallback), finalizeHint});
6851
+ napi_status status = node_api_post_finalizer(
6852
+ _env,
6853
+ details::FinalizeData<T, FinalizerType, Hint>::WrapperGCWithHint,
6854
+ data,
6855
+ finalizeData);
6856
+ if (status != napi_ok) {
6857
+ delete finalizeData;
6858
+ NAPI_FATAL_IF_FAILED(
6859
+ status, "BasicEnv::PostFinalizer", "invalid arguments");
6860
+ }
6861
+ }
6862
+ #endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
6863
+
6658
6864
  #ifdef NAPI_CPP_CUSTOM_NAMESPACE
6659
6865
  } // namespace NAPI_CPP_CUSTOM_NAMESPACE
6660
6866
  #endif
package/napi.h CHANGED
@@ -312,9 +312,9 @@ using MaybeOrValue = T;
312
312
  ///
313
313
  /// In the V8 JavaScript engine, a Node-API environment approximately
314
314
  /// corresponds to an Isolate.
315
- class Env {
315
+ class BasicEnv {
316
316
  private:
317
- napi_env _env;
317
+ node_api_nogc_env _env;
318
318
  #if NAPI_VERSION > 5
319
319
  template <typename T>
320
320
  static void DefaultFini(Env, T* data);
@@ -322,20 +322,21 @@ class Env {
322
322
  static void DefaultFiniWithHint(Env, DataType* data, HintType* hint);
323
323
  #endif // NAPI_VERSION > 5
324
324
  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;
325
+ BasicEnv(node_api_nogc_env env);
326
+ operator node_api_nogc_env() const;
327
+
328
+ // Without these operator overloads, the error:
329
+ //
330
+ // Use of overloaded operator '==' is ambiguous (with operand types
331
+ // 'Napi::Env' and 'Napi::Env')
332
+ //
333
+ // ... occurs when comparing foo.Env() == bar.Env() or foo.Env() == nullptr
334
+ bool operator==(const BasicEnv& other) const {
335
+ return _env == other._env;
336
+ };
337
+ bool operator==(std::nullptr_t /*other*/) const {
338
+ return _env == nullptr;
339
+ };
339
340
 
340
341
  #if NAPI_VERSION > 2
341
342
  template <typename Hook, typename Arg = void>
@@ -354,7 +355,7 @@ class Env {
354
355
 
355
356
  template <typename T>
356
357
  using Finalizer = void (*)(Env, T*);
357
- template <typename T, Finalizer<T> fini = Env::DefaultFini<T>>
358
+ template <typename T, Finalizer<T> fini = BasicEnv::DefaultFini<T>>
358
359
  void SetInstanceData(T* data) const;
359
360
 
360
361
  template <typename DataType, typename HintType>
@@ -362,7 +363,7 @@ class Env {
362
363
  template <typename DataType,
363
364
  typename HintType,
364
365
  FinalizerWithHint<DataType, HintType> fini =
365
- Env::DefaultFiniWithHint<DataType, HintType>>
366
+ BasicEnv::DefaultFiniWithHint<DataType, HintType>>
366
367
  void SetInstanceData(DataType* data, HintType* hint) const;
367
368
  #endif // NAPI_VERSION > 5
368
369
 
@@ -371,9 +372,9 @@ class Env {
371
372
  class CleanupHook {
372
373
  public:
373
374
  CleanupHook();
374
- CleanupHook(Env env, Hook hook, Arg* arg);
375
- CleanupHook(Env env, Hook hook);
376
- bool Remove(Env env);
375
+ CleanupHook(BasicEnv env, Hook hook, Arg* arg);
376
+ CleanupHook(BasicEnv env, Hook hook);
377
+ bool Remove(BasicEnv env);
377
378
  bool IsEmpty() const;
378
379
 
379
380
  private:
@@ -391,6 +392,39 @@ class Env {
391
392
  #if NAPI_VERSION > 8
392
393
  const char* GetModuleFileName() const;
393
394
  #endif // NAPI_VERSION > 8
395
+
396
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
397
+ template <typename FinalizerType>
398
+ inline void PostFinalizer(FinalizerType finalizeCallback) const;
399
+
400
+ template <typename FinalizerType, typename T>
401
+ inline void PostFinalizer(FinalizerType finalizeCallback, T* data) const;
402
+
403
+ template <typename FinalizerType, typename T, typename Hint>
404
+ inline void PostFinalizer(FinalizerType finalizeCallback,
405
+ T* data,
406
+ Hint* finalizeHint) const;
407
+ #endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
408
+
409
+ friend class Env;
410
+ };
411
+
412
+ class Env : public BasicEnv {
413
+ public:
414
+ Env(napi_env env);
415
+
416
+ operator napi_env() const;
417
+
418
+ Object Global() const;
419
+ Value Undefined() const;
420
+ Value Null() const;
421
+
422
+ bool IsExceptionPending() const;
423
+ Error GetAndClearPendingException() const;
424
+
425
+ MaybeOrValue<Value> RunScript(const char* utf8script) const;
426
+ MaybeOrValue<Value> RunScript(const std::string& utf8script) const;
427
+ MaybeOrValue<Value> RunScript(String script) const;
394
428
  };
395
429
 
396
430
  /// A JavaScript value of unknown type.
@@ -2415,6 +2449,7 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
2415
2449
  napi_property_attributes attributes = napi_default);
2416
2450
  static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo);
2417
2451
  virtual void Finalize(Napi::Env env);
2452
+ virtual void Finalize(BasicEnv env);
2418
2453
 
2419
2454
  private:
2420
2455
  using This = ObjectWrap<T>;
@@ -2429,7 +2464,10 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
2429
2464
  napi_callback_info info);
2430
2465
  static napi_value StaticSetterCallbackWrapper(napi_env env,
2431
2466
  napi_callback_info info);
2432
- static void FinalizeCallback(napi_env env, void* data, void* hint);
2467
+ static void FinalizeCallback(node_api_nogc_env env, void* data, void* hint);
2468
+
2469
+ static void PostFinalizeCallback(napi_env env, void* data, void* hint);
2470
+
2433
2471
  static Function DefineClass(Napi::Env env,
2434
2472
  const char* utf8name,
2435
2473
  const size_t props_count,
package/package.json CHANGED
@@ -478,7 +478,7 @@
478
478
  "lint:fix": "node tools/clang-format --fix && node tools/eslint-format --fix"
479
479
  },
480
480
  "pre-commit": "lint",
481
- "version": "8.1.0",
481
+ "version": "8.2.0",
482
482
  "support": true,
483
483
  "engines": {
484
484
  "node": "^18 || ^20 || >= 21"