node-addon-api 2.0.1 → 2.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # node-addon-api Changelog
2
2
 
3
+ ## 2020-07-01 Version 2.0.2, @NickNaso
4
+
5
+ ### Notable changes:
6
+
7
+ #### API
8
+
9
+ - `Napi::ObjectWrap`: avoid double-free on old Node.js.
10
+ - `Napi::ObjectWrap`: remove wrap only on failure.
11
+ - `Napi::ObjectWrap`: gracefully handle constructor exceptions
12
+ - `Napi::ObjectWrap`: call `napi_remove_wrap()` in destructor.
13
+
14
+ #### TEST
15
+
16
+ - Updated `Napi::BigInt` test for recent change in core.
17
+
18
+ ### Commmits
19
+
20
+ * [[`5abf60257d`](https://github.com/nodejs/node-addon-api/commit/5abf60257d)] - Merge pull request #723 from gabrielschulhof/backport-4e885069-pr-475 (Nicola Del Gobbo)
21
+ * [[`470f130666`](https://github.com/nodejs/node-addon-api/commit/470f130666)] - **objectwrap**: avoid double-free on old Node.js (Gabriel Schulhof)
22
+ * [[`81e2eac7ba`](https://github.com/nodejs/node-addon-api/commit/81e2eac7ba)] - **test**: update BigInt test for recent change in core (Michael Dawson) [#649](https://github.com/nodejs/node-addon-api/pull/649)
23
+ * [[`204f07252c`](https://github.com/nodejs/node-addon-api/commit/204f07252c)] - **objectwrap**: remove wrap only on failure (Gabriel Schulhof)
24
+ * [[`a552a384dd`](https://github.com/nodejs/node-addon-api/commit/a552a384dd)] - **src**: call `napi\_remove\_wrap()` in `ObjectWrap` dtor (Anna Henningsen) [#475](https://github.com/nodejs/node-addon-api/pull/475)
25
+ * [[`1a51067438`](https://github.com/nodejs/node-addon-api/commit/1a51067438)] - **objectwrap**: gracefully handle constructor exceptions (Gabriel Schulhof)
26
+
3
27
  ## 2020-06-02 Version 2.0.1, @NickNaso
4
28
 
5
29
  ### Notable changes:
package/README.md CHANGED
@@ -46,7 +46,7 @@ to ideas specified in the **ECMA262 Language Specification**.
46
46
  - **[Contributors](#contributors)**
47
47
  - **[License](#license)**
48
48
 
49
- ## **Current version: 2.0.1**
49
+ ## **Current version: 2.0.2**
50
50
 
51
51
  (See [CHANGELOG.md](CHANGELOG.md) for complete Changelog)
52
52
 
package/napi-inl.h CHANGED
@@ -10,6 +10,7 @@
10
10
  // Note: Do not include this file directly! Include "napi.h" instead.
11
11
 
12
12
  #include <algorithm>
13
+ #include <atomic>
13
14
  #include <cstring>
14
15
  #include <mutex>
15
16
  #include <type_traits>
@@ -19,6 +20,8 @@ namespace Napi {
19
20
  // Helpers to handle functions exposed from C++.
20
21
  namespace details {
21
22
 
23
+ extern std::atomic_bool needs_objectwrap_destructor_fix;
24
+
22
25
  // Attach a data item to an object and delete it when the object gets
23
26
  // garbage-collected.
24
27
  // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
@@ -251,11 +254,16 @@ struct AccessorCallbackData {
251
254
  // Module registration
252
255
  ////////////////////////////////////////////////////////////////////////////////
253
256
 
254
- #define NODE_API_MODULE(modname, regfunc) \
255
- napi_value __napi_ ## regfunc(napi_env env, \
256
- napi_value exports) { \
257
- return Napi::RegisterModule(env, exports, regfunc); \
258
- } \
257
+ #define NODE_API_MODULE(modname, regfunc) \
258
+ namespace Napi { \
259
+ namespace details { \
260
+ std::atomic_bool needs_objectwrap_destructor_fix(false); \
261
+ } \
262
+ } \
263
+ napi_value __napi_ ## regfunc(napi_env env, \
264
+ napi_value exports) { \
265
+ return Napi::RegisterModule(env, exports, regfunc); \
266
+ } \
259
267
  NAPI_MODULE(modname, __napi_ ## regfunc)
260
268
 
261
269
  // Adapt the NAPI_MODULE registration function:
@@ -264,6 +272,12 @@ struct AccessorCallbackData {
264
272
  inline napi_value RegisterModule(napi_env env,
265
273
  napi_value exports,
266
274
  ModuleRegisterCallback registerCallback) {
275
+ const napi_node_version* nver = Napi::VersionManagement::GetNodeVersion(env);
276
+ Napi::details::needs_objectwrap_destructor_fix =
277
+ (nver->major < 10 ||
278
+ (nver->major == 10 && nver->minor < 15) ||
279
+ (nver->major == 10 && nver->minor == 15 && nver->patch < 3));
280
+
267
281
  return details::WrapCallback([&] {
268
282
  return napi_value(registerCallback(Napi::Env(env),
269
283
  Napi::Object(env, exports)));
@@ -2968,16 +2982,36 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
2968
2982
  napi_value wrapper = callbackInfo.This();
2969
2983
  napi_status status;
2970
2984
  napi_ref ref;
2971
- T* instance = static_cast<T*>(this);
2972
- status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref);
2985
+ status = napi_wrap(env, wrapper, this, FinalizeCallback, nullptr, &ref);
2973
2986
  NAPI_THROW_IF_FAILED_VOID(env, status);
2974
2987
 
2975
- Reference<Object>* instanceRef = instance;
2988
+ Reference<Object>* instanceRef = this;
2976
2989
  *instanceRef = Reference<Object>(env, ref);
2977
2990
  }
2978
2991
 
2979
- template<typename T>
2980
- inline ObjectWrap<T>::~ObjectWrap() {}
2992
+ template <typename T>
2993
+ inline ObjectWrap<T>::~ObjectWrap() {
2994
+ // If the JS object still exists at this point, remove the finalizer added
2995
+ // through `napi_wrap()`.
2996
+ if (!IsEmpty()) {
2997
+ Object object = Value();
2998
+ // It is not valid to call `napi_remove_wrap()` with an empty `object`.
2999
+ // This happens e.g. during garbage collection.
3000
+ if (!object.IsEmpty() && _construction_failed) {
3001
+ napi_remove_wrap(Env(), object, nullptr);
3002
+
3003
+ if (Napi::details::needs_objectwrap_destructor_fix) {
3004
+ // If construction failed we delete the reference via
3005
+ // `napi_remove_wrap()`, not via `napi_delete_reference()` in the
3006
+ // `Reference<Object>` destructor. This will prevent the
3007
+ // `Reference<Object>` destructor from doing a double delete of this
3008
+ // reference.
3009
+ _ref = nullptr;
3010
+ _env = nullptr;
3011
+ }
3012
+ }
3013
+ }
3014
+ }
2981
3015
 
2982
3016
  template<typename T>
2983
3017
  inline T* ObjectWrap<T>::Unwrap(Object wrapper) {
@@ -3369,10 +3403,21 @@ inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
3369
3403
  return nullptr;
3370
3404
  }
3371
3405
 
3372
- T* instance;
3373
3406
  napi_value wrapper = details::WrapCallback([&] {
3374
3407
  CallbackInfo callbackInfo(env, info);
3375
- instance = new T(callbackInfo);
3408
+ T* instance = new T(callbackInfo);
3409
+ #ifdef NAPI_CPP_EXCEPTIONS
3410
+ instance->_construction_failed = false;
3411
+ #else
3412
+ if (callbackInfo.Env().IsExceptionPending()) {
3413
+ // We need to clear the exception so that removing the wrap might work.
3414
+ Error e = callbackInfo.Env().GetAndClearPendingException();
3415
+ delete instance;
3416
+ e.ThrowAsJavaScriptException();
3417
+ } else {
3418
+ instance->_construction_failed = false;
3419
+ }
3420
+ # endif // NAPI_CPP_EXCEPTIONS
3376
3421
  return callbackInfo.This();
3377
3422
  });
3378
3423
 
@@ -3497,7 +3542,7 @@ inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper(
3497
3542
 
3498
3543
  template <typename T>
3499
3544
  inline void ObjectWrap<T>::FinalizeCallback(napi_env env, void* data, void* /*hint*/) {
3500
- T* instance = reinterpret_cast<T*>(data);
3545
+ ObjectWrap<T>* instance = static_cast<ObjectWrap<T>*>(data);
3501
3546
  instance->Finalize(Napi::Env(env));
3502
3547
  delete instance;
3503
3548
  }
package/napi.h CHANGED
@@ -1735,6 +1735,8 @@ namespace Napi {
1735
1735
  StaticAccessorCallbackData;
1736
1736
  typedef AccessorCallbackData<InstanceGetterCallback, InstanceSetterCallback>
1737
1737
  InstanceAccessorCallbackData;
1738
+
1739
+ bool _construction_failed = true;
1738
1740
  };
1739
1741
 
1740
1742
  class HandleScope {
package/package.json CHANGED
@@ -240,5 +240,5 @@
240
240
  "dev:incremental": "node test",
241
241
  "doc": "doxygen doc/Doxyfile"
242
242
  },
243
- "version": "2.0.1"
243
+ "version": "2.0.2"
244
244
  }