Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Function::Call Napi::Value override #1026

Merged
merged 2 commits into from Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions napi-inl.h
Expand Up @@ -2287,6 +2287,11 @@ inline MaybeOrValue<Value> Function::Call(
return Call(Env().Undefined(), args);
}

inline MaybeOrValue<Value> Function::Call(
const std::vector<Value>& args) const {
return Call(Env().Undefined(), args);
}

inline MaybeOrValue<Value> Function::Call(size_t argc,
const napi_value* args) const {
return Call(Env().Undefined(), argc, args);
Expand All @@ -2302,6 +2307,27 @@ inline MaybeOrValue<Value> Function::Call(
return Call(recv, args.size(), args.data());
}

inline MaybeOrValue<Value> Function::Call(
napi_value recv, const std::vector<Value>& args) const {
const size_t argc = args.size();
const size_t stackArgsCount = 6;
napi_value stackArgs[stackArgsCount];
std::vector<napi_value> heapArgs;
napi_value* argv;
if (argc <= stackArgsCount) {
argv = stackArgs;
} else {
heapArgs.resize(argc);
argv = heapArgs.data();
}

for (size_t index = 0; index < argc; index++) {
argv[index] = static_cast<napi_value>(args[index]);
}

return Call(recv, argc, argv);
}

inline MaybeOrValue<Value> Function::Call(napi_value recv,
size_t argc,
const napi_value* args) const {
Expand Down
3 changes: 3 additions & 0 deletions napi.h
Expand Up @@ -1350,11 +1350,14 @@ namespace Napi {
MaybeOrValue<Value> Call(
const std::initializer_list<napi_value>& args) const;
MaybeOrValue<Value> Call(const std::vector<napi_value>& args) const;
MaybeOrValue<Value> Call(const std::vector<Value>& args) const;
MaybeOrValue<Value> Call(size_t argc, const napi_value* args) const;
MaybeOrValue<Value> Call(
napi_value recv, const std::initializer_list<napi_value>& args) const;
MaybeOrValue<Value> Call(napi_value recv,
const std::vector<napi_value>& args) const;
MaybeOrValue<Value> Call(napi_value recv,
const std::vector<Value>& args) const;
MaybeOrValue<Value> Call(napi_value recv,
size_t argc,
const napi_value* args) const;
Expand Down
29 changes: 29 additions & 0 deletions test/function.cc
Expand Up @@ -69,6 +69,16 @@ Value CallWithVector(const CallbackInfo& info) {
return MaybeUnwrap(func.Call(args));
}

Value CallWithVectorUsingCppWrapper(const CallbackInfo& info) {
Function func = info[0].As<Function>();
std::vector<Value> args;
args.reserve(3);
args.push_back(info[1]);
args.push_back(info[2]);
args.push_back(info[3]);
return MaybeUnwrap(func.Call(args));
}

Value CallWithCStyleArray(const CallbackInfo& info) {
Function func = info[0].As<Function>();
std::vector<napi_value> args;
Expand Down Expand Up @@ -108,6 +118,17 @@ Value CallWithReceiverAndVector(const CallbackInfo& info) {
return MaybeUnwrap(func.Call(receiver, args));
}

Value CallWithReceiverAndVectorUsingCppWrapper(const CallbackInfo& info) {
Function func = info[0].As<Function>();
Value receiver = info[1];
std::vector<Value> args;
args.reserve(3);
args.push_back(info[2]);
args.push_back(info[3]);
args.push_back(info[4]);
return MaybeUnwrap(func.Call(receiver, args));
}

Value CallWithInvalidReceiver(const CallbackInfo& info) {
Function func = info[0].As<Function>();
return MaybeUnwrapOr(func.Call(Value(), std::initializer_list<napi_value>{}),
Expand Down Expand Up @@ -213,11 +234,15 @@ Object InitFunction(Env env) {
Function::New(env, ValueCallbackWithData, nullptr, &testData);
exports["callWithArgs"] = Function::New(env, CallWithArgs);
exports["callWithVector"] = Function::New(env, CallWithVector);
exports["callWithVectorUsingCppWrapper"] =
Function::New(env, CallWithVectorUsingCppWrapper);
exports["callWithCStyleArray"] = Function::New(env, CallWithCStyleArray);
exports["callWithReceiverAndCStyleArray"] =
Function::New(env, CallWithReceiverAndCStyleArray);
exports["callWithReceiverAndArgs"] = Function::New(env, CallWithReceiverAndArgs);
exports["callWithReceiverAndVector"] = Function::New(env, CallWithReceiverAndVector);
exports["callWithReceiverAndVectorUsingCppWrapper"] =
Function::New(env, CallWithReceiverAndVectorUsingCppWrapper);
exports["callWithInvalidReceiver"] = Function::New(env, CallWithInvalidReceiver);
exports["callConstructorWithArgs"] = Function::New(env, CallConstructorWithArgs);
exports["callConstructorWithVector"] = Function::New(env, CallConstructorWithVector);
Expand Down Expand Up @@ -246,13 +271,17 @@ Object InitFunction(Env env) {
Function::New<ValueCallbackWithData>(env, nullptr, &testData);
exports["callWithArgs"] = Function::New<CallWithArgs>(env);
exports["callWithVector"] = Function::New<CallWithVector>(env);
exports["callWithVectorUsingCppWrapper"] =
Function::New<CallWithVectorUsingCppWrapper>(env);
exports["callWithCStyleArray"] = Function::New<CallWithCStyleArray>(env);
exports["callWithReceiverAndCStyleArray"] =
Function::New<CallWithReceiverAndCStyleArray>(env);
exports["callWithReceiverAndArgs"] =
Function::New<CallWithReceiverAndArgs>(env);
exports["callWithReceiverAndVector"] =
Function::New<CallWithReceiverAndVector>(env);
exports["callWithReceiverAndVectorUsingCppWrapper"] =
Function::New<CallWithReceiverAndVectorUsingCppWrapper>(env);
exports["callWithInvalidReceiver"] =
Function::New<CallWithInvalidReceiver>(env);
exports["callConstructorWithArgs"] =
Expand Down
58 changes: 35 additions & 23 deletions test/function.js
Expand Up @@ -8,113 +8,125 @@ module.exports = require('./common').runTest(binding => {
testLambda(binding.function.lambda);
});

function test(binding) {
function test (binding) {
assert.strictEqual(binding.emptyConstructor(true), true);
assert.strictEqual(binding.emptyConstructor(false), false);

let obj = {};
assert.deepStrictEqual(binding.voidCallback(obj), undefined);
assert.deepStrictEqual(obj, { "foo": "bar" });
assert.deepStrictEqual(obj, { foo: 'bar' });

assert.deepStrictEqual(binding.valueCallback(), { "foo": "bar" });
assert.deepStrictEqual(binding.valueCallback(), { foo: 'bar' });

let args = null;
let ret = null;
let receiver = null;
function testFunction() {
function testFunction () {
receiver = this;
args = [].slice.call(arguments);
return ret;
}
function testConstructor() {
function testConstructor () {
args = [].slice.call(arguments);
}

function makeCallbackTestFunction(receiver, expectedOne, expectedTwo, expectedThree) {
return function callback(one, two, three) {
function makeCallbackTestFunction (receiver, expectedOne, expectedTwo, expectedThree) {
return function callback (one, two, three) {
assert.strictEqual(this, receiver);
assert.strictEqual(one, expectedOne);
assert.strictEqual(two, expectedTwo);
assert.strictEqual(three, expectedThree);
}
};
}

ret = 4;
assert.strictEqual(binding.callWithArgs(testFunction, 1, 2, 3), 4);
assert.strictEqual(receiver, undefined);
assert.deepStrictEqual(args, [ 1, 2, 3 ]);
assert.deepStrictEqual(args, [1, 2, 3]);

ret = 5;
assert.strictEqual(binding.callWithVector(testFunction, 2, 3, 4), 5);
assert.strictEqual(receiver, undefined);
assert.deepStrictEqual(args, [ 2, 3, 4 ]);
assert.deepStrictEqual(args, [2, 3, 4]);

ret = 5;
assert.strictEqual(binding.callWithVectorUsingCppWrapper(testFunction, 2, 3, 4), 5);
assert.strictEqual(receiver, undefined);
assert.deepStrictEqual(args, [2, 3, 4]);

ret = 6;
assert.strictEqual(binding.callWithReceiverAndArgs(testFunction, obj, 3, 4, 5), 6);
assert.deepStrictEqual(receiver, obj);
assert.deepStrictEqual(args, [ 3, 4, 5 ]);
assert.deepStrictEqual(args, [3, 4, 5]);

ret = 7;
assert.strictEqual(binding.callWithReceiverAndVector(testFunction, obj, 4, 5, 6), 7);
assert.deepStrictEqual(receiver, obj);
assert.deepStrictEqual(args, [ 4, 5, 6 ]);
assert.deepStrictEqual(args, [4, 5, 6]);

ret = 7;
assert.strictEqual(binding.callWithReceiverAndVectorUsingCppWrapper(testFunction, obj, 4, 5, 6), 7);
assert.deepStrictEqual(receiver, obj);
assert.deepStrictEqual(args, [4, 5, 6]);

ret = 8;
assert.strictEqual(binding.callWithCStyleArray(testFunction, 5, 6, 7), ret);
assert.deepStrictEqual(receiver, undefined);
assert.deepStrictEqual(args, [ 5, 6, 7 ]);
assert.deepStrictEqual(args, [5, 6, 7]);

ret = 9;
assert.strictEqual(binding.callWithReceiverAndCStyleArray(testFunction, obj, 6, 7, 8), ret);
assert.deepStrictEqual(receiver, obj);
assert.deepStrictEqual(args, [ 6, 7, 8 ]);
assert.deepStrictEqual(args, [6, 7, 8]);

ret = 10;
assert.strictEqual(binding.callWithFunctionOperator(testFunction, 7, 8, 9), ret);
assert.strictEqual(receiver, undefined);
assert.deepStrictEqual(args, [ 7, 8, 9 ]);
assert.deepStrictEqual(args, [7, 8, 9]);

assert.throws(() => {
binding.callWithInvalidReceiver();
}, /Invalid (pointer passed as )?argument/);

obj = binding.callConstructorWithArgs(testConstructor, 5, 6, 7);
assert(obj instanceof testConstructor);
assert.deepStrictEqual(args, [ 5, 6, 7 ]);
assert.deepStrictEqual(args, [5, 6, 7]);

obj = binding.callConstructorWithVector(testConstructor, 6, 7, 8);
assert(obj instanceof testConstructor);
assert.deepStrictEqual(args, [ 6, 7, 8 ]);
assert.deepStrictEqual(args, [6, 7, 8]);

obj = binding.callConstructorWithCStyleArray(testConstructor, 7, 8, 9);
assert(obj instanceof testConstructor);
assert.deepStrictEqual(args, [ 7, 8, 9 ]);
assert.deepStrictEqual(args, [7, 8, 9]);

obj = {};
assert.deepStrictEqual(binding.voidCallbackWithData(obj), undefined);
assert.deepStrictEqual(obj, { "foo": "bar", "data": 1 });
assert.deepStrictEqual(obj, { foo: 'bar', data: 1 });

assert.deepStrictEqual(binding.valueCallbackWithData(), { "foo": "bar", "data": 1 });
assert.deepStrictEqual(binding.valueCallbackWithData(), { foo: 'bar', data: 1 });

assert.strictEqual(binding.voidCallback.name, 'voidCallback');
assert.strictEqual(binding.valueCallback.name, 'valueCallback');

let testConstructCall = undefined;
let testConstructCall;
binding.isConstructCall((result) => { testConstructCall = result; });
assert.ok(!testConstructCall);
/* eslint-disable no-new, new-cap */
new binding.isConstructCall((result) => { testConstructCall = result; });
/* eslint-enable no-new, new-cap */
assert.ok(testConstructCall);

obj = {};
binding.makeCallbackWithArgs(makeCallbackTestFunction(obj, "1", "2", "3"), obj, "1", "2", "3");
binding.makeCallbackWithArgs(makeCallbackTestFunction(obj, '1', '2', '3'), obj, '1', '2', '3');
binding.makeCallbackWithVector(makeCallbackTestFunction(obj, 4, 5, 6), obj, 4, 5, 6);
binding.makeCallbackWithCStyleArray(makeCallbackTestFunction(obj, 7, 8, 9), obj, 7, 8, 9);
assert.throws(() => {
binding.makeCallbackWithInvalidReceiver(() => {});
});
}

function testLambda(binding) {
function testLambda (binding) {
assert.ok(binding.lambdaWithNoCapture());
assert.ok(binding.lambdaWithCapture());
assert.ok(binding.lambdaWithMoveOnlyCapture());
Expand Down