node-addon-api 8.1.0 → 8.2.1

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 +325 -102
  3. package/napi.h +78 -24
  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.1
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_addon_api_basic_env /*env*/,
39
+ void* data,
40
+ void* /*hint*/) {
56
41
  delete static_cast<FreeType*>(data);
57
42
  }
58
43
 
@@ -60,8 +45,9 @@ inline void default_finalizer(napi_env /*env*/, void* data, void* /*hint*/) {
60
45
  // garbage-collected.
61
46
  // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
62
47
  // available on all supported versions of Node.js.
63
- template <typename FreeType,
64
- napi_finalize finalizer = default_finalizer<FreeType>>
48
+ template <
49
+ typename FreeType,
50
+ node_addon_api_basic_finalize finalizer = default_basic_finalizer<FreeType>>
65
51
  inline napi_status AttachData(napi_env env,
66
52
  napi_value obj,
67
53
  FreeType* data,
@@ -85,8 +71,7 @@ inline napi_status AttachData(napi_env env,
85
71
  }
86
72
  }
87
73
  #else // NAPI_VERSION >= 5
88
- status = napi_add_finalizer(
89
- env, obj, data, details::PostFinalizerWrapper<finalizer>, hint, nullptr);
74
+ status = napi_add_finalizer(env, obj, data, finalizer, hint, nullptr);
90
75
  #endif
91
76
  return status;
92
77
  }
@@ -206,23 +191,102 @@ napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info)
206
191
 
207
192
  template <typename T, typename Finalizer, typename Hint = void>
