Skip to content

Commit

Permalink
Add Function::Call Napi::Value override (#1026)
Browse files Browse the repository at this point in the history
* Add Function::Call Napi::Value vector override

* Fix lint issues
  • Loading branch information
rgerd authored Feb 21, 2022
1 parent e906b5a commit c54aeef
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 23 deletions.
26 changes: 26 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit c54aeef

Please sign in to comment.