From 3b364033335c354d877f85b43d2652db6e1c88d4 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 a4b1d426b..0d5d3c5e9 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 @@ -1587,6 +1618,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, @@ -1594,12 +1642,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); } @@ -2527,14 +2578,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, @@ -2548,22 +2603,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, @@ -2577,24 +2638,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, @@ -2608,24 +2680,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, @@ -2639,74 +2723,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, @@ -2777,20 +2863,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 @@ -2799,47 +2932,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; } @@ -2884,7 +3032,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 }); @@ -2923,7 +3070,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}); @@ -2941,7 +3087,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(); @@ -2958,7 +3103,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}); @@ -2976,7 +3120,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(); @@ -2994,7 +3137,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 2e3753091..1326e965d 100644 --- a/napi.h +++ b/napi.h @@ -1299,66 +1299,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); @@ -1453,11 +1473,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); @@ -1529,6 +1551,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 c2bd101f3..e374ac124 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -28,6 +28,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)); @@ -56,6 +57,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 7bff35e8a..36870dbc2 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -31,6 +31,7 @@ 'objectwrap.cc', 'objectreference.cc', 'version_management.cc' + 'thunking_manual.cc', ], 'conditions': [ ['NAPI_VERSION!=""', { 'defines': ['NAPI_VERSION=<@(NAPI_VERSION)'] } ] diff --git a/test/object/object.cc b/test/object/object.cc index 713ed5827..f74f6abbd 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -60,13 +60,13 @@ void DefineProperties(const CallbackInfo& info) { if (nameType.Utf8Value() == "literal") { obj.DefineProperties({ - PropertyDescriptor::Accessor("readonlyAccessor", TestGetter), - PropertyDescriptor::Accessor("readwriteAccessor", TestGetter, TestSetter), + PropertyDescriptor::Accessor(info.Env(), obj, "readonlyAccessor", TestGetter), + PropertyDescriptor::Accessor(info.Env(), obj, "readwriteAccessor", TestGetter, TestSetter), PropertyDescriptor::Value("readonlyValue", trueValue), PropertyDescriptor::Value("readwriteValue", trueValue, napi_writable), PropertyDescriptor::Value("enumerableValue", trueValue, napi_enumerable), PropertyDescriptor::Value("configurableValue", trueValue, napi_configurable), - PropertyDescriptor::Function("function", TestFunction), + PropertyDescriptor::Function(info.Env(), "function", TestFunction), }); } else if (nameType.Utf8Value() == "string") { // VS2013 has lifetime issues when passing temporary objects into the constructor of another @@ -82,19 +82,19 @@ void DefineProperties(const CallbackInfo& info) { std::string str7("function"); obj.DefineProperties({ - PropertyDescriptor::Accessor(str1, TestGetter), - PropertyDescriptor::Accessor(str2, TestGetter, TestSetter), + PropertyDescriptor::Accessor(info.Env(), obj, str1, TestGetter), + PropertyDescriptor::Accessor(info.Env(), obj, str2, TestGetter, TestSetter), PropertyDescriptor::Value(str3, trueValue), PropertyDescriptor::Value(str4, trueValue, napi_writable), PropertyDescriptor::Value(str5, trueValue, napi_enumerable), PropertyDescriptor::Value(str6, trueValue, napi_configurable), - PropertyDescriptor::Function(str7, TestFunction), + PropertyDescriptor::Function(info.Env(), str7, TestFunction), }); } else if (nameType.Utf8Value() == "value") { obj.DefineProperties({ - PropertyDescriptor::Accessor( + PropertyDescriptor::Accessor(info.Env(), obj, Napi::String::New(info.Env(), "readonlyAccessor"), TestGetter), - PropertyDescriptor::Accessor( + PropertyDescriptor::Accessor(info.Env(), obj, Napi::String::New(info.Env(), "readwriteAccessor"), TestGetter, TestSetter), PropertyDescriptor::Value( Napi::String::New(info.Env(), "readonlyValue"), trueValue), @@ -104,7 +104,7 @@ void DefineProperties(const CallbackInfo& info) { Napi::String::New(info.Env(), "enumerableValue"), trueValue, napi_enumerable), PropertyDescriptor::Value( Napi::String::New(info.Env(), "configurableValue"), trueValue, napi_configurable), - PropertyDescriptor::Function( + PropertyDescriptor::Function(info.Env(), Napi::String::New(info.Env(), "function"), TestFunction), }); } diff --git a/test/thunking_manual.cc b/test/thunking_manual.cc new file mode 100644 index 000000000..00201d38c --- /dev/null +++ b/test/thunking_manual.cc @@ -0,0 +1,106 @@ +#include + +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"); +}