208
193
  struct FinalizeData {
209
- static inline void Wrapper(napi_env env,
194
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
195
+ template <typename F = Finalizer,
196
+ typename = std::enable_if_t<
197
+ std::is_invocable_v<F, node_addon_api_basic_env, T*>>>
198
+ #endif
199
+ static inline void Wrapper(node_addon_api_basic_env env,
210
200
  void* data,
211
201
  void* finalizeHint) NAPI_NOEXCEPT {
212
202
  WrapVoidCallback([&] {
213
203
  FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
214
- finalizeData->callback(Env(env), static_cast<T*>(data));
204
+ finalizeData->callback(env, static_cast<T*>(data));
205
+ delete finalizeData;
206
+ });
207
+ }
208
+
209
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
210
+ template <typename F = Finalizer,
211
+ typename = std::enable_if_t<
212
+ !std::is_invocable_v<F, node_addon_api_basic_env, T*>>,
213
+ typename = void>
214
+ static inline void Wrapper(node_addon_api_basic_env env,
215
+ void* data,
216
+ void* finalizeHint) NAPI_NOEXCEPT {
217
+ #ifdef NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS
218
+ static_assert(false,
219
+ "NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS defined: Finalizer "
220
+ "must be basic.");
221
+ #endif
222
+ napi_status status =
223
+ node_api_post_finalizer(env, WrapperGC, data, finalizeHint);
224
+ NAPI_FATAL_IF_FAILED(
225
+ status, "FinalizeData::Wrapper", "node_api_post_finalizer failed");
226
+ }
227
+ #endif
228
+
229
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
230
+ template <typename F = Finalizer,
231
+ typename = std::enable_if_t<
232
+ std::is_invocable_v<F, node_addon_api_basic_env, T*, Hint*>>>
233
+ #endif
234
+ static inline void WrapperWithHint(node_addon_api_basic_env env,
235
+ void* data,
236
+ void* finalizeHint) NAPI_NOEXCEPT {
237
+ WrapVoidCallback([&] {
238
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
239
+ finalizeData->callback(env, static_cast<T*>(data), finalizeData->hint);
215
240
  delete finalizeData;
216
241
  });
217
242
  }
218
243
 
219
- static inline void WrapperWithHint(napi_env env,
244
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
245
+ template <typename F = Finalizer,
246
+ typename = std::enable_if_t<
247
+ !std::is_invocable_v<F, node_addon_api_basic_env, T*, Hint*>>,
248
+ typename = void>
249
+ static inline void WrapperWithHint(node_addon_api_basic_env env,
220
250
  void* data,
221
251
  void* finalizeHint) NAPI_NOEXCEPT {
252
+ #ifdef NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS
253
+ static_assert(false,
254
+ "NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS defined: Finalizer "
255
+ "must be basic.");
256
+ #endif
257
+ napi_status status =
258
+ node_api_post_finalizer(env, WrapperGCWithHint, data, finalizeHint);
259
+ NAPI_FATAL_IF_FAILED(
260
+ status, "FinalizeData::Wrapper", "node_api_post_finalizer failed");
261
+ }
262
+ #endif
263
+
264
+ static inline void WrapperGCWithoutData(napi_env env,
265
+ void* /*data*/,
266
+ void* finalizeHint) NAPI_NOEXCEPT {
267
+ WrapVoidCallback([&] {
268
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
269
+ finalizeData->callback(env);
270
+ delete finalizeData;
271
+ });
272
+ }
273
+
274
+ static inline void WrapperGC(napi_env env,
275
+ void* data,
276
+ void* finalizeHint) NAPI_NOEXCEPT {
277
+ WrapVoidCallback([&] {
278
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
279
+ finalizeData->callback(env, static_cast<T*>(data));
280
+ delete finalizeData;
281
+ });
282
+ }
283
+
284
+ static inline void WrapperGCWithHint(napi_env env,
285
+ void* data,
286
+ void* finalizeHint) NAPI_NOEXCEPT {
222
287
  WrapVoidCallback([&] {
223
288
  FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
224
- finalizeData->callback(
225
- Env(env), static_cast<T*>(data), finalizeData->hint);
289
+ finalizeData->callback(env, static_cast<T*>(data), finalizeData->hint);
226
290
  delete finalizeData;
227
291
  });
228
292
  }
@@ -373,6 +437,34 @@ inline std::string StringFormat(const char* format, ...) {
373
437
  return result;
374
438
  }
375
439
 
440
+ template <typename T>
441
+ class HasExtendedFinalizer {
442
+ private:
443
+ template <typename U, void (U::*)(Napi::Env)>
444
+ struct SFINAE {};
445
+ template <typename U>
446
+ static char test(SFINAE<U, &U::Finalize>*);
447
+ template <typename U>
448
+ static int test(...);
449
+
450
+ public:
451
+ static constexpr bool value = sizeof(test<T>(0)) == sizeof(char);
452
+ };
453
+
454
+ template <typename T>
455
+ class HasBasicFinalizer {
456
+ private:
457
+ template <typename U, void (U::*)(Napi::BasicEnv)>
458
+ struct SFINAE {};
459
+ template <typename U>
460
+ static char test(SFINAE<U, &U::Finalize>*);
461
+ template <typename U>
462
+ static int test(...);
463
+
464
+ public:
465
+ static constexpr bool value = sizeof(test<T>(0)) == sizeof(char);
466
+ };
467
+
376
468
  } // namespace details
377
469
 
378
470
  #ifndef NODE_ADDON_API_DISABLE_DEPRECATED
@@ -482,15 +574,21 @@ inline Maybe<T> Just(const T& t) {
482
574
  }
483
575
 
484
576
  ////////////////////////////////////////////////////////////////////////////////
485
- // Env class
577
+ // BasicEnv / Env class
486
578
  ////////////////////////////////////////////////////////////////////////////////
487
579
 
488
- inline Env::Env(napi_env env) : _env(env) {}
580
+ inline BasicEnv::BasicEnv(node_addon_api_basic_env env) : _env(env) {}
489
581
 
490
- inline Env::operator napi_env() const {
582
+ inline BasicEnv::operator node_addon_api_basic_env() const {
491
583
  return _env;
492
584
  }
493
585
 
586
+ inline Env::Env(napi_env env) : BasicEnv(env) {}
587
+
588
+ inline Env::operator napi_env() const {
589
+ return const_cast<napi_env>(_env);
590
+ }
591
+
494
592
  inline Object Env::Global() const {
495
593
  napi_value value;
496
594
  napi_status status = napi_get_global(*this, &value);
@@ -514,7 +612,7 @@ inline Value Env::Null() const {
514
612
 
515
613
  inline bool Env::IsExceptionPending() const {
516
614
  bool result;
517
- napi_status status = napi_is_exception_pending(_env, &result);
615
+ napi_status status = napi_is_exception_pending(*this, &result);
518
616
  if (status != napi_ok)
519
617
  result = false; // Checking for a pending exception shouldn't throw.
520
618
  return result;
@@ -522,16 +620,16 @@ inline bool Env::IsExceptionPending() const {
522
620
 
523
621
  inline Error Env::GetAndClearPendingException() const {
524
622
  napi_value value;
525
- napi_status status = napi_get_and_clear_last_exception(_env, &value);
623
+ napi_status status = napi_get_and_clear_last_exception(*this, &value);
526
624
  if (status != napi_ok) {
527
625
  // Don't throw another exception when failing to get the exception!
528
626
  return Error();
529
627
  }
530
- return Error(_env, value);
628
+ return Error(*this, value);
531
629
  }
532
630
 
533
631
  inline MaybeOrValue<Value> Env::RunScript(const char* utf8script) const {
534
- String script = String::New(_env, utf8script);
632
+ String script = String::New(*this, utf8script);
535
633
  return RunScript(script);
536
634
  }
537
635
 
@@ -541,46 +639,46 @@ inline MaybeOrValue<Value> Env::RunScript(const std::string& utf8script) const {
541
639
 
542
640
  inline MaybeOrValue<Value> Env::RunScript(String script) const {
543
641
  napi_value result;
544
- napi_status status = napi_run_script(_env, script, &result);
642
+ napi_status status = napi_run_script(*this, script, &result);
545
643
  NAPI_RETURN_OR_THROW_IF_FAILED(
546
- _env, status, Napi::Value(_env, result), Napi::Value);
644
+ *this, status, Napi::Value(*this, result), Napi::Value);
547
645
  }
548
646
 
549
647
  #if NAPI_VERSION > 2
550
648
  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);
649
+ void BasicEnv::CleanupHook<Hook, Arg>::Wrapper(void* data) NAPI_NOEXCEPT {
650
+ auto* cleanupData = static_cast<
651
+ typename Napi::BasicEnv::CleanupHook<Hook, Arg>::CleanupData*>(data);
555
652
  cleanupData->hook();
556
653
  delete cleanupData;
557
654
  }
558
655
 
559
656
  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);
657
+ void BasicEnv::CleanupHook<Hook, Arg>::WrapperWithArg(void* data)
658
+ NAPI_NOEXCEPT {
659
+ auto* cleanupData = static_cast<
660
+ typename Napi::BasicEnv::CleanupHook<Hook, Arg>::CleanupData*>(data);
564
661
  cleanupData->hook(static_cast<Arg*>(cleanupData->arg));
565
662
  delete cleanupData;
566
663
  }
567
664
  #endif // NAPI_VERSION > 2
568
665
 
569
666
  #if NAPI_VERSION > 5
570
- template <typename T, Env::Finalizer<T> fini>
571
- inline void Env::SetInstanceData(T* data) const {
667
+ template <typename T, BasicEnv::Finalizer<T> fini>
668
+ inline void BasicEnv::SetInstanceData(T* data) const {
572
669
  napi_status status = napi_set_instance_data(
573
670
  _env,
574
671
  data,
575
672
  [](napi_env env, void* data, void*) { fini(env, static_cast<T*>(data)); },
576
673
  nullptr);
577
- NAPI_THROW_IF_FAILED_VOID(_env, status);
674
+ NAPI_FATAL_IF_FAILED(
675
+ status, "BasicEnv::SetInstanceData", "invalid arguments");
578
676
  }
579
677
 
580
678
  template <typename DataType,
581
679
  typename HintType,
582
- Napi::Env::FinalizerWithHint<DataType, HintType> fini>
583
- inline void Env::SetInstanceData(DataType* data, HintType* hint) const {
680
+ Napi::BasicEnv::FinalizerWithHint<DataType, HintType> fini>
681
+ inline void BasicEnv::SetInstanceData(DataType* data, HintType* hint) const {
584
682
  napi_status status = napi_set_instance_data(
585
683
  _env,
586
684
  data,
@@ -588,35 +686,38 @@ inline void Env::SetInstanceData(DataType* data, HintType* hint) const {
588
686
  fini(env, static_cast<DataType*>(data), static_cast<HintType*>(hint));
589
687
  },
590
688
  hint);
591
- NAPI_THROW_IF_FAILED_VOID(_env, status);
689
+ NAPI_FATAL_IF_FAILED(
690
+ status, "BasicEnv::SetInstanceData", "invalid arguments");
592
691
  }
593
692
 
594
693
  template <typename T>
595
- inline T* Env::GetInstanceData() const {
694
+ inline T* BasicEnv::GetInstanceData() const {
596
695
  void* data = nullptr;
597
696
 
598
697
  napi_status status = napi_get_instance_data(_env, &data);
599
- NAPI_THROW_IF_FAILED(_env, status, nullptr);
698
+ NAPI_FATAL_IF_FAILED(
699
+ status, "BasicEnv::GetInstanceData", "invalid arguments");
600
700
 
601
701
  return static_cast<T*>(data);
602
702
  }
603
703
 
604
704
  template <typename T>
605
- void Env::DefaultFini(Env, T* data) {
705
+ void BasicEnv::DefaultFini(Env, T* data) {
606
706
  delete data;
607
707
  }
608
708
 
609
709
  template <typename DataType, typename HintType>
610
- void Env::DefaultFiniWithHint(Env, DataType* data, HintType*) {
710
+ void BasicEnv::DefaultFiniWithHint(Env, DataType* data, HintType*) {
611
711
  delete data;
612
712
  }
613
713
  #endif // NAPI_VERSION > 5
614
714
 
615
715
  #if NAPI_VERSION > 8
616
- inline const char* Env::GetModuleFileName() const {
716
+ inline const char* BasicEnv::GetModuleFileName() const {
617
717
  const char* result;
618
718
  napi_status status = node_api_get_module_file_name(_env, &result);
619
- NAPI_THROW_IF_FAILED(*this, status, nullptr);
719
+ NAPI_FATAL_IF_FAILED(
720
+ status, "BasicEnv::GetModuleFileName", "invalid arguments");
620
721
  return result;
621
722
  }
622
723
  #endif // NAPI_VERSION > 8
@@ -796,6 +897,16 @@ inline T Value::As() const {
796
897
  return T(_env, _value);
797
898
  }
798
899
 
900
+ template <typename T>
901
+ inline T Value::UnsafeAs() const {
902
+ return T(_env, _value);
903
+ }
904
+
905
+ // static
906
+ inline void Value::CheckCast(napi_env /* env */, napi_value value) {
907
+ NAPI_CHECK(value != nullptr, "Value::CheckCast", "empty value");
908
+ }
909
+
799
910
  inline MaybeOrValue<Boolean> Value::ToBoolean() const {
800
911
  napi_value result;
801
912
  napi_status status = napi_coerce_to_bool(_env, _value, &result);
@@ -1203,12 +1314,15 @@ inline Symbol Symbol::New(napi_env env, napi_value description) {
1203
1314
 
1204
1315
  inline MaybeOrValue<Symbol> Symbol::WellKnown(napi_env env,
1205
1316
  const std::string& name) {
1317
+ // No need to check if the return value is a symbol or undefined.
1318
+ // Well known symbols are definite and it is an develop time error
1319
+ // if the symbol does not exist.
1206
1320
  #if defined(NODE_ADDON_API_ENABLE_MAYBE)
1207
1321
  Value symbol_obj;
1208
1322
  Value symbol_value;
1209
1323
  if (Napi::Env(env).Global().Get("Symbol").UnwrapTo(&symbol_obj) &&
1210
1324
  symbol_obj.As<Object>().Get(name).UnwrapTo(&symbol_value)) {
1211
- return Just<Symbol>(symbol_value.As<Symbol>());
1325
+ return Just<Symbol>(symbol_value.UnsafeAs<Symbol>());
1212
1326
  }
1213
1327
  return Nothing<Symbol>();
1214
1328
  #else
@@ -1217,7 +1331,7 @@ inline MaybeOrValue<Symbol> Symbol::WellKnown(napi_env env,
1217
1331
  .Get("Symbol")
1218
1332
  .As<Object>()
1219
1333
  .Get(name)
1220
- .As<Symbol>();
1334
+ .UnsafeAs<Symbol>();
1221
1335
  #endif
1222
1336
  }
1223
1337
 
@@ -1435,7 +1549,10 @@ inline void Object::CheckCast(napi_env env, napi_value value) {
1435
1549
  napi_valuetype type;
1436
1550
  napi_status status = napi_typeof(env, value, &type);
1437
1551
  NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed");
1438
- NAPI_INTERNAL_CHECK_EQ(type, napi_object, "%d", "Object::CheckCast");
1552
+ NAPI_INTERNAL_CHECK(type == napi_object || type == napi_function,
1553
+ "Object::CheckCast",
1554
+ "Expect napi_object or napi_function, but got %d.",
1555
+ type);
1439
1556
  }
1440
1557
 
1441
1558
  inline Object::Object() : TypeTaggable() {}
@@ -1805,8 +1922,7 @@ inline External<T> External<T>::New(napi_env env,
1805
1922
  napi_status status =
1806
1923
  napi_create_external(env,
1807
1924
  data,
1808
- details::PostFinalizerWrapper<
1809
- details::FinalizeData<T, Finalizer>::Wrapper>,
1925
+ details::FinalizeData<T, Finalizer>::Wrapper,
1810
1926
  finalizeData,
1811
1927
  &value);
1812
1928
  if (status != napi_ok) {
@@ -1829,8 +1945,7 @@ inline External<T> External<T>::New(napi_env env,
1829
1945
  napi_status status = napi_create_external(
1830
1946
  env,
1831
1947
  data,
1832
- details::PostFinalizerWrapper<
1833
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint>,
1948
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
1834
1949
  finalizeData,
1835
1950
  &value);
1836
1951
  if (status != napi_ok) {
@@ -1941,8 +2056,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
1941
2056
  env,
1942
2057
  externalData,
1943
2058
  byteLength,
1944
- details::PostFinalizerWrapper<
1945
- details::FinalizeData<void, Finalizer>::Wrapper>,
2059
+ details::FinalizeData<void, Finalizer>::Wrapper,
1946
2060
  finalizeData,
1947
2061
  &value);
1948
2062
  if (status != napi_ok) {
@@ -1967,8 +2081,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
1967
2081
  env,
1968
2082
  externalData,
1969
2083
  byteLength,
1970
- details::PostFinalizerWrapper<
1971
- details::FinalizeData<void, Finalizer, Hint>::WrapperWithHint>,
2084
+ details::FinalizeData<void, Finalizer, Hint>::WrapperWithHint,
1972
2085
  finalizeData,
1973
2086
  &value);
1974
2087
  if (status != napi_ok) {
@@ -2684,14 +2797,13 @@ inline Buffer<T> Buffer<T>::New(napi_env env,
2684
2797
  details::FinalizeData<T, Finalizer>* finalizeData =
2685
2798
  new details::FinalizeData<T, Finalizer>(
2686
2799
  {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);
2800
+ napi_status status =
2801
+ napi_create_external_buffer(env,
2802
+ length * sizeof(T),
2803
+ data,
2804
+ details::FinalizeData<T, Finalizer>::Wrapper,
2805
+ finalizeData,
2806
+ &value);
2695
2807
  if (status != napi_ok) {
2696
2808
  delete finalizeData;
2697
2809
  NAPI_THROW_IF_FAILED(env, status, Buffer());
@@ -2714,8 +2826,7 @@ inline Buffer<T> Buffer<T>::New(napi_env env,
2714
2826
  env,
2715
2827
  length * sizeof(T),
2716
2828
  data,
2717
- details::PostFinalizerWrapper<
2718
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint>,
2829
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
2719
2830
  finalizeData,
2720
2831
  &value);
2721
2832
  if (status != napi_ok) {
@@ -2754,19 +2865,18 @@ inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2754
2865
  {std::move(finalizeCallback), nullptr});
2755
2866
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2756
2867
  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);
2868
+ napi_status status =
2869
+ napi_create_external_buffer(env,
2870
+ length * sizeof(T),
2871
+ data,
2872
+ details::FinalizeData<T, Finalizer>::Wrapper,
2873
+ finalizeData,
2874
+ &value);
2765
2875
  if (status == details::napi_no_external_buffers_allowed) {
2766
2876
  #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2767
2877
  // If we can't create an external buffer, we'll just copy the data.
2768
2878
  Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2769
- details::FinalizeData<T, Finalizer>::Wrapper(env, data, finalizeData);
2879
+ details::FinalizeData<T, Finalizer>::WrapperGC(env, data, finalizeData);
2770
2880
  return ret;
2771
2881
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2772
2882
  }
@@ -2794,15 +2904,14 @@ inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2794
2904
  env,
2795
2905
  length * sizeof(T),
2796
2906
  data,
2797
- details::PostFinalizerWrapper<
2798
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint>,
2907
+ details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
2799
2908
  finalizeData,
2800
2909
  &value);
2801
2910
  if (status == details::napi_no_external_buffers_allowed) {
2802
2911
  #endif
2803
2912
  // If we can't create an external buffer, we'll just copy the data.
2804
2913
  Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2805
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint(
2914
+ details::FinalizeData<T, Finalizer, Hint>::WrapperGCWithHint(
2806
2915
  env, data, finalizeData);
2807
2916
  return ret;
2808
2917
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
@@ -3232,7 +3341,13 @@ template <typename T>
3232
3341
  inline Reference<T>::~Reference() {
3233
3342
  if (_ref != nullptr) {
3234
3343
  if (!_suppressDestruct) {
3344
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
3345
+ Env().PostFinalizer(
3346
+ [](Napi::Env env, napi_ref ref) { napi_delete_reference(env, ref); },
3347
+ _ref);
3348
+ #else
3235
3349
  napi_delete_reference(_env, _ref);
3350
+ #endif
3236
3351
  }
3237
3352
 
3238
3353
  _ref = nullptr;
@@ -4469,12 +4584,7 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
4469
4584
  napi_status status;
4470
4585
  napi_ref ref;
4471
4586
  T* instance = static_cast<T*>(this);
4472
- status = napi_wrap(env,
4473
- wrapper,
4474
- instance,
4475
- details::PostFinalizerWrapper<FinalizeCallback>,
4476
- nullptr,
4477
- &ref);
4587
+ status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref);
4478
4588
  NAPI_THROW_IF_FAILED_VOID(env, status);
4479
4589
 
4480
4590
  Reference<Object>* instanceRef = instance;
@@ -4837,6 +4947,9 @@ inline Value ObjectWrap<T>::OnCalledAsFunction(
4837
4947
  template <typename T>
4838
4948
  inline void ObjectWrap<T>::Finalize(Napi::Env /*env*/) {}
4839
4949
 
4950
+ template <typename T>
4951
+ inline void ObjectWrap<T>::Finalize(BasicEnv /*env*/) {}
4952
+
4840
4953
  template <typename T>
4841
4954
  inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
4842
4955
  napi_env env, napi_callback_info info) {
@@ -4922,10 +5035,59 @@ inline napi_value ObjectWrap<T>::StaticSetterCallbackWrapper(
4922
5035
  }
4923
5036
 
4924
5037
  template <typename T>
4925
- inline void ObjectWrap<T>::FinalizeCallback(napi_env env,
5038
+ inline void ObjectWrap<T>::FinalizeCallback(node_addon_api_basic_env env,
4926
5039
  void* data,
4927
5040
  void* /*hint*/) {
4928
- HandleScope scope(env);
5041
+ // If the child class does not override _any_ Finalize() method, `env` will be
5042
+ // unused because of the constexpr guards. Explicitly reference it here to
5043
+ // bypass compiler warnings.
5044
+ (void)env;
5045
+ T* instance = static_cast<T*>(data);
5046
+
5047
+ // Prevent ~ObjectWrap from calling napi_remove_wrap
5048
+ instance->_ref = nullptr;
5049
+
5050
+ // If class overrides the basic finalizer, execute it.
5051
+ if constexpr (details::HasBasicFinalizer<T>::value) {
5052
+ #ifndef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
5053
+ HandleScope scope(env);
5054
+ #endif
5055
+
5056
+ instance->Finalize(Napi::BasicEnv(env));
5057
+ }
5058
+
5059
+ // If class overrides the (extended) finalizer, either schedule it or
5060
+ // execute it immediately (depending on experimental features enabled).
5061
+ if constexpr (details::HasExtendedFinalizer<T>::value) {
5062
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
5063
+ // In experimental, attach via node_api_post_finalizer.
5064
+ // `PostFinalizeCallback` is responsible for deleting the `T* instance`,
5065
+ // after calling the user-provided finalizer.
5066
+ napi_status status =
5067
+ node_api_post_finalizer(env, PostFinalizeCallback, data, nullptr);
5068
+ NAPI_FATAL_IF_FAILED(status,
5069
+ "ObjectWrap<T>::FinalizeCallback",
5070
+ "node_api_post_finalizer failed");
5071
+ #else
5072
+ // In non-experimental, this `FinalizeCallback` already executes from a
5073
+ // non-basic environment. Execute the override directly.
5074
+ // `PostFinalizeCallback` is responsible for deleting the `T* instance`,
5075
+ // after calling the user-provided finalizer.
5076
+ HandleScope scope(env);
5077
+ PostFinalizeCallback(env, data, static_cast<void*>(nullptr));
5078
+ #endif
5079
+ }
5080
+ // If the instance does _not_ override the (extended) finalizer, delete the
5081
+ // `T* instance` immediately.
5082
+ else {
5083
+ delete instance;
5084
+ }
5085
+ }
5086
+
5087
+ template <typename T>
5088
+ inline void ObjectWrap<T>::PostFinalizeCallback(napi_env env,
5089
+ void* data,
5090
+ void* /*hint*/) {
4929
5091
  T* instance = static_cast<T*>(data);
4930
5092
  instance->Finalize(Napi::Env(env));
4931
5093
  delete instance;
@@ -6605,12 +6767,12 @@ inline Napi::Object Addon<T>::DefineProperties(
6605
6767
 
6606
6768
  #if NAPI_VERSION > 2
6607
6769
  template <typename Hook, typename Arg>
6608
- Env::CleanupHook<Hook, Arg> Env::AddCleanupHook(Hook hook, Arg* arg) {
6770
+ Env::CleanupHook<Hook, Arg> BasicEnv::AddCleanupHook(Hook hook, Arg* arg) {
6609
6771
  return CleanupHook<Hook, Arg>(*this, hook, arg);
6610
6772
  }
6611
6773
 
6612
6774
  template <typename Hook>
6613
- Env::CleanupHook<Hook> Env::AddCleanupHook(Hook hook) {
6775
+ Env::CleanupHook<Hook> BasicEnv::AddCleanupHook(Hook hook) {
6614
6776
  return CleanupHook<Hook>(*this, hook);
6615
6777
  }
6616
6778
 
@@ -6620,7 +6782,7 @@ Env::CleanupHook<Hook, Arg>::CleanupHook() {
6620
6782
  }
6621
6783
 
6622
6784
  template <typename Hook, typename Arg>
6623
- Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook)
6785
+ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::BasicEnv env, Hook hook)
6624
6786
  : wrapper(Env::CleanupHook<Hook, Arg>::Wrapper) {
6625
6787
  data = new CleanupData{std::move(hook), nullptr};
6626
6788
  napi_status status = napi_add_env_cleanup_hook(env, wrapper, data);
@@ -6631,7 +6793,9 @@ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook)
6631
6793
  }
6632
6794
 
6633
6795
  template <typename Hook, typename Arg>
6634
- Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook, Arg* arg)
6796
+ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::BasicEnv env,
6797
+ Hook hook,
6798
+ Arg* arg)
6635
6799
  : wrapper(Env::CleanupHook<Hook, Arg>::WrapperWithArg) {
6636
6800
  data = new CleanupData{std::move(hook), arg};
6637
6801
  napi_status status = napi_add_env_cleanup_hook(env, wrapper, data);
@@ -6642,7 +6806,7 @@ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook, Arg* arg)
6642
6806
  }
6643
6807
 
6644
6808
  template <class Hook, class Arg>
6645
- bool Env::CleanupHook<Hook, Arg>::Remove(Env env) {
6809
+ bool Env::CleanupHook<Hook, Arg>::Remove(BasicEnv env) {
6646
6810
  napi_status status = napi_remove_env_cleanup_hook(env, wrapper, data);
6647
6811
  delete data;
6648
6812
  data = nullptr;
@@ -6655,6 +6819,65 @@ bool Env::CleanupHook<Hook, Arg>::IsEmpty() const {
6655
6819
  }
6656
6820
  #endif // NAPI_VERSION > 2
6657
6821
 
6822
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
6823
+ template <typename FinalizerType>
6824
+ inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback) const {
6825
+ using T = void*;
6826
+ details::FinalizeData<T, FinalizerType>* finalizeData =
6827
+ new details::FinalizeData<T, FinalizerType>(
6828
+ {std::move(finalizeCallback), nullptr});
6829
+
6830
+ napi_status status = node_api_post_finalizer(
6831
+ _env,
6832
+ details::FinalizeData<T, FinalizerType>::WrapperGCWithoutData,
6833
+ static_cast<void*>(nullptr),
6834
+ finalizeData);
6835
+ if (status != napi_ok) {
6836
+ delete finalizeData;
6837
+ NAPI_FATAL_IF_FAILED(
6838
+ status, "BasicEnv::PostFinalizer", "invalid arguments");
6839
+ }
6840
+ }
6841
+
6842
+ template <typename FinalizerType, typename T>
6843
+ inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback,
6844
+ T* data) const {
6845
+ details::FinalizeData<T, FinalizerType>* finalizeData =
6846
+ new details::FinalizeData<T, FinalizerType>(
6847
+ {std::move(finalizeCallback), nullptr});
6848
+
6849
+ napi_status status = node_api_post_finalizer(
6850
+ _env,
6851
+ details::FinalizeData<T, FinalizerType>::WrapperGC,
6852
+ data,
6853
+ finalizeData);
6854
+ if (status != napi_ok) {
6855
+ delete finalizeData;
6856
+ NAPI_FATAL_IF_FAILED(
6857
+ status, "BasicEnv::PostFinalizer", "invalid arguments");
6858
+ }
6859
+ }
6860
+
6861
+ template <typename FinalizerType, typename T, typename Hint>
6862
+ inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback,
6863
+ T* data,
6864
+ Hint* finalizeHint) const {
6865
+ details::FinalizeData<T, FinalizerType, Hint>* finalizeData =
6866
+ new details::FinalizeData<T, FinalizerType, Hint>(
6867
+ {std::move(finalizeCallback), finalizeHint});
6868
+ napi_status status = node_api_post_finalizer(
6869
+ _env,
6870
+ details::FinalizeData<T, FinalizerType, Hint>::WrapperGCWithHint,
6871
+ data,
6872
+ finalizeData);
6873
+ if (status != napi_ok) {
6874
+ delete finalizeData;
6875
+ NAPI_FATAL_IF_FAILED(
6876
+ status, "BasicEnv::PostFinalizer", "invalid arguments");
6877
+ }
6878
+ }
6879
+ #endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
6880
+
6658
6881
  #ifdef NAPI_CPP_CUSTOM_NAMESPACE
6659
6882
  } // namespace NAPI_CPP_CUSTOM_NAMESPACE
6660
6883
  #endif
package/napi.h CHANGED
@@ -299,6 +299,14 @@ template <typename T>
299
299
  using MaybeOrValue = T;
300
300
  #endif
301
301
 
302
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
303
+ using node_addon_api_basic_env = node_api_nogc_env;
304
+ using node_addon_api_basic_finalize = node_api_nogc_finalize;
305
+ #else
306
+ using node_addon_api_basic_env = napi_env;
307
+ using node_addon_api_basic_finalize = napi_finalize;
308
+ #endif
309
+
302
310
  /// Environment for Node-API values and operations.
303
311
  ///
304
312
  /// All Node-API values and operations must be associated with an environment.
@@ -307,14 +315,13 @@ using MaybeOrValue = T;
307
315
  /// Node-API operations within the callback. (Many methods infer the
308
316
  /// environment from the `this` instance that the method is called on.)
309
317
  ///
310
- /// In the future, multiple environments per process may be supported,
311
- /// although current implementations only support one environment per process.
318
+ /// Multiple environments may co-exist in a single process or a thread.
312
319
  ///
313
320
  /// In the V8 JavaScript engine, a Node-API environment approximately
314
321
  /// corresponds to an Isolate.
315
- class Env {
322
+ class BasicEnv {
316
323
  private:
317
- napi_env _env;
324
+ node_addon_api_basic_env _env;
318
325
  #if NAPI_VERSION > 5
319
326
  template <typename T>
320
327
  static void DefaultFini(Env, T* data);
@@ -322,20 +329,22 @@ class Env {
322
329
  static void DefaultFiniWithHint(Env, DataType* data, HintType* hint);
323
330
  #endif // NAPI_VERSION > 5
324
331
  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;
332
+ BasicEnv(node_addon_api_basic_env env);
333
+
334
+ operator node_addon_api_basic_env() const;
335
+
336
+ // Without these operator overloads, the error:
337
+ //
338
+ // Use of overloaded operator '==' is ambiguous (with operand types
339
+ // 'Napi::Env' and 'Napi::Env')
340
+ //
341
+ // ... occurs when comparing foo.Env() == bar.Env() or foo.Env() == nullptr
342
+ bool operator==(const BasicEnv& other) const {
343
+ return _env == other._env;
344
+ };
345
+ bool operator==(std::nullptr_t /*other*/) const {
346
+ return _env == nullptr;
347
+ };
339
348
 
340
349
  #if NAPI_VERSION > 2
341
350
  template <typename Hook, typename Arg = void>
@@ -354,7 +363,7 @@ class Env {
354
363
 
355
364
  template <typename T>
356
365
  using Finalizer = void (*)(Env, T*);
357
- template <typename T, Finalizer<T> fini = Env::DefaultFini<T>>
366
+ template <typename T, Finalizer<T> fini = BasicEnv::DefaultFini<T>>
358
367
  void SetInstanceData(T* data) const;
359
368
 
360
369
  template <typename DataType, typename HintType>
@@ -362,7 +371,7 @@ class Env {
362
371
  template <typename DataType,
363
372
  typename HintType,
364
373
  FinalizerWithHint<DataType, HintType> fini =
365
- Env::DefaultFiniWithHint<DataType, HintType>>
374
+ BasicEnv::DefaultFiniWithHint<DataType, HintType>>
366
375
  void SetInstanceData(DataType* data, HintType* hint) const;
367
376
  #endif // NAPI_VERSION > 5
368
377
 
@@ -371,9 +380,9 @@ class Env {
371
380
  class CleanupHook {
372
381
  public:
373
382
  CleanupHook();
374
- CleanupHook(Env env, Hook hook, Arg* arg);
375
- CleanupHook(Env env, Hook hook);
376
- bool Remove(Env env);
383
+ CleanupHook(BasicEnv env, Hook hook, Arg* arg);
384
+ CleanupHook(BasicEnv env, Hook hook);
385
+ bool Remove(BasicEnv env);
377
386
  bool IsEmpty() const;
378
387
 
379
388
  private:
@@ -391,6 +400,39 @@ class Env {
391
400
  #if NAPI_VERSION > 8
392
401
  const char* GetModuleFileName() const;
393
402
  #endif // NAPI_VERSION > 8
403
+
404
+ #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
405
+ template <typename FinalizerType>
406
+ inline void PostFinalizer(FinalizerType finalizeCallback) const;
407
+
408
+ template <typename FinalizerType, typename T>
409
+ inline void PostFinalizer(FinalizerType finalizeCallback, T* data) const;
410
+
411
+ template <typename FinalizerType, typename T, typename Hint>
412
+ inline void PostFinalizer(FinalizerType finalizeCallback,
413
+ T* data,
414
+ Hint* finalizeHint) const;
415
+ #endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER
416
+
417
+ friend class Env;
418
+ };
419
+
420
+ class Env : public BasicEnv {
421
+ public:
422
+ Env(napi_env env);
423
+
424
+ operator napi_env() const;
425
+
426
+ Object Global() const;
427
+ Value Undefined() const;
428
+ Value Null() const;
429
+
430
+ bool IsExceptionPending() const;
431
+ Error GetAndClearPendingException() const;
432
+
433
+ MaybeOrValue<Value> RunScript(const char* utf8script) const;
434
+ MaybeOrValue<Value> RunScript(const std::string& utf8script) const;
435
+ MaybeOrValue<Value> RunScript(String script) const;
394
436
  };
395
437
 
396
438
  /// A JavaScript value of unknown type.
@@ -427,6 +469,8 @@ class Value {
427
469
  template <typename T>
428
470
  static Value From(napi_env env, const T& value);
429
471
 
472
+ static void CheckCast(napi_env env, napi_value value);
473
+
430
474
  /// Converts to a Node-API value primitive.
431
475
  ///
432
476
  /// If the instance is _empty_, this returns `nullptr`.
@@ -493,6 +537,10 @@ class Value {
493
537
  template <typename T>
494
538
  T As() const;
495
539
 
540
+ // Unsafe Value::As(), should be avoided.
541
+ template <typename T>
542
+ T UnsafeAs() const;
543
+
496
544
  MaybeOrValue<Boolean> ToBoolean()
497
545
  const; ///< Coerces a value to a JavaScript boolean.
498
546
  MaybeOrValue<Number> ToNumber()
@@ -2415,6 +2463,7 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
2415
2463
  napi_property_attributes attributes = napi_default);
2416
2464
  static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo);
2417
2465
  virtual void Finalize(Napi::Env env);
2466
+ virtual void Finalize(BasicEnv env);
2418
2467
 
2419
2468
  private:
2420
2469
  using This = ObjectWrap<T>;
@@ -2429,7 +2478,12 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
2429
2478
  napi_callback_info info);
2430
2479
  static napi_value StaticSetterCallbackWrapper(napi_env env,
2431
2480
  napi_callback_info info);
2432
- static void FinalizeCallback(napi_env env, void* data, void* hint);
2481
+ static void FinalizeCallback(node_addon_api_basic_env env,
2482
+ void* data,
2483
+ void* hint);
2484
+
2485
+ static void PostFinalizeCallback(napi_env env, void* data, void* hint);
2486
+
2433
2487
  static Function DefineClass(Napi::Env env,
2434
2488
  const char* utf8name,
2435
2489
  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.1",
482
482
  "support": true,
483
483
  "engines": {
484
484
  "node": "^18 || ^20 || >= 21"