node-addon-api 8.0.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.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # **node-addon-api module**
2
2
 
3
+ [![codecov](https://codecov.io/gh/nodejs/node-addon-api/branch/main/graph/badge.svg)](https://app.codecov.io/gh/nodejs/node-addon-api/tree/main)
4
+
3
5
  [![NPM](https://nodei.co/npm/node-addon-api.png?downloads=true&downloadRank=true)](https://nodei.co/npm/node-addon-api/) [![NPM](https://nodei.co/npm-dl/node-addon-api.png?months=6&height=1)](https://nodei.co/npm/node-addon-api/)
4
6
 
5
7
  This module contains **header-only C++ wrapper classes** which simplify
@@ -16,7 +18,9 @@ and exception handling semantics with low overhead.
16
18
 
17
19
  API references are available in the [doc](doc/README.md) directory.
18
20
 
19
- ## Current version: 8.0.0
21
+ <!-- x-release-please-start-version -->
22
+ ## Current version: 8.2.0
23
+ <!-- x-release-please-end -->
20
24
 
21
25
  (See [CHANGELOG.md](CHANGELOG.md) for complete Changelog)
22
26
 
package/common.gypi CHANGED
@@ -5,6 +5,7 @@
5
5
  },
6
6
  'conditions': [
7
7
  ['NAPI_VERSION!=""', { 'defines': ['NAPI_VERSION=<@(NAPI_VERSION)'] } ],
8
+ ['NAPI_VERSION==2147483647', { 'defines': ['NAPI_EXPERIMENTAL'] } ],
8
9
  ['disable_deprecated=="true"', {
9
10
  'defines': ['NODE_ADDON_API_DISABLE_DEPRECATED']
10
11
  }],
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const path = require('path');
2
+ const { version } = require('./package.json');
2
3
 
3
4
  const includeDir = path.relative('.', __dirname);
4
5
 
@@ -7,6 +8,7 @@ module.exports = {
7
8
  include_dir: includeDir,
8
9
  gyp: path.join(includeDir, 'node_api.gyp:nothing'), // deprecated.
9
10
  targets: path.join(includeDir, 'node_addon_api.gyp'),
11
+ version,
10
12
  isNodeApiBuiltin: true,
11
13
  needsFlag: false
12
14
  };
package/napi-inl.h CHANGED
@@ -9,8 +9,11 @@
9
9
  ////////////////////////////////////////////////////////////////////////////////
10
10
 
11
11
  // Note: Do not include this file directly! Include "napi.h" instead.
12
+ // This should be a no-op and is intended for better IDE integration.
13
+ #include "napi.h"
12
14
 
13
15
  #include <algorithm>
16
+ #include <cstdarg>
14
17
  #include <cstring>
15
18
  #if NAPI_HAS_THREADS
16
19
  #include <mutex>
@@ -32,7 +35,9 @@ namespace details {
32
35
  constexpr int napi_no_external_buffers_allowed = 22;
33
36
 
34
37
  template <typename FreeType>
35
- 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*/) {
36
41
  delete static_cast<FreeType*>(data);
37
42
  }
38
43
 
@@ -41,7 +46,7 @@ inline void default_finalizer(napi_env /*env*/, void* data, void* /*hint*/) {
41
46
  // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
42
47
  // available on all supported versions of Node.js.
43
48
  template <typename FreeType,
44
- napi_finalize finalizer = default_finalizer<FreeType>>
49
+ node_api_nogc_finalize finalizer = default_basic_finalizer<FreeType>>
45
50
  inline napi_status AttachData(napi_env env,
46
51
  napi_value obj,
47
52
  FreeType* data,
@@ -154,7 +159,10 @@ napi_value TemplatedCallback(napi_env env,
154
159
  napi_callback_info info) NAPI_NOEXCEPT {
155
160
  return details::WrapCallback([&] {
156
161
  CallbackInfo cbInfo(env, info);
157
- return Callback(cbInfo);
162
+ // MSVC requires to copy 'Callback' function pointer to a local variable
163
+ // before invoking it.
164
+ auto callback = Callback;
165
+ return callback(cbInfo);
158
166
  });
159
167
  }
160
168
 
@@ -182,23 +190,102 @@ napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info)
182
190
 
183
191
  template <typename T, typename Finalizer, typename Hint = void>
184
192
  struct FinalizeData {
185
- 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,
186
199
  void* data,
187
200
  void* finalizeHint) NAPI_NOEXCEPT {
188
201
  WrapVoidCallback([&] {
189
202
  FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
190
- finalizeData->callback(Env(env), static_cast<T*>(data));
203
+ finalizeData->callback(env, static_cast<T*>(data));
191
204
  delete finalizeData;
192
205
  });
193
206
  }
194
207
 
195
- static inline void WrapperWithHint(napi_env env,
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,
214
+ void* data,
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,
196
234
  void* data,
197
235
  void* finalizeHint) NAPI_NOEXCEPT {
198
236
  WrapVoidCallback([&] {
199
237
  FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
200
- finalizeData->callback(
201
- Env(env), static_cast<T*>(data), finalizeData->hint);
238
+ finalizeData->callback(env, static_cast<T*>(data), finalizeData->hint);
239
+ delete finalizeData;
240
+ });
241
+ }
242
+
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,
249
+ void* data,
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 {
266
+ WrapVoidCallback([&] {
267
+ FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
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);
202
289
  delete finalizeData;
203
290
  });
204
291
  }
@@ -337,6 +424,46 @@ struct AccessorCallbackData {
337
424
  void* data;
338
425
  };
339
426
 
427
+ // Debugging-purpose C++-style variant of sprintf().
428
+ inline std::string StringFormat(const char* format, ...) {
429
+ std::string result;
430
+ va_list args;
431
+ va_start(args, format);
432
+ int len = vsnprintf(nullptr, 0, format, args);
433
+ result.resize(len);
434
+ vsnprintf(&result[0], len + 1, format, args);
435
+ va_end(args);
436
+ return result;
437
+ }
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
+
340
467
  } // namespace details
341
468
 
342
469
  #ifndef NODE_ADDON_API_DISABLE_DEPRECATED
@@ -446,15 +573,21 @@ inline Maybe<T> Just(const T& t) {
446
573
  }
447
574
 
448
575
  ////////////////////////////////////////////////////////////////////////////////
449
- // Env class
576
+ // BasicEnv / Env class
450
577
  ////////////////////////////////////////////////////////////////////////////////
451
578
 
452
- inline Env::Env(napi_env env) : _env(env) {}
579
+ inline BasicEnv::BasicEnv(node_api_nogc_env env) : _env(env) {}
453
580
 
454
- inline Env::operator napi_env() const {
581
+ inline BasicEnv::operator node_api_nogc_env() const {
455
582
  return _env;
456
583
  }
457
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
+
458
591
  inline Object Env::Global() const {
459
592
  napi_value value;
460
593
  napi_status status = napi_get_global(*this, &value);
@@ -478,7 +611,7 @@ inline Value Env::Null() const {
478
611
 
479
612
  inline bool Env::IsExceptionPending() const {
480
613
  bool result;
481
- napi_status status = napi_is_exception_pending(_env, &result);
614
+ napi_status status = napi_is_exception_pending(*this, &result);
482
615
  if (status != napi_ok)
483
616
  result = false; // Checking for a pending exception shouldn't throw.
484
617
  return result;
@@ -486,16 +619,16 @@ inline bool Env::IsExceptionPending() const {
486
619
 
487
620
  inline Error Env::GetAndClearPendingException() const {
488
621
  napi_value value;
489
- napi_status status = napi_get_and_clear_last_exception(_env, &value);
622
+ napi_status status = napi_get_and_clear_last_exception(*this, &value);
490
623
  if (status != napi_ok) {
491
624
  // Don't throw another exception when failing to get the exception!
492
625
  return Error();
493
626
  }
494
- return Error(_env, value);
627
+ return Error(*this, value);
495
628
  }
496
629
 
497
630
  inline MaybeOrValue<Value> Env::RunScript(const char* utf8script) const {
498
- String script = String::New(_env, utf8script);
631
+ String script = String::New(*this, utf8script);
499
632
  return RunScript(script);
500
633
  }
501
634
 
@@ -505,46 +638,46 @@ inline MaybeOrValue<Value> Env::RunScript(const std::string& utf8script) const {
505
638
 
506
639
  inline MaybeOrValue<Value> Env::RunScript(String script) const {
507
640
  napi_value result;
508
- napi_status status = napi_run_script(_env, script, &result);
641
+ napi_status status = napi_run_script(*this, script, &result);
509
642
  NAPI_RETURN_OR_THROW_IF_FAILED(
510
- _env, status, Napi::Value(_env, result), Napi::Value);
643
+ *this, status, Napi::Value(*this, result), Napi::Value);
511
644
  }
512
645
 
513
646
  #if NAPI_VERSION > 2
514
647
  template <typename Hook, typename Arg>
515
- void Env::CleanupHook<Hook, Arg>::Wrapper(void* data) NAPI_NOEXCEPT {
516
- auto* cleanupData =
517
- static_cast<typename Napi::Env::CleanupHook<Hook, Arg>::CleanupData*>(
518
- 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);
519
651
  cleanupData->hook();
520
652
  delete cleanupData;
521
653
  }
522
654
 
523
655
  template <typename Hook, typename Arg>
524
- void Env::CleanupHook<Hook, Arg>::WrapperWithArg(void* data) NAPI_NOEXCEPT {
525
- auto* cleanupData =
526
- static_cast<typename Napi::Env::CleanupHook<Hook, Arg>::CleanupData*>(
527
- 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);
528
660
  cleanupData->hook(static_cast<Arg*>(cleanupData->arg));
529
661
  delete cleanupData;
530
662
  }
531
663
  #endif // NAPI_VERSION > 2
532
664
 
533
665
  #if NAPI_VERSION > 5
534
- template <typename T, Env::Finalizer<T> fini>
535
- inline void Env::SetInstanceData(T* data) const {
666
+ template <typename T, BasicEnv::Finalizer<T> fini>
667
+ inline void BasicEnv::SetInstanceData(T* data) const {
536
668
  napi_status status = napi_set_instance_data(
537
669
  _env,
538
670
  data,
539
671
  [](napi_env env, void* data, void*) { fini(env, static_cast<T*>(data)); },
540
672
  nullptr);
541
- NAPI_THROW_IF_FAILED_VOID(_env, status);
673
+ NAPI_FATAL_IF_FAILED(
674
+ status, "BasicEnv::SetInstanceData", "invalid arguments");
542
675
  }
543
676
 
544
677
  template <typename DataType,
545
678
  typename HintType,
546
- Napi::Env::FinalizerWithHint<DataType, HintType> fini>
547
- 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 {
548
681
  napi_status status = napi_set_instance_data(
549
682
  _env,
550
683
  data,
@@ -552,35 +685,38 @@ inline void Env::SetInstanceData(DataType* data, HintType* hint) const {
552
685
  fini(env, static_cast<DataType*>(data), static_cast<HintType*>(hint));
553
686
  },
554
687
  hint);
555
- NAPI_THROW_IF_FAILED_VOID(_env, status);
688
+ NAPI_FATAL_IF_FAILED(
689
+ status, "BasicEnv::SetInstanceData", "invalid arguments");
556
690
  }
557
691
 
558
692
  template <typename T>
559
- inline T* Env::GetInstanceData() const {
693
+ inline T* BasicEnv::GetInstanceData() const {
560
694
  void* data = nullptr;
561
695
 
562
696
  napi_status status = napi_get_instance_data(_env, &data);
563
- NAPI_THROW_IF_FAILED(_env, status, nullptr);
697
+ NAPI_FATAL_IF_FAILED(
698
+ status, "BasicEnv::GetInstanceData", "invalid arguments");
564
699
 
565
700
  return static_cast<T*>(data);
566
701
  }
567
702
 
568
703
  template <typename T>
569
- void Env::DefaultFini(Env, T* data) {
704
+ void BasicEnv::DefaultFini(Env, T* data) {
570
705
  delete data;
571
706
  }
572
707
 
573
708
  template <typename DataType, typename HintType>
574
- void Env::DefaultFiniWithHint(Env, DataType* data, HintType*) {
709
+ void BasicEnv::DefaultFiniWithHint(Env, DataType* data, HintType*) {
575
710
  delete data;
576
711
  }
577
712
  #endif // NAPI_VERSION > 5
578
713
 
579
714
  #if NAPI_VERSION > 8
580
- inline const char* Env::GetModuleFileName() const {
715
+ inline const char* BasicEnv::GetModuleFileName() const {
581
716
  const char* result;
582
717
  napi_status status = node_api_get_module_file_name(_env, &result);
583
- NAPI_THROW_IF_FAILED(*this, status, nullptr);
718
+ NAPI_FATAL_IF_FAILED(
719
+ status, "BasicEnv::GetModuleFileName", "invalid arguments");
584
720
  return result;
585
721
  }
586
722
  #endif // NAPI_VERSION > 8
@@ -805,8 +941,7 @@ inline void Boolean::CheckCast(napi_env env, napi_value value) {
805
941
  napi_valuetype type;
806
942
  napi_status status = napi_typeof(env, value, &type);
807
943
  NAPI_CHECK(status == napi_ok, "Boolean::CheckCast", "napi_typeof failed");
808
- NAPI_CHECK(
809
- type == napi_boolean, "Boolean::CheckCast", "value is not napi_boolean");
944
+ NAPI_INTERNAL_CHECK_EQ(type, napi_boolean, "%d", "Boolean::CheckCast");
810
945
  }
811
946
 
812
947
  inline Boolean::Boolean() : Napi::Value() {}
@@ -842,8 +977,7 @@ inline void Number::CheckCast(napi_env env, napi_value value) {
842
977
  napi_valuetype type;
843
978
  napi_status status = napi_typeof(env, value, &type);
844
979
  NAPI_CHECK(status == napi_ok, "Number::CheckCast", "napi_typeof failed");
845
- NAPI_CHECK(
846
- type == napi_number, "Number::CheckCast", "value is not napi_number");
980
+ NAPI_INTERNAL_CHECK_EQ(type, napi_number, "%d", "Number::CheckCast");
847
981
  }
848
982
 
849
983
  inline Number::Number() : Value() {}
@@ -938,8 +1072,7 @@ inline void BigInt::CheckCast(napi_env env, napi_value value) {
938
1072
  napi_valuetype type;
939
1073
  napi_status status = napi_typeof(env, value, &type);
940
1074
  NAPI_CHECK(status == napi_ok, "BigInt::CheckCast", "napi_typeof failed");
941
- NAPI_CHECK(
942
- type == napi_bigint, "BigInt::CheckCast", "value is not napi_bigint");
1075
+ NAPI_INTERNAL_CHECK_EQ(type, napi_bigint, "%d", "BigInt::CheckCast");
943
1076
  }
944
1077
 
945
1078
  inline BigInt::BigInt() : Value() {}
@@ -1025,9 +1158,10 @@ inline void Name::CheckCast(napi_env env, napi_value value) {
1025
1158
  napi_valuetype type;
1026
1159
  napi_status status = napi_typeof(env, value, &type);
1027
1160
  NAPI_CHECK(status == napi_ok, "Name::CheckCast", "napi_typeof failed");
1028
- NAPI_CHECK(type == napi_string || type == napi_symbol,
1029
- "Name::CheckCast",
1030
- "value is not napi_string or napi_symbol");
1161
+ NAPI_INTERNAL_CHECK(type == napi_string || type == napi_symbol,
1162
+ "Name::CheckCast",
1163
+ "value is not napi_string or napi_symbol, got %d.",
1164
+ type);
1031
1165
  }
1032
1166
 
1033
1167
  inline Name::Name() : Value() {}
@@ -1094,8 +1228,7 @@ inline void String::CheckCast(napi_env env, napi_value value) {
1094
1228
  napi_valuetype type;
1095
1229
  napi_status status = napi_typeof(env, value, &type);
1096
1230
  NAPI_CHECK(status == napi_ok, "String::CheckCast", "napi_typeof failed");
1097
- NAPI_CHECK(
1098
- type == napi_string, "String::CheckCast", "value is not napi_string");
1231
+ NAPI_INTERNAL_CHECK_EQ(type, napi_string, "%d", "String::CheckCast");
1099
1232
  }
1100
1233
 
1101
1234
  inline String::String() : Name() {}
@@ -1231,8 +1364,7 @@ inline void Symbol::CheckCast(napi_env env, napi_value value) {
1231
1364
  napi_valuetype type;
1232
1365
  napi_status status = napi_typeof(env, value, &type);
1233
1366
  NAPI_CHECK(status == napi_ok, "Symbol::CheckCast", "napi_typeof failed");
1234
- NAPI_CHECK(
1235
- type == napi_symbol, "Symbol::CheckCast", "value is not napi_symbol");
1367
+ NAPI_INTERNAL_CHECK_EQ(type, napi_symbol, "%d", "Symbol::CheckCast");
1236
1368
  }
1237
1369
 
1238
1370
  inline Symbol::Symbol() : Name() {}
@@ -1403,8 +1535,7 @@ inline void Object::CheckCast(napi_env env, napi_value value) {
1403
1535
  napi_valuetype type;
1404
1536
  napi_status status = napi_typeof(env, value, &type);
1405
1537
  NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed");
1406
- NAPI_CHECK(
1407
- type == napi_object, "Object::CheckCast", "value is not napi_object");
1538
+ NAPI_INTERNAL_CHECK_EQ(type, napi_object, "%d", "Object::CheckCast");
1408
1539
  }
1409
1540
 
1410
1541
  inline Object::Object() : TypeTaggable() {}
@@ -1814,9 +1945,7 @@ inline void External<T>::CheckCast(napi_env env, napi_value value) {
1814
1945
  napi_valuetype type;
1815
1946
  napi_status status = napi_typeof(env, value, &type);
1816
1947
  NAPI_CHECK(status == napi_ok, "External::CheckCast", "napi_typeof failed");
1817
- NAPI_CHECK(type == napi_external,
1818
- "External::CheckCast",
1819
- "value is not napi_external");
1948
+ NAPI_INTERNAL_CHECK_EQ(type, napi_external, "%d", "External::CheckCast");
1820
1949
  }
1821
1950
 
1822
1951
  template <typename T>
@@ -2270,12 +2399,13 @@ inline void TypedArrayOf<T>::CheckCast(napi_env env, napi_value value) {
2270
2399
  "TypedArrayOf::CheckCast",
2271
2400
  "napi_is_typedarray failed");
2272
2401
 
2273
- NAPI_CHECK(
2402
+ NAPI_INTERNAL_CHECK(
2274
2403
  (type == TypedArrayTypeForPrimitiveType<T>() ||
2275
2404
  (type == napi_uint8_clamped_array && std::is_same<T, uint8_t>::value)),
2276
2405
  "TypedArrayOf::CheckCast",
2277
- "Array type must match the template parameter. (Uint8 arrays may "
2278
- "optionally have the \"clamped\" array type.)");
2406
+ "Array type must match the template parameter, (Uint8 arrays may "
2407
+ "optionally have the \"clamped\" array type.), got %d.",
2408
+ type);
2279
2409
  }
2280
2410
 
2281
2411
  template <typename T>
@@ -2456,9 +2586,7 @@ inline void Function::CheckCast(napi_env env, napi_value value) {
2456
2586
  napi_valuetype type;
2457
2587
  napi_status status = napi_typeof(env, value, &type);
2458
2588
  NAPI_CHECK(status == napi_ok, "Function::CheckCast", "napi_typeof failed");
2459
- NAPI_CHECK(type == napi_function,
2460
- "Function::CheckCast",
2461
- "value is not napi_function");
2589
+ NAPI_INTERNAL_CHECK_EQ(type, napi_function, "%d", "Function::CheckCast");
2462
2590
  }
2463
2591
 
2464
2592
  inline Function::Function() : Object() {}
@@ -2731,7 +2859,7 @@ inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2731
2859
  #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2732
2860
  // If we can't create an external buffer, we'll just copy the data.
2733
2861
  Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2734
- details::FinalizeData<T, Finalizer>::Wrapper(env, data, finalizeData);
2862
+ details::FinalizeData<T, Finalizer>::WrapperGC(env, data, finalizeData);
2735
2863
  return ret;
2736
2864
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2737
2865
  }
@@ -2766,7 +2894,7 @@ inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2766
2894
  #endif
2767
2895
  // If we can't create an external buffer, we'll just copy the data.
2768
2896
  Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2769
- details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint(
2897
+ details::FinalizeData<T, Finalizer, Hint>::WrapperGCWithHint(
2770
2898
  env, data, finalizeData);
2771
2899
  return ret;
2772
2900
  #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
@@ -3054,7 +3182,12 @@ inline void Error::ThrowAsJavaScriptException() const {
3054
3182
 
3055
3183
  status = napi_throw(_env, Value());
3056
3184
 
3057
- if (status == napi_pending_exception) {
3185
+ #ifdef NAPI_EXPERIMENTAL
3186
+ napi_status expected_failure_mode = napi_cannot_run_js;
3187
+ #else
3188
+ napi_status expected_failure_mode = napi_pending_exception;
3189
+ #endif
3190
+ if (status == expected_failure_mode) {
3058
3191
  // The environment must be terminating as we checked earlier and there
3059
3192
  // was no pending exception. In this case continuing will result
3060
3193
  // in a fatal error and there is nothing the author has done incorrectly
@@ -3191,7 +3324,13 @@ template <typename T>
3191
3324
  inline Reference<T>::~Reference() {
3192
3325
  if (_ref != nullptr) {
3193
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
3194
3332
  napi_delete_reference(_env, _ref);
3333
+ #endif
3195
3334
  }
3196
3335
 
3197
3336
  _ref = nullptr;
@@ -4791,6 +4930,9 @@ inline Value ObjectWrap<T>::OnCalledAsFunction(
4791
4930
  template <typename T>
4792
4931
  inline void ObjectWrap<T>::Finalize(Napi::Env /*env*/) {}
4793
4932
 
4933
+ template <typename T>
4934
+ inline void ObjectWrap<T>::Finalize(BasicEnv /*env*/) {}
4935
+
4794
4936
  template <typename T>
4795
4937
  inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
4796
4938
  napi_env env, napi_callback_info info) {
@@ -4876,10 +5018,59 @@ inline napi_value ObjectWrap<T>::StaticSetterCallbackWrapper(
4876
5018
  }
4877
5019
 
4878
5020
  template <typename T>
4879
- inline void ObjectWrap<T>::FinalizeCallback(napi_env env,
5021
+ inline void ObjectWrap<T>::FinalizeCallback(node_api_nogc_env env,
4880
5022
  void* data,
4881
5023
  void* /*hint*/) {
4882
- 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*/) {
4883
5074
  T* instance = static_cast<T*>(data);
4884
5075
  instance->Finalize(Napi::Env(env));
4885
5076
  delete instance;
@@ -4891,7 +5082,10 @@ inline napi_value ObjectWrap<T>::WrappedMethod(
4891
5082
  napi_env env, napi_callback_info info) NAPI_NOEXCEPT {
4892
5083
  return details::WrapCallback([&] {
4893
5084
  const CallbackInfo cbInfo(env, info);
4894
- method(cbInfo, cbInfo[0]);
5085
+ // MSVC requires to copy 'method' function pointer to a local variable
5086
+ // before invoking it.
5087
+ auto m = method;
5088
+ m(cbInfo, cbInfo[0]);
4895
5089
  return nullptr;
4896
5090
  });
4897
5091
  }
@@ -5321,19 +5515,21 @@ TypedThreadSafeFunction<ContextType, DataType, CallJs>::New(
5321
5515
  auto* finalizeData = new details::
5322
5516
  ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>(
5323
5517
  {data, finalizeCallback});
5324
- napi_status status = napi_create_threadsafe_function(
5325
- env,
5326
- nullptr,
5327
- nullptr,
5328
- String::From(env, resourceName),
5329
- maxQueueSize,
5330
- initialThreadCount,
5331
- finalizeData,
5518
+ auto fini =
5332
5519
  details::ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>::
5333
- FinalizeFinalizeWrapperWithDataAndContext,
5334
- context,
5335
- CallJsInternal,
5336
- &tsfn._tsfn);
5520
+ FinalizeFinalizeWrapperWithDataAndContext;
5521
+ napi_status status =
5522
+ napi_create_threadsafe_function(env,
5523
+ nullptr,
5524
+ nullptr,
5525
+ String::From(env, resourceName),
5526
+ maxQueueSize,
5527
+ initialThreadCount,
5528
+ finalizeData,
5529
+ fini,
5530
+ context,
5531
+ CallJsInternal,
5532
+ &tsfn._tsfn);
5337
5533
  if (status != napi_ok) {
5338
5534
  delete finalizeData;
5339
5535
  NAPI_THROW_IF_FAILED(
@@ -5365,19 +5561,21 @@ TypedThreadSafeFunction<ContextType, DataType, CallJs>::New(
5365
5561
  auto* finalizeData = new details::
5366
5562
  ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>(
5367
5563
  {data, finalizeCallback});
5368
- napi_status status = napi_create_threadsafe_function(
5369
- env,
5370
- nullptr,
5371
- resource,
5372
- String::From(env, resourceName),
5373
- maxQueueSize,
5374
- initialThreadCount,
5375
- finalizeData,
5564
+ auto fini =
5376
5565
  details::ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>::
5377
- FinalizeFinalizeWrapperWithDataAndContext,
5378
- context,
5379
- CallJsInternal,
5380
- &tsfn._tsfn);
5566
+ FinalizeFinalizeWrapperWithDataAndContext;
5567
+ napi_status status =
5568
+ napi_create_threadsafe_function(env,
5569
+ nullptr,
5570
+ resource,
5571
+ String::From(env, resourceName),
5572
+ maxQueueSize,
5573
+ initialThreadCount,
5574
+ finalizeData,
5575
+ fini,
5576
+ context,
5577
+ CallJsInternal,
5578
+ &tsfn._tsfn);
5381
5579
  if (status != napi_ok) {
5382
5580
  delete finalizeData;
5383
5581
  NAPI_THROW_IF_FAILED(
@@ -5481,19 +5679,21 @@ TypedThreadSafeFunction<ContextType, DataType, CallJs>::New(
5481
5679
  auto* finalizeData = new details::
5482
5680
  ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>(
5483
5681
  {data, finalizeCallback});
5484
- napi_status status = napi_create_threadsafe_function(
5485
- env,
5486
- callback,
5487
- nullptr,
5488
- String::From(env, resourceName),
5489
- maxQueueSize,
5490
- initialThreadCount,
5491
- finalizeData,
5682
+ auto fini =
5492
5683
  details::ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>::
5493
- FinalizeFinalizeWrapperWithDataAndContext,
5494
- context,
5495
- CallJsInternal,
5496
- &tsfn._tsfn);
5684
+ FinalizeFinalizeWrapperWithDataAndContext;
5685
+ napi_status status =
5686
+ napi_create_threadsafe_function(env,
5687
+ callback,
5688
+ nullptr,
5689
+ String::From(env, resourceName),
5690
+ maxQueueSize,
5691
+ initialThreadCount,
5692
+ finalizeData,
5693
+ fini,
5694
+ context,
5695
+ CallJsInternal,
5696
+ &tsfn._tsfn);
5497
5697
  if (status != napi_ok) {
5498
5698
  delete finalizeData;
5499
5699
  NAPI_THROW_IF_FAILED(
@@ -5527,6 +5727,9 @@ TypedThreadSafeFunction<ContextType, DataType, CallJs>::New(
5527
5727
  auto* finalizeData = new details::
5528
5728
  ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>(
5529
5729
  {data, finalizeCallback});
5730
+ auto fini =
5731
+ details::ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>::
5732
+ FinalizeFinalizeWrapperWithDataAndContext;
5530
5733
  napi_status status = napi_create_threadsafe_function(
5531
5734
  env,
5532
5735
  details::DefaultCallbackWrapper<
@@ -5538,8 +5741,7 @@ TypedThreadSafeFunction<ContextType, DataType, CallJs>::New(
5538
5741
  maxQueueSize,
5539
5742
  initialThreadCount,
5540
5743
  finalizeData,
5541
- details::ThreadSafeFinalize<ContextType, Finalizer, FinalizerDataType>::
5542
- FinalizeFinalizeWrapperWithDataAndContext,
5744
+ fini,
5543
5745
  context,
5544
5746
  CallJsInternal,
5545
5747
  &tsfn._tsfn);
@@ -6548,12 +6750,12 @@ inline Napi::Object Addon<T>::DefineProperties(
6548
6750
 
6549
6751
  #if NAPI_VERSION > 2
6550
6752
  template <typename Hook, typename Arg>
6551
- Env::CleanupHook<Hook, Arg> Env::AddCleanupHook(Hook hook, Arg* arg) {
6753
+ Env::CleanupHook<Hook, Arg> BasicEnv::AddCleanupHook(Hook hook, Arg* arg) {
6552
6754
  return CleanupHook<Hook, Arg>(*this, hook, arg);
6553
6755
  }
6554
6756
 
6555
6757
  template <typename Hook>
6556
- Env::CleanupHook<Hook> Env::AddCleanupHook(Hook hook) {
6758
+ Env::CleanupHook<Hook> BasicEnv::AddCleanupHook(Hook hook) {
6557
6759
  return CleanupHook<Hook>(*this, hook);
6558
6760
  }
6559
6761
 
@@ -6563,7 +6765,7 @@ Env::CleanupHook<Hook, Arg>::CleanupHook() {
6563
6765
  }
6564
6766
 
6565
6767
  template <typename Hook, typename Arg>
6566
- Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook)
6768
+ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::BasicEnv env, Hook hook)
6567
6769
  : wrapper(Env::CleanupHook<Hook, Arg>::Wrapper) {
6568
6770
  data = new CleanupData{std::move(hook), nullptr};
6569
6771
  napi_status status = napi_add_env_cleanup_hook(env, wrapper, data);
@@ -6574,7 +6776,9 @@ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook)
6574
6776
  }
6575
6777
 
6576
6778
  template <typename Hook, typename Arg>
6577
- 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)
6578
6782
  : wrapper(Env::CleanupHook<Hook, Arg>::WrapperWithArg) {
6579
6783
  data = new CleanupData{std::move(hook), arg};
6580
6784
  napi_status status = napi_add_env_cleanup_hook(env, wrapper, data);
@@ -6585,7 +6789,7 @@ Env::CleanupHook<Hook, Arg>::CleanupHook(Napi::Env env, Hook hook, Arg* arg)
6585
6789
  }
6586
6790
 
6587
6791
  template <class Hook, class Arg>
6588
- bool Env::CleanupHook<Hook, Arg>::Remove(Env env) {
6792
+ bool Env::CleanupHook<Hook, Arg>::Remove(BasicEnv env) {
6589
6793
  napi_status status = napi_remove_env_cleanup_hook(env, wrapper, data);
6590
6794
  delete data;
6591
6795
  data = nullptr;
@@ -6598,6 +6802,65 @@ bool Env::CleanupHook<Hook, Arg>::IsEmpty() const {
6598
6802
  }
6599
6803
  #endif // NAPI_VERSION > 2
6600
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
+
6601
6864
  #ifdef NAPI_CPP_CUSTOM_NAMESPACE
6602
6865
  } // namespace NAPI_CPP_CUSTOM_NAMESPACE
6603
6866
  #endif
package/napi.h CHANGED
@@ -142,6 +142,26 @@ static_assert(sizeof(char16_t) == sizeof(wchar_t),
142
142
  } \
143
143
  } while (0)
144
144
 
145
+ // Internal check helper. Be careful that the formatted message length should be
146
+ // max 255 size and null terminated.
147
+ #define NAPI_INTERNAL_CHECK(expr, location, ...) \
148
+ do { \
149
+ if (!(expr)) { \
150
+ std::string msg = Napi::details::StringFormat(__VA_ARGS__); \
151
+ Napi::Error::Fatal(location, msg.c_str()); \
152
+ } \
153
+ } while (0)
154
+
155
+ #define NAPI_INTERNAL_CHECK_EQ(actual, expected, value_format, location) \
156
+ do { \
157
+ auto actual_value = (actual); \
158
+ NAPI_INTERNAL_CHECK(actual_value == (expected), \
159
+ location, \
160
+ "Expected " #actual " to be equal to " #expected \
161
+ ", but got " value_format ".", \
162
+ actual_value); \
163
+ } while (0)
164
+
145
165
  #define NAPI_FATAL_IF_FAILED(status, location, message) \
146
166
  NAPI_CHECK((status) == napi_ok, location, message)
147
167
 
@@ -292,9 +312,9 @@ using MaybeOrValue = T;
292
312
  ///
293
313
  /// In the V8 JavaScript engine, a Node-API environment approximately
294
314
  /// corresponds to an Isolate.
295
- class Env {
315
+ class BasicEnv {
296
316
  private:
297
- napi_env _env;
317
+ node_api_nogc_env _env;
298
318
  #if NAPI_VERSION > 5
299
319
  template <typename T>
300
320
  static void DefaultFini(Env, T* data);
@@ -302,20 +322,21 @@ class Env {
302
322
  static void DefaultFiniWithHint(Env, DataType* data, HintType* hint);
303
323
  #endif // NAPI_VERSION > 5
304
324
  public:
305
- Env(napi_env env);
306
-
307
- operator napi_env() const;
308
-
309
- Object Global() const;
310
- Value Undefined() const;
311
- Value Null() const;
312
-
313
- bool IsExceptionPending() const;
314
- Error GetAndClearPendingException() const;
315
-
316
- MaybeOrValue<Value> RunScript(const char* utf8script) const;
317
- MaybeOrValue<Value> RunScript(const std::string& utf8script) const;
318
- 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
+ };
319
340
 
320
341
  #if NAPI_VERSION > 2
321
342
  template <typename Hook, typename Arg = void>
@@ -334,7 +355,7 @@ class Env {
334
355
 
335
356
  template <typename T>
336
357
  using Finalizer = void (*)(Env, T*);
337
- template <typename T, Finalizer<T> fini = Env::DefaultFini<T>>
358
+ template <typename T, Finalizer<T> fini = BasicEnv::DefaultFini<T>>
338
359
  void SetInstanceData(T* data) const;
339
360
 
340
361
  template <typename DataType, typename HintType>
@@ -342,7 +363,7 @@ class Env {
342
363
  template <typename DataType,
343
364
  typename HintType,
344
365
  FinalizerWithHint<DataType, HintType> fini =
345
- Env::DefaultFiniWithHint<DataType, HintType>>
366
+ BasicEnv::DefaultFiniWithHint<DataType, HintType>>
346
367
  void SetInstanceData(DataType* data, HintType* hint) const;
347
368
  #endif // NAPI_VERSION > 5
348
369
 
@@ -351,9 +372,9 @@ class Env {
351
372
  class CleanupHook {
352
373
  public:
353
374
  CleanupHook();
354
- CleanupHook(Env env, Hook hook, Arg* arg);
355
- CleanupHook(Env env, Hook hook);
356
- bool Remove(Env env);
375
+ CleanupHook(BasicEnv env, Hook hook, Arg* arg);
376
+ CleanupHook(BasicEnv env, Hook hook);
377
+ bool Remove(BasicEnv env);
357
378
  bool IsEmpty() const;
358
379
 
359
380
  private:
@@ -371,6 +392,39 @@ class Env {
371
392
  #if NAPI_VERSION > 8
372
393
  const char* GetModuleFileName() const;
373
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;
374
428
  };
375
429
 
376
430
  /// A JavaScript value of unknown type.
@@ -1268,7 +1322,7 @@ class TypedArrayOf : public TypedArray {
1268
1322
  napi_typedarray_type type =
1269
1323
  TypedArray::TypedArrayTypeForPrimitiveType<T>()
1270
1324
  #else
1271
- napi_typedarray_type type
1325
+ napi_typedarray_type type
1272
1326
  #endif
1273
1327
  ///< Type of array, if different from the default array type for the
1274
1328
  ///< template parameter T.
@@ -1291,7 +1345,7 @@ class TypedArrayOf : public TypedArray {
1291
1345
  napi_typedarray_type type =
1292
1346
  TypedArray::TypedArrayTypeForPrimitiveType<T>()
1293
1347
  #else
1294
- napi_typedarray_type type
1348
+ napi_typedarray_type type
1295
1349
  #endif
1296
1350
  ///< Type of array, if different from the default array type for the
1297
1351
  ///< template parameter T.
@@ -1381,8 +1435,8 @@ class DataView : public Object {
1381
1435
  template <typename T>
1382
1436
  void WriteData(size_t byteOffset, T value) const;
1383
1437
 
1384
- void* _data;
1385
- size_t _length;
1438
+ void* _data{};
1439
+ size_t _length{};
1386
1440
  };
1387
1441
 
1388
1442
  class Function : public Object {
@@ -1715,7 +1769,7 @@ FunctionReference Persistent(Function value);
1715
1769
  ///
1716
1770
  /// Following C++ statements will not be executed. The exception will bubble
1717
1771
  /// up as a C++ exception of type `Napi::Error`, until it is either caught
1718
- /// while still in C++, or else automatically propataged as a JavaScript
1772
+ /// while still in C++, or else automatically propagated as a JavaScript
1719
1773
  /// exception when the callback returns to JavaScript.
1720
1774
  ///
1721
1775
  /// #### Example 2A - Propagating a Node-API C++ exception:
@@ -1888,7 +1942,7 @@ class CallbackInfo {
1888
1942
  napi_value _this;
1889
1943
  size_t _argc;
1890
1944
  napi_value* _argv;
1891
- napi_value _staticArgs[6];
1945
+ napi_value _staticArgs[6]{};
1892
1946
  napi_value* _dynamicArgs;
1893
1947
  void* _data;
1894
1948
  };
@@ -2395,6 +2449,7 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
2395
2449
  napi_property_attributes attributes = napi_default);
2396
2450
  static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo);
2397
2451
  virtual void Finalize(Napi::Env env);
2452
+ virtual void Finalize(BasicEnv env);
2398
2453
 
2399
2454
  private:
2400
2455
  using This = ObjectWrap<T>;
@@ -2409,7 +2464,10 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
2409
2464
  napi_callback_info info);
2410
2465
  static napi_value StaticSetterCallbackWrapper(napi_env env,
2411
2466
  napi_callback_info info);
2412
- 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
+
2413
2471
  static Function DefineClass(Napi::Env env,
2414
2472
  const char* utf8name,
2415
2473
  const size_t props_count,
package/package.json CHANGED
@@ -374,7 +374,6 @@
374
374
  {
375
375
  "name": "Vladimir Morozov",
376
376
  "url": "https://github.com/vmoroz"
377
-
378
377
  },
379
378
  {
380
379
  "name": "WenheLI",
@@ -431,7 +430,8 @@
431
430
  "fs-extra": "^11.1.1",
432
431
  "path": "^0.12.7",
433
432
  "pre-commit": "^1.2.2",
434
- "safe-buffer": "^5.1.1"
433
+ "safe-buffer": "^5.1.1",
434
+ "semver": "^7.6.0"
435
435
  },
436
436
  "directories": {},
437
437
  "gypfile": false,
@@ -463,6 +463,9 @@
463
463
  "scripts": {
464
464
  "prebenchmark": "node-gyp rebuild -C benchmark",
465
465
  "benchmark": "node benchmark",
466
+ "create-coverage": "npm test --coverage",
467
+ "report-coverage-html": "rm -rf coverage-html && mkdir coverage-html && gcovr -e test --merge-mode-functions merge-use-line-max --html-nested ./coverage-html/index.html test",
468
+ "report-coverage-xml": "rm -rf coverage-xml && mkdir coverage-xml && gcovr -e test --merge-mode-functions merge-use-line-max --xml -o ./coverage-xml/coverage-cxx.xml test",
466
469
  "pretest": "node-gyp rebuild -C test",
467
470
  "test": "node test",
468
471
  "test:debug": "node-gyp rebuild -C test --debug && NODE_API_BUILD_CONFIG=Debug node ./test/index.js",
@@ -475,7 +478,7 @@
475
478
  "lint:fix": "node tools/clang-format --fix && node tools/eslint-format --fix"
476
479
  },
477
480
  "pre-commit": "lint",
478
- "version": "8.0.0",
481
+ "version": "8.2.0",
479
482
  "support": true,
480
483
  "engines": {
481
484
  "node": "^18 || ^20 || >= 21"
@@ -12,7 +12,7 @@ if (!dir) {
12
12
  process.exit(1);
13
13
  }
14
14
 
15
- const NodeApiVersion = require('../package.json').version;
15
+ const NodeApiVersion = require('../').version;
16
16
 
17
17
  const disable = args[1];
18
18
  let ConfigFileOperations;