diff --git a/napi-inl.h b/napi-inl.h index 88a6cd4a7..b591812ab 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -4630,9 +4630,11 @@ inline napi_value InstanceWrap::WrappedMethod( //////////////////////////////////////////////////////////////////////////////// template -inline ObjectWrap::ObjectWrap(const Napi::CallbackInfo& callbackInfo) { +template +inline ObjectWrap::ObjectWrap(const Napi::CallbackInfo& callbackInfo, + ConstructorTraitTag) { napi_env env = callbackInfo.Env(); - napi_value wrapper = callbackInfo.This(); + napi_value wrapper = ConstructorTraitTag::GetThis(callbackInfo); napi_status status; napi_ref ref; T* instance = static_cast(this); @@ -5026,11 +5028,12 @@ inline napi_value ObjectWrap::ConstructorCallbackWrapper( Error e = callbackInfo.Env().GetAndClearPendingException(); delete instance; e.ThrowAsJavaScriptException(); + return Object(); } else { instance->_construction_failed = false; } #endif // NODE_ADDON_API_CPP_EXCEPTIONS - return callbackInfo.This(); + return instance->Value(); }); return wrapper; diff --git a/napi.h b/napi.h index bd604c6fd..d68e8c8ea 100644 --- a/napi.h +++ b/napi.h @@ -2359,6 +2359,10 @@ class InstanceWrap { } }; +struct ConstructorTraitTag { + static napi_value GetThis(const CallbackInfo& info) { return info.This(); } +}; + /// Base class to be extended by C++ classes exposed to JavaScript; each C++ /// class instance gets "wrapped" by a JavaScript object that is managed by this /// class. @@ -2389,7 +2393,9 @@ class InstanceWrap { template class ObjectWrap : public InstanceWrap, public Reference { public: - ObjectWrap(const CallbackInfo& callbackInfo); + template + ObjectWrap(const CallbackInfo& callbackInfo, + ConstructorTraitTag = ConstructorTraitTag()); virtual ~ObjectWrap(); static T* Unwrap(Object wrapper); diff --git a/test/binding.cc b/test/binding.cc index 9e5aaaaa6..06fcf7b82 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -69,6 +69,7 @@ Object InitTypedArray(Env env); Object InitGlobalObject(Env env); Object InitObjectWrap(Env env); Object InitObjectWrapConstructorException(Env env); +Object InitObjectWrapConstructorTraitTag(Env env); Object InitObjectWrapFunction(Env env); Object InitObjectWrapRemoveWrap(Env env); Object InitObjectWrapMultipleInheritance(Env env); @@ -168,6 +169,8 @@ Object Init(Env env, Object exports) { exports.Set("objectwrap", InitObjectWrap(env)); exports.Set("objectwrapConstructorException", InitObjectWrapConstructorException(env)); + exports.Set("objectwrap_constructor_trait_tag", + InitObjectWrapConstructorTraitTag(env)); exports.Set("objectwrap_function", InitObjectWrapFunction(env)); exports.Set("objectwrap_removewrap", InitObjectWrapRemoveWrap(env)); exports.Set("objectwrap_multiple_inheritance", diff --git a/test/binding.gyp b/test/binding.gyp index 8ee391fb9..bde31c689 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -73,6 +73,7 @@ 'typedarray.cc', 'objectwrap.cc', 'objectwrap_constructor_exception.cc', + 'objectwrap_constructor_trait_tag.cc', 'objectwrap_function.cc', 'objectwrap_removewrap.cc', 'objectwrap_multiple_inheritance.cc', diff --git a/test/objectwrap_constructor_trait_tag.cc b/test/objectwrap_constructor_trait_tag.cc new file mode 100644 index 000000000..c2f305ada --- /dev/null +++ b/test/objectwrap_constructor_trait_tag.cc @@ -0,0 +1,39 @@ +#include +#include "test_helper.h" + +struct EventTargetConstructorTraitTag { + static napi_value GetThis(const Napi::CallbackInfo& info) { + Napi::Function eventTargetCons = + MaybeUnwrap(info.Env().Global().Get("EventTarget")) + .As(); + Napi::Object eventTarget = MaybeUnwrap(eventTargetCons.New({})); + return eventTarget; + } +}; + +class TestOverride : public Napi::ObjectWrap { + public: + TestOverride(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info, EventTargetConstructorTraitTag()) { + } + + Napi::Value IsTestOverride(const Napi::CallbackInfo& info) { + TestOverride* self = Unwrap(info.This().As()); + return Napi::Boolean::New(info.Env(), self != nullptr); + } + + static void Initialize(Napi::Env env, Napi::Object exports) { + Napi::Function cons = DefineClass( + env, + "TestOverride", + {InstanceAccessor<&TestOverride::IsTestOverride>("isTestOverride")}); + + exports.Set("TestOverride", cons); + } +}; + +Napi::Object InitObjectWrapConstructorTraitTag(Napi::Env env) { + Napi::Object exports = Napi::Object::New(env); + TestOverride::Initialize(env, exports); + return exports; +} diff --git a/test/objectwrap_constructor_trait_tag.js b/test/objectwrap_constructor_trait_tag.js new file mode 100644 index 000000000..2d3faa29f --- /dev/null +++ b/test/objectwrap_constructor_trait_tag.js @@ -0,0 +1,25 @@ +'use strict'; + +const assert = require('assert'); + +const test = bindingName => { + const binding = require(bindingName); + const TestOverride = binding.objectwrap_constructor_trait_tag.TestOverride; + // TODO(legendecas): Expose Object.setPrototypeOf in node-api. + Object.setPrototypeOf(TestOverride, EventTarget); + Object.setPrototypeOf(TestOverride.prototype, EventTarget.prototype); + + const test = new TestOverride(); + Object.setPrototypeOf(test, TestOverride.prototype); + + // Verify that the TestOverride class is an EventTarget instance and has both + // EventTarget brand and TestOverride brand. + assert(test instanceof EventTarget); + assert(test instanceof TestOverride); + // Verify TestOverride brand. + assert.strictEqual(test.isTestOverride, true); + // Verify EventTarget brand. + test.addEventListener('test', () => {}); +}; + +module.exports = require('./common').runTestWithBindingPath(test);