From 3aac9eb52eb62d1a128662f06d003f90cac65bb3 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Thu, 13 Sep 2018 09:24:48 -0400 Subject: [PATCH] Fix TODOs to fix memory leaks Fixes: https://github.com/nodejs/node-addon-api/issues/333 --- napi-inl.h | 308 +++++++++++++++++++++++++++++----------- napi.h | 55 +++++-- test/binding.cc | 2 + test/binding.gyp | 1 + test/object/object.cc | 18 +-- test/thunking_manual.cc | 106 ++++++++++++++ test/thunking_manual.js | 18 +++ 7 files changed, 402 insertions(+), 106 deletions(-) create mode 100644 test/thunking_manual.cc create mode 100644 test/thunking_manual.js diff --git a/napi-inl.h b/napi-inl.h index c96319843..781239164 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -60,6 +60,37 @@ namespace details { } \ } while (0) +template +static inline napi_status AttachData(napi_env env, + napi_value obj, + FreeType* data) { + napi_value symbol, external; + napi_status status = napi_create_symbol(env, nullptr, &symbol); + if (status == napi_ok) { + status = napi_create_external(env, + data, + [](napi_env /*env*/, void* data, void* /*hint*/) { + delete static_cast(data); + }, + nullptr, + &external); + if (status == napi_ok) { + napi_property_descriptor desc = { + nullptr, + symbol, + nullptr, + nullptr, + nullptr, + external, + napi_default, + nullptr + }; + status = napi_define_properties(env, obj, 1, &desc); + } + } + return status; +} + // For use in JS to C++ callback wrappers to catch any Napi::Error exceptions // and rethrow them as JavaScript exceptions before returning from the callback. template @@ -1583,6 +1614,23 @@ inline const T* TypedArrayOf::Data() const { // Function class //////////////////////////////////////////////////////////////////////////////// +template +static inline napi_status +CreateFunction(napi_env env, + const char* utf8name, + size_t length, + napi_callback cb, + CbData* data, + napi_value* result) { + napi_status status = + napi_create_function(env, utf8name, length, cb, data, result); + if (status == napi_ok) { + status = Napi::details::AttachData(env, *result, data); + } + + return status; +} + template inline Function Function::New(napi_env env, Callable cb, @@ -1590,12 +1638,15 @@ inline Function Function::New(napi_env env, void* data) { typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType; typedef details::CallbackData CbData; - // TODO: Delete when the function is destroyed auto callbackData = new CbData({ cb, data }); napi_value value; - napi_status status = napi_create_function( - env, utf8name, NAPI_AUTO_LENGTH, CbData::Wrapper, callbackData, &value); + napi_status status = CreateFunction(env, + utf8name, + NAPI_AUTO_LENGTH, + CbData::Wrapper, + callbackData, + &value); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, value); } @@ -2503,14 +2554,18 @@ inline void CallbackInfo::SetData(void* data) { template inline PropertyDescriptor -PropertyDescriptor::Accessor(const char* utf8name, +PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + const char* utf8name, Getter getter, napi_property_attributes attributes, void* /*data*/) { typedef details::CallbackData CbData; - // TODO: Delete when the function is destroyed auto callbackData = new CbData({ getter, nullptr }); + napi_status status = Napi::details::AttachData(env, obj, callbackData); + NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); + return PropertyDescriptor({ utf8name, nullptr, @@ -2524,22 +2579,28 @@ PropertyDescriptor::Accessor(const char* utf8name, } template -inline PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name, +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + const std::string& utf8name, Getter getter, napi_property_attributes attributes, void* data) { - return Accessor(utf8name.c_str(), getter, attributes, data); + return Accessor(env, obj, utf8name.c_str(), getter, attributes, data); } template -inline PropertyDescriptor PropertyDescriptor::Accessor(napi_value name, - Getter getter, - napi_property_attributes attributes, - void* /*data*/) { +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + napi_value name, + Getter getter, + napi_property_attributes attributes, + void* /*data*/) { typedef details::CallbackData CbData; - // TODO: Delete when the function is destroyed auto callbackData = new CbData({ getter, nullptr }); + napi_status status = Napi::details::AttachData(env, obj, callbackData); + NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); + return PropertyDescriptor({ nullptr, name, @@ -2553,24 +2614,35 @@ inline PropertyDescriptor PropertyDescriptor::Accessor(napi_value name, } template -inline PropertyDescriptor PropertyDescriptor::Accessor(Name name, - Getter getter, - napi_property_attributes attributes, - void* data) { +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + Name name, + Getter getter, + napi_property_attributes attributes, + void* data) { napi_value nameValue = name; - return PropertyDescriptor::Accessor(nameValue, getter, attributes, data); + return PropertyDescriptor::Accessor(env, + obj, + nameValue, + getter, + attributes, + data); } template -inline PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name, - Getter getter, - Setter setter, - napi_property_attributes attributes, - void* /*data*/) { +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + const char* utf8name, + Getter getter, + Setter setter, + napi_property_attributes attributes, + void* /*data*/) { typedef details::AccessorCallbackData CbData; - // TODO: Delete when the function is destroyed auto callbackData = new CbData({ getter, setter }); + napi_status status = Napi::details::AttachData(env, obj, callbackData); + NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); + return PropertyDescriptor({ utf8name, nullptr, @@ -2584,24 +2656,36 @@ inline PropertyDescriptor PropertyDescriptor::Accessor(const char* utf8name, } template -inline PropertyDescriptor PropertyDescriptor::Accessor(const std::string& utf8name, +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { - return Accessor(utf8name.c_str(), getter, setter, attributes, data); + return Accessor(env, + obj, + utf8name.c_str(), + getter, + setter, + attributes, + data); } template -inline PropertyDescriptor PropertyDescriptor::Accessor(napi_value name, +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + napi_value name, Getter getter, Setter setter, napi_property_attributes attributes, void* /*data*/) { typedef details::AccessorCallbackData CbData; - // TODO: Delete when the function is destroyed auto callbackData = new CbData({ getter, setter }); + napi_status status = Napi::details::AttachData(env, obj, callbackData); + NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); + return PropertyDescriptor({ nullptr, name, @@ -2615,74 +2699,76 @@ inline PropertyDescriptor PropertyDescriptor::Accessor(napi_value name, } template -inline PropertyDescriptor PropertyDescriptor::Accessor(Name name, +inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, + Napi::Object obj, + Name name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { napi_value nameValue = name; - return PropertyDescriptor::Accessor(nameValue, getter, setter, attributes, data); + return PropertyDescriptor::Accessor(env, + obj, + nameValue, + getter, + setter, + attributes, + data); } template -inline PropertyDescriptor PropertyDescriptor::Function(const char* utf8name, +inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, + const char* utf8name, Callable cb, napi_property_attributes attributes, - void* /*data*/) { - typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType; - typedef details::CallbackData CbData; - // TODO: Delete when the function is destroyed - auto callbackData = new CbData({ cb, nullptr }); - + void* data) { return PropertyDescriptor({ utf8name, nullptr, - CbData::Wrapper, nullptr, nullptr, nullptr, + Napi::Function::New(env, cb, utf8name, data), attributes, - callbackData + nullptr }); } template -inline PropertyDescriptor PropertyDescriptor::Function(const std::string& utf8name, +inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, + const std::string& utf8name, Callable cb, napi_property_attributes attributes, void* data) { - return Function(utf8name.c_str(), cb, attributes, data); + return Function(env, utf8name.c_str(), cb, attributes, data); } template -inline PropertyDescriptor PropertyDescriptor::Function(napi_value name, +inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, + napi_value name, Callable cb, napi_property_attributes attributes, - void* /*data*/) { - typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType; - typedef details::CallbackData CbData; - // TODO: Delete when the function is destroyed - auto callbackData = new CbData({ cb, nullptr }); - + void* data) { return PropertyDescriptor({ nullptr, name, - CbData::Wrapper, nullptr, nullptr, nullptr, + Napi::Function::New(env, cb, nullptr, data), attributes, - callbackData + nullptr }); } template -inline PropertyDescriptor PropertyDescriptor::Function(Name name, +inline PropertyDescriptor PropertyDescriptor::Function(Napi::Env env, + Name name, Callable cb, napi_property_attributes attributes, void* data) { napi_value nameValue = name; - return PropertyDescriptor::Function(nameValue, cb, attributes, data); + return PropertyDescriptor::Function(env, nameValue, cb, attributes, data); } inline PropertyDescriptor PropertyDescriptor::Value(const char* utf8name, @@ -2753,20 +2839,67 @@ inline T* ObjectWrap::Unwrap(Object wrapper) { return unwrapped; } +// Attach instance method and accessor data to the `napi_value` holding the +// class constructor. +// TODO: It may be possible to detach instance methods from .prototype. +// Therefore it might be better to retrieve the `napi_value` holding the +// instance method from .prototype and attaching the data there, rather +// than to the class constructor. +template +inline Function ObjectWrap::DefineClass(napi_env env, + const char* utf8name, + size_t count, + const napi_property_descriptor* desc, + void* data) { + napi_value constructor; + napi_status status = napi_define_class(env, + utf8name, + NAPI_AUTO_LENGTH, + T::ConstructorCallbackWrapper, + data, + count, + desc, + &constructor); + NAPI_THROW_IF_FAILED(env, status, Function()); + + for (size_t idx = 0; idx < count; idx++) { + const napi_property_descriptor* prop = &desc[idx]; + + if (prop->getter == T::StaticGetterCallbackWrapper || + prop->setter == T::StaticSetterCallbackWrapper || + prop->getter == T::InstanceGetterCallbackWrapper || + prop->setter == T::InstanceSetterCallbackWrapper) { + status = Napi::details::AttachData(env, + constructor, + static_cast(prop->data)); + NAPI_THROW_IF_FAILED(env, status, Function()); + } else if (prop->method != nullptr && !(prop->attributes & napi_static)) { + if (prop->method == T::InstanceVoidMethodCallbackWrapper) { + status = Napi::details::AttachData(env, + constructor, + static_cast(prop->data)); + } else if (prop->method == T::InstanceMethodCallbackWrapper) { + status = Napi::details::AttachData(env, + constructor, + static_cast(prop->data)); + } + NAPI_THROW_IF_FAILED(env, status, Function()); + } + } + return Function(env, constructor); +} + template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::initializer_list>& properties, void* data) { - napi_value value; - napi_status status = napi_define_class( - env, utf8name, NAPI_AUTO_LENGTH, - T::ConstructorCallbackWrapper, data, properties.size(), - reinterpret_cast(properties.begin()), &value); - NAPI_THROW_IF_FAILED(env, status, Function()); - - return Function(env, value); + return DefineClass(env, + utf8name, + properties.size(), + reinterpret_cast(properties.begin()), + data); } template @@ -2775,47 +2908,62 @@ inline Function ObjectWrap::DefineClass( const char* utf8name, const std::vector>& properties, void* data) { - napi_value value; - napi_status status = napi_define_class( - env, utf8name, NAPI_AUTO_LENGTH, - T::ConstructorCallbackWrapper, data, properties.size(), - reinterpret_cast(properties.data()), &value); - NAPI_THROW_IF_FAILED(env, status, Function()); - - return Function(env, value); + return DefineClass(env, + utf8name, + properties.size(), + reinterpret_cast(properties.data()), + data); } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( + Napi::Env env, const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({ method, data }); + napi_value fn; + napi_status status = CreateFunction(env, + utf8name, + NAPI_AUTO_LENGTH, + T::StaticVoidMethodCallbackWrapper, + callbackData, + &fn); + napi_property_descriptor desc = napi_property_descriptor(); - desc.utf8name = utf8name; - desc.method = T::StaticVoidMethodCallbackWrapper; - desc.data = callbackData; - desc.attributes = static_cast(attributes | napi_static); + if (status == napi_ok) { + desc.utf8name = utf8name; + desc.value = fn; + desc.attributes = static_cast(attributes | napi_static); + } return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( + Napi::Env env, const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({ method, data }); + napi_value fn; + napi_status status = CreateFunction(env, + utf8name, + NAPI_AUTO_LENGTH, + T::StaticMethodCallbackWrapper, + callbackData, + &fn); + napi_property_descriptor desc = napi_property_descriptor(); - desc.utf8name = utf8name; - desc.method = T::StaticMethodCallbackWrapper; - desc.data = callbackData; - desc.attributes = static_cast(attributes | napi_static); + if (status == napi_ok) { + desc.utf8name = utf8name; + desc.value = fn; + desc.attributes = static_cast(attributes | napi_static); + } return desc; } @@ -2860,7 +3008,6 @@ inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( StaticSetterCallback setter, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({ getter, setter, data }); @@ -2899,7 +3046,6 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({ method, data}); @@ -2917,7 +3063,6 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( InstanceMethodCallback method, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); @@ -2934,7 +3079,6 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({ method, data}); @@ -2952,7 +3096,6 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( InstanceMethodCallback method, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({ method, data }); napi_property_descriptor desc = napi_property_descriptor(); @@ -2970,7 +3113,6 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { - // TODO: Delete when the class is destroyed InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({ getter, setter, data }); diff --git a/napi.h b/napi.h index 7b0f17f11..3e9a6b0ab 100644 --- a/napi.h +++ b/napi.h @@ -1287,66 +1287,86 @@ namespace Napi { class PropertyDescriptor { public: template - static PropertyDescriptor Accessor(const char* utf8name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(const std::string& utf8name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(napi_value name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + napi_value name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(Name name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(const char* utf8name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(const std::string& utf8name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(napi_value name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + napi_value name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Accessor(Name name, + static PropertyDescriptor Accessor(Napi::Env env, + Napi::Object destination, + Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Function(const char* utf8name, + static PropertyDescriptor Function(Napi::Env env, + const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Function(const std::string& utf8name, + static PropertyDescriptor Function(Napi::Env env, + const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Function(napi_value name, + static PropertyDescriptor Function(Napi::Env env, + napi_value name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template - static PropertyDescriptor Function(Name name, + static PropertyDescriptor Function(Napi::Env env, + Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); @@ -1441,11 +1461,13 @@ namespace Napi { const char* utf8name, const std::vector& properties, void* data = nullptr); - static PropertyDescriptor StaticMethod(const char* utf8name, + static PropertyDescriptor StaticMethod(Napi::Env env, + const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); - static PropertyDescriptor StaticMethod(const char* utf8name, + static PropertyDescriptor StaticMethod(Napi::Env env, + const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); @@ -1517,6 +1539,11 @@ namespace Napi { static napi_value InstanceGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceSetterCallbackWrapper(napi_env env, napi_callback_info info); static void FinalizeCallback(napi_env env, void* data, void* hint); + static Function DefineClass(napi_env env, + const char* utf8name, + size_t count, + const napi_property_descriptor* desc, + void* data); template struct MethodCallbackData { diff --git a/test/binding.cc b/test/binding.cc index 071b75172..533566063 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -23,6 +23,7 @@ Object InitTypedArray(Env env); Object InitObjectWrap(Env env); Object InitObjectReference(Env env); Object InitVersionManagement(Env env); +Object InitThunkingManual(Env env); Object Init(Env env, Object exports) { exports.Set("arraybuffer", InitArrayBuffer(env)); @@ -47,6 +48,7 @@ Object Init(Env env, Object exports) { exports.Set("objectwrap", InitObjectWrap(env)); exports.Set("objectreference", InitObjectReference(env)); exports.Set("version_management", InitVersionManagement(env)); + exports.Set("thunking_manual", InitThunkingManual(env)); return exports; } diff --git a/test/binding.gyp b/test/binding.gyp index ed9c6899b..e2f5987dd 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -28,6 +28,7 @@ 'objectwrap.cc', 'objectreference.cc', 'version_management.cc' + 'thunking_manual.cc', ], 'include_dirs': [" + +static Napi::Value TestMethod(const Napi::CallbackInfo& /*info*/) { + return Napi::Value(); +} + +static Napi::Value TestGetter(const Napi::CallbackInfo& /*info*/) { + return Napi::Value(); +} + +static void TestSetter(const Napi::CallbackInfo& /*info*/) { +} + +class TestClass : public Napi::ObjectWrap { + public: + TestClass(const Napi::CallbackInfo& info): + ObjectWrap(info) { + } + static Napi::Value TestClassStaticMethod(const Napi::CallbackInfo& info) { + return Napi::Number::New(info.Env(), 42); + } + + static void TestClassStaticVoidMethod(const Napi::CallbackInfo& /*info*/) { + } + + Napi::Value TestClassInstanceMethod(const Napi::CallbackInfo& info) { + return Napi::Number::New(info.Env(), 42); + } + + void TestClassInstanceVoidMethod(const Napi::CallbackInfo& /*info*/) { + } + + Napi::Value TestClassInstanceGetter(const Napi::CallbackInfo& info) { + return Napi::Number::New(info.Env(), 42); + } + + void TestClassInstanceSetter(const Napi::CallbackInfo& /*info*/, + const Napi::Value& /*new_value*/) { + } + + static Napi::Function NewClass(Napi::Env env) { + return DefineClass(env, "TestClass", { + StaticMethod(env, "staticMethod", TestClassStaticMethod), + StaticMethod(env, "staticVoidMethod", TestClassStaticVoidMethod), + InstanceMethod("instanceMethod", &TestClass::TestClassInstanceMethod), + InstanceMethod("instanceVoidMethod", + &TestClass::TestClassInstanceVoidMethod), + InstanceMethod(Napi::Symbol::New(env, "instanceMethod"), + &TestClass::TestClassInstanceMethod), + InstanceMethod(Napi::Symbol::New(env, "instanceVoidMethod"), + &TestClass::TestClassInstanceVoidMethod), + InstanceAccessor("instanceAccessor", + &TestClass::TestClassInstanceGetter, + &TestClass::TestClassInstanceSetter) + }); + } +}; + +static Napi::Value CreateTestObject(const Napi::CallbackInfo& info) { + Napi::Object item = Napi::Object::New(info.Env()); + item["testMethod"] = + Napi::Function::New(info.Env(), TestMethod, "testMethod"); + + item.DefineProperties({ + Napi::PropertyDescriptor::Accessor(info.Env(), + item, + "accessor_1", + TestGetter), + Napi::PropertyDescriptor::Accessor(info.Env(), + item, + std::string("accessor_1_std_string"), + TestGetter), + Napi::PropertyDescriptor::Accessor(info.Env(), + item, + Napi::String::New(info.Env(), + "accessor_1_js_string"), + TestGetter), + Napi::PropertyDescriptor::Accessor(info.Env(), + item, + "accessor_2", + TestGetter, + TestSetter), + Napi::PropertyDescriptor::Accessor(info.Env(), + item, + std::string("accessor_2_std_string"), + TestGetter, + TestSetter), + Napi::PropertyDescriptor::Accessor(info.Env(), + item, + Napi::String::New(info.Env(), + "accessor_2_js_string"), + TestGetter, + TestSetter), + Napi::PropertyDescriptor::Value("TestClass", + TestClass::NewClass(info.Env())), + }); + + return item; +} + +Napi::Object InitThunkingManual(Napi::Env env) { + Napi::Object exports = Napi::Object::New(env); + exports["createTestObject"] = + Napi::Function::New(env, CreateTestObject, "createTestObject"); + return exports; +} diff --git a/test/thunking_manual.js b/test/thunking_manual.js new file mode 100644 index 000000000..ec41a8a43 --- /dev/null +++ b/test/thunking_manual.js @@ -0,0 +1,18 @@ +// Flags: --expose-gc +'use strict'; +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); + +test(require(`./build/${buildType}/binding.node`)); +test(require(`./build/${buildType}/binding_noexcept.node`)); + +function test(binding) { + console.log("Thunking: Performing initial GC"); + global.gc(); + console.log("Thunking: Creating test object"); + let object = binding.thunking_manual.createTestObject(); + object = null; + console.log("Thunking: About to GC\n--------"); + global.gc(); + console.log("--------\nThunking: GC complete"); +}