Skip to content

Commit

Permalink
Rename NonConstructor to OnCalledAsFunction and add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
p120ph37 committed Feb 5, 2022
1 parent b337aea commit 124d582
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 75 deletions.
15 changes: 15 additions & 0 deletions doc/object_wrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,21 @@ property of the `Napi::CallbackInfo`.

Returns a `Napi::Function` representing the constructor function for the class.

### OnCalledAsFunction

The default behavior when the constructor is called from JavaScript as a
function (without the `new` keyword) is to throw a `Napi::TypeError` with the
message `Class constructors cannot be invoked without 'new'`.

This default behavior can be altered by defining this static method.

```cpp
static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo);
```
- `[in] callbackInfo`: The object representing the components of the JavaScript
request being made.
### Finalize
Provides an opportunity to run cleanup code that requires access to the
Expand Down
7 changes: 4 additions & 3 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4452,9 +4452,10 @@ inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticValue(Symbol name,
}

template <typename T>
inline Value ObjectWrap<T>::NonConstructor(const Napi::CallbackInfo& info) {
inline Value ObjectWrap<T>::OnCalledAsFunction(
const Napi::CallbackInfo& callbackInfo) {
NAPI_THROW(
TypeError::New(info.Env(),
TypeError::New(callbackInfo.Env(),
"Class constructors cannot be invoked without 'new'"),
Napi::Value());
}
Expand All @@ -4473,7 +4474,7 @@ inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
bool isConstructCall = (new_target != nullptr);
if (!isConstructCall) {
return details::WrapCallback(
[&] { return T::NonConstructor(CallbackInfo(env, info)); });
[&] { return T::OnCalledAsFunction(CallbackInfo(env, info)); });
}

napi_value wrapper = details::WrapCallback([&] {
Expand Down
3 changes: 2 additions & 1 deletion napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2199,7 +2199,8 @@ namespace Napi {
static PropertyDescriptor StaticValue(Symbol name,
Napi::Value value,
napi_property_attributes attributes = napi_default);
static Napi::Value NonConstructor(const Napi::CallbackInfo& info);
static Napi::Value OnCalledAsFunction(
const Napi::CallbackInfo& callbackInfo);
virtual void Finalize(Napi::Env env);

private:
Expand Down
4 changes: 2 additions & 2 deletions test/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Object InitTypedArray(Env env);
Object InitGlobalObject(Env env);
Object InitObjectWrap(Env env);
Object InitObjectWrapConstructorException(Env env);
Object InitObjectWrapNonConstructor(Env env);
Object InitObjectWrapFunction(Env env);
Object InitObjectWrapRemoveWrap(Env env);
Object InitObjectWrapMultipleInheritance(Env env);
Object InitObjectReference(Env env);
Expand Down Expand Up @@ -153,7 +153,7 @@ Object Init(Env env, Object exports) {
exports.Set("objectwrap", InitObjectWrap(env));
exports.Set("objectwrapConstructorException",
InitObjectWrapConstructorException(env));
exports.Set("objectwrap_nonconstructor", InitObjectWrapNonConstructor(env));
exports.Set("objectwrap_function", InitObjectWrapFunction(env));
exports.Set("objectwrap_removewrap", InitObjectWrapRemoveWrap(env));
exports.Set("objectwrap_multiple_inheritance", InitObjectWrapMultipleInheritance(env));
exports.Set("objectreference", InitObjectReference(env));
Expand Down
2 changes: 1 addition & 1 deletion test/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
'typedarray.cc',
'objectwrap.cc',
'objectwrap_constructor_exception.cc',
'objectwrap_nonconstructor.cc',
'objectwrap_function.cc',
'objectwrap_removewrap.cc',
'objectwrap_multiple_inheritance.cc',
'object_reference.cc',
Expand Down
45 changes: 45 additions & 0 deletions test/objectwrap_function.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <napi.h>
#include <unordered_map>
#include "test_helper.h"

class FunctionTest : public Napi::ObjectWrap<FunctionTest> {
public:
FunctionTest(const Napi::CallbackInfo& info)
: Napi::ObjectWrap<FunctionTest>(info) {}

static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& info) {
// If called with a "true" argument, throw an exeption to test the handling.
if (!info[0].IsUndefined() && MaybeUnwrap(info[0].ToBoolean())) {
NAPI_THROW(Napi::Error::New(info.Env(), "an exception"), Napi::Value());
}
// Otherwise, act as a factory.
std::vector<napi_value> args;
for (size_t i = 0; i < info.Length(); i++) args.push_back(info[i]);
return MaybeUnwrap(GetConstructor(info.Env()).New(args));
}

// Constructor-per-env map in a static member because env.SetInstanceData()
// would interfere with Napi::Addon<T>
static std::unordered_map<napi_env, Napi::FunctionReference> constructors;

static void Initialize(Napi::Env env, Napi::Object exports) {
const char* name = "FunctionTest";
Napi::Function func = DefineClass(env, name, {});
constructors[env] = Napi::Persistent(func);
env.AddCleanupHook([env] { constructors.erase(env); });
exports.Set(name, func);
}

static Napi::Function GetConstructor(Napi::Env env) {
return constructors[env].Value();
}
};

std::unordered_map<napi_env, Napi::FunctionReference>
FunctionTest::constructors = {};

Napi::Object InitObjectWrapFunction(Napi::Env env) {
Napi::Object exports = Napi::Object::New(env);
FunctionTest::Initialize(env, exports);
return exports;
}
22 changes: 22 additions & 0 deletions test/objectwrap_function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

const assert = require('assert');
const testUtil = require('./testUtil');

function test (binding) {
return testUtil.runGCTests([
'objectwrap function',
() => {
const { FunctionTest } = binding.objectwrap_function;
const newConstructed = new FunctionTest();
const functionConstructed = FunctionTest();
assert(newConstructed instanceof FunctionTest);
assert(functionConstructed instanceof FunctionTest);
assert.throws(() => (FunctionTest(true)), /an exception/);
},
// Do on gc before returning.
() => {}
]);
}

module.exports = require('./common').runTest(test);
46 changes: 0 additions & 46 deletions test/objectwrap_nonconstructor.cc

This file was deleted.

22 changes: 0 additions & 22 deletions test/objectwrap_nonconstructor.js

This file was deleted.

0 comments on commit 124d582

Please sign in to comment.