Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
| `test_error` | Not ported | Medium |
| `test_exception` | Not ported | Medium |
| `test_finalizer` | Not ported | Medium |
| `test_function` | Not ported | Medium |
| `test_function` | Ported ✅ | Medium |
| `test_general` | Not ported | Hard |
| `test_handle_scope` | Ported ✅ | Easy |
| `test_instance_data` | Not ported | Medium |
Expand Down
1 change: 1 addition & 0 deletions tests/js-native-api/test_function/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_node_api_cts_addon(test_function test_function.c)
47 changes: 47 additions & 0 deletions tests/js-native-api/test_function/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use strict";
// Flags: --expose-gc

// Testing api calls for function
const test_function = loadAddon("test_function");

function func1() {
return 1;
}
assert.strictEqual(test_function.TestCall(func1), 1);

function func2() {
console.log("hello world!");
return null;
}
assert.strictEqual(test_function.TestCall(func2), null);

function func3(input) {
return input + 1;
}
assert.strictEqual(test_function.TestCall(func3, 1), 2);

function func4(input) {
return func3(input);
}
assert.strictEqual(test_function.TestCall(func4, 1), 2);

assert.strictEqual(test_function.TestName.name, "Name");
assert.strictEqual(test_function.TestNameShort.name, "Name_");

let tracked_function = test_function.MakeTrackedFunction(mustCall());
assert(!!tracked_function);
tracked_function = null;
let gcCount = 0;
await gcUntil("test", () => gcCount++ > 0);

assert.deepStrictEqual(test_function.TestCreateFunctionParameters(), {
envIsNull: "Invalid argument",
nameIsNull: "napi_ok",
cbIsNull: "Invalid argument",
resultIsNull: "Invalid argument",
});

assert.throws(() => test_function.TestBadReturnExceptionPending(), {
code: "throwing exception",
name: "Error",
});
204 changes: 204 additions & 0 deletions tests/js-native-api/test_function/test_function.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"

static napi_value TestCreateFunctionParameters(napi_env env,
napi_callback_info info) {
napi_status status;
napi_value result, return_value;

NODE_API_CALL(env, napi_create_object(env, &return_value));

status = napi_create_function(NULL,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
&result);

add_returned_status(env,
"envIsNull",
return_value,
"Invalid argument",
napi_invalid_arg,
status);

napi_create_function(env,
NULL,
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
&result);

add_last_status(env, "nameIsNull", return_value);

napi_create_function(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
NULL,
NULL,
&result);

add_last_status(env, "cbIsNull", return_value);

napi_create_function(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
NULL);

add_last_status(env, "resultIsNull", return_value);

return return_value;
}

static napi_value TestCallFunction(napi_env env, napi_callback_info info) {
size_t argc = 10;
napi_value args[10];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

NODE_API_ASSERT(env, argc > 0, "Wrong number of arguments");

napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));

NODE_API_ASSERT(env, valuetype0 == napi_function,
"Wrong type of arguments. Expects a function as first argument.");

napi_value* argv = args + 1;
argc = argc - 1;

napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));

napi_value result;
NODE_API_CALL(env, napi_call_function(env, global, args[0], argc, argv, &result));

return result;
}

static napi_value TestFunctionName(napi_env env, napi_callback_info info) {
return NULL;
}

static void finalize_function(napi_env env, void* data, void* hint) {
napi_ref ref = data;

// Retrieve the JavaScript undefined value.
napi_value undefined;
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));

// Retrieve the JavaScript function we must call.
napi_value js_function;
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, ref, &js_function));

// Call the JavaScript function to indicate that the generated JavaScript
// function is about to be gc-ed.
NODE_API_CALL_RETURN_VOID(env,
napi_call_function(env, undefined, js_function, 0, NULL, NULL));

// Destroy the persistent reference to the function we just called so as to
// properly clean up.
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, ref));
}

static napi_value MakeTrackedFunction(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value js_finalize_cb;
napi_valuetype arg_type;

// Retrieve and validate from the arguments the function we will use to
// indicate to JavaScript that the function we are about to create is about to
// be gc-ed.
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, &js_finalize_cb, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
NODE_API_CALL(env, napi_typeof(env, js_finalize_cb, &arg_type));
NODE_API_ASSERT(env, arg_type == napi_function, "Argument must be a function");

// Dynamically create a function.
napi_value result;
NODE_API_CALL(env,
napi_create_function(
env, "TrackedFunction", NAPI_AUTO_LENGTH, TestFunctionName, NULL,
&result));

// Create a strong reference to the function we will call when the tracked
// function is about to be gc-ed.
napi_ref js_finalize_cb_ref;
NODE_API_CALL(env,
napi_create_reference(env, js_finalize_cb, 1, &js_finalize_cb_ref));

// Attach a finalizer to the dynamically created function and pass it the
// strong reference we created in the previous step.
NODE_API_CALL(env,
napi_wrap(
env, result, js_finalize_cb_ref, finalize_function, NULL, NULL));

return result;
}

static napi_value TestBadReturnExceptionPending(napi_env env, napi_callback_info info) {
napi_throw_error(env, "throwing exception", "throwing exception");

// addons should only ever return a valid napi_value even if an
// exception occurs, but we have seen that the C++ wrapper
// with exceptions enabled sometimes returns an invalid value
// when an exception is thrown. Test that we ignore the return
// value then an exception is pending. We use 0xFFFFFFFF as a value
// that should never be a valid napi_value and node seems to
// crash if it is not ignored indicating that it is indeed invalid.
return (napi_value)(0xFFFFFFFFF);
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_value fn1;
NODE_API_CALL(env, napi_create_function(
env, NULL, NAPI_AUTO_LENGTH, TestCallFunction, NULL, &fn1));

napi_value fn2;
NODE_API_CALL(env, napi_create_function(
env, "Name", NAPI_AUTO_LENGTH, TestFunctionName, NULL, &fn2));

napi_value fn3;
NODE_API_CALL(env, napi_create_function(
env, "Name_extra", 5, TestFunctionName, NULL, &fn3));

napi_value fn4;
NODE_API_CALL(env,
napi_create_function(
env, "MakeTrackedFunction", NAPI_AUTO_LENGTH, MakeTrackedFunction,
NULL, &fn4));

napi_value fn5;
NODE_API_CALL(env,
napi_create_function(
env, "TestCreateFunctionParameters", NAPI_AUTO_LENGTH,
TestCreateFunctionParameters, NULL, &fn5));

napi_value fn6;
NODE_API_CALL(env,
napi_create_function(
env, "TestBadReturnExceptionPending", NAPI_AUTO_LENGTH,
TestBadReturnExceptionPending, NULL, &fn6));

NODE_API_CALL(env, napi_set_named_property(env, exports, "TestCall", fn1));
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestName", fn2));
NODE_API_CALL(env,
napi_set_named_property(env, exports, "TestNameShort", fn3));
NODE_API_CALL(env,
napi_set_named_property(env, exports, "MakeTrackedFunction", fn4));

NODE_API_CALL(env,
napi_set_named_property(
env, exports, "TestCreateFunctionParameters", fn5));

NODE_API_CALL(env,
napi_set_named_property(
env, exports, "TestBadReturnExceptionPending", fn6));

return exports;
}
EXTERN_C_END