Skip to content

Commit f7cdb0d

Browse files
committed
port test_function to CTS
ports [test_function](https://github.com/nodejs/node/tree/main/test/js-native-api/test_function) from the Node.js test suite to the CTS. Signed-off-by: Balakrishna Avulapati <ba@bavulapati.com>
1 parent 57dba59 commit f7cdb0d

File tree

4 files changed

+253
-1
lines changed

4 files changed

+253
-1
lines changed

PORTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
5757
| `test_error` | Not ported | Medium |
5858
| `test_exception` | Not ported | Medium |
5959
| `test_finalizer` | Not ported | Medium |
60-
| `test_function` | Not ported | Medium |
60+
| `test_function` | Ported ✅ | Medium |
6161
| `test_general` | Not ported | Hard |
6262
| `test_handle_scope` | Ported ✅ | Easy |
6363
| `test_instance_data` | Not ported | Medium |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_node_api_cts_addon(test_function test_function.c)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"use strict";
2+
// Flags: --expose-gc
3+
4+
// Testing api calls for function
5+
const test_function = loadAddon("test_function");
6+
7+
function func1() {
8+
return 1;
9+
}
10+
assert.strictEqual(test_function.TestCall(func1), 1);
11+
12+
function func2() {
13+
console.log("hello world!");
14+
return null;
15+
}
16+
assert.strictEqual(test_function.TestCall(func2), null);
17+
18+
function func3(input) {
19+
return input + 1;
20+
}
21+
assert.strictEqual(test_function.TestCall(func3, 1), 2);
22+
23+
function func4(input) {
24+
return func3(input);
25+
}
26+
assert.strictEqual(test_function.TestCall(func4, 1), 2);
27+
28+
assert.strictEqual(test_function.TestName.name, "Name");
29+
assert.strictEqual(test_function.TestNameShort.name, "Name_");
30+
31+
let tracked_function = test_function.MakeTrackedFunction(mustCall());
32+
assert(!!tracked_function);
33+
tracked_function = null;
34+
let gcCount = 0;
35+
await gcUntil("test", () => gcCount++ > 0);
36+
37+
assert.deepStrictEqual(test_function.TestCreateFunctionParameters(), {
38+
envIsNull: "Invalid argument",
39+
nameIsNull: "napi_ok",
40+
cbIsNull: "Invalid argument",
41+
resultIsNull: "Invalid argument",
42+
});
43+
44+
assert.throws(() => test_function.TestBadReturnExceptionPending(), {
45+
code: "throwing exception",
46+
name: "Error",
47+
});
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#include <js_native_api.h>
2+
#include "../common.h"
3+
#include "../entry_point.h"
4+
5+
static napi_value TestCreateFunctionParameters(napi_env env,
6+
napi_callback_info info) {
7+
napi_status status;
8+
napi_value result, return_value;
9+
10+
NODE_API_CALL(env, napi_create_object(env, &return_value));
11+
12+
status = napi_create_function(NULL,
13+
"TrackedFunction",
14+
NAPI_AUTO_LENGTH,
15+
TestCreateFunctionParameters,
16+
NULL,
17+
&result);
18+
19+
add_returned_status(env,
20+
"envIsNull",
21+
return_value,
22+
"Invalid argument",
23+
napi_invalid_arg,
24+
status);
25+
26+
napi_create_function(env,
27+
NULL,
28+
NAPI_AUTO_LENGTH,
29+
TestCreateFunctionParameters,
30+
NULL,
31+
&result);
32+
33+
add_last_status(env, "nameIsNull", return_value);
34+
35+
napi_create_function(env,
36+
"TrackedFunction",
37+
NAPI_AUTO_LENGTH,
38+
NULL,
39+
NULL,
40+
&result);
41+
42+
add_last_status(env, "cbIsNull", return_value);
43+
44+
napi_create_function(env,
45+
"TrackedFunction",
46+
NAPI_AUTO_LENGTH,
47+
TestCreateFunctionParameters,
48+
NULL,
49+
NULL);
50+
51+
add_last_status(env, "resultIsNull", return_value);
52+
53+
return return_value;
54+
}
55+
56+
static napi_value TestCallFunction(napi_env env, napi_callback_info info) {
57+
size_t argc = 10;
58+
napi_value args[10];
59+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
60+
61+
NODE_API_ASSERT(env, argc > 0, "Wrong number of arguments");
62+
63+
napi_valuetype valuetype0;
64+
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
65+
66+
NODE_API_ASSERT(env, valuetype0 == napi_function,
67+
"Wrong type of arguments. Expects a function as first argument.");
68+
69+
napi_value* argv = args + 1;
70+
argc = argc - 1;
71+
72+
napi_value global;
73+
NODE_API_CALL(env, napi_get_global(env, &global));
74+
75+
napi_value result;
76+
NODE_API_CALL(env, napi_call_function(env, global, args[0], argc, argv, &result));
77+
78+
return result;
79+
}
80+
81+
static napi_value TestFunctionName(napi_env env, napi_callback_info info) {
82+
return NULL;
83+
}
84+
85+
static void finalize_function(napi_env env, void* data, void* hint) {
86+
napi_ref ref = data;
87+
88+
// Retrieve the JavaScript undefined value.
89+
napi_value undefined;
90+
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
91+
92+
// Retrieve the JavaScript function we must call.
93+
napi_value js_function;
94+
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, ref, &js_function));
95+
96+
// Call the JavaScript function to indicate that the generated JavaScript
97+
// function is about to be gc-ed.
98+
NODE_API_CALL_RETURN_VOID(env,
99+
napi_call_function(env, undefined, js_function, 0, NULL, NULL));
100+
101+
// Destroy the persistent reference to the function we just called so as to
102+
// properly clean up.
103+
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, ref));
104+
}
105+
106+
static napi_value MakeTrackedFunction(napi_env env, napi_callback_info info) {
107+
size_t argc = 1;
108+
napi_value js_finalize_cb;
109+
napi_valuetype arg_type;
110+
111+
// Retrieve and validate from the arguments the function we will use to
112+
// indicate to JavaScript that the function we are about to create is about to
113+
// be gc-ed.
114+
NODE_API_CALL(env,
115+
napi_get_cb_info(env, info, &argc, &js_finalize_cb, NULL, NULL));
116+
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
117+
NODE_API_CALL(env, napi_typeof(env, js_finalize_cb, &arg_type));
118+
NODE_API_ASSERT(env, arg_type == napi_function, "Argument must be a function");
119+
120+
// Dynamically create a function.
121+
napi_value result;
122+
NODE_API_CALL(env,
123+
napi_create_function(
124+
env, "TrackedFunction", NAPI_AUTO_LENGTH, TestFunctionName, NULL,
125+
&result));
126+
127+
// Create a strong reference to the function we will call when the tracked
128+
// function is about to be gc-ed.
129+
napi_ref js_finalize_cb_ref;
130+
NODE_API_CALL(env,
131+
napi_create_reference(env, js_finalize_cb, 1, &js_finalize_cb_ref));
132+
133+
// Attach a finalizer to the dynamically created function and pass it the
134+
// strong reference we created in the previous step.
135+
NODE_API_CALL(env,
136+
napi_wrap(
137+
env, result, js_finalize_cb_ref, finalize_function, NULL, NULL));
138+
139+
return result;
140+
}
141+
142+
static napi_value TestBadReturnExceptionPending(napi_env env, napi_callback_info info) {
143+
napi_throw_error(env, "throwing exception", "throwing exception");
144+
145+
// addons should only ever return a valid napi_value even if an
146+
// exception occurs, but we have seen that the C++ wrapper
147+
// with exceptions enabled sometimes returns an invalid value
148+
// when an exception is thrown. Test that we ignore the return
149+
// value then an exception is pending. We use 0xFFFFFFFF as a value
150+
// that should never be a valid napi_value and node seems to
151+
// crash if it is not ignored indicating that it is indeed invalid.
152+
return (napi_value)(0xFFFFFFFFF);
153+
}
154+
155+
EXTERN_C_START
156+
napi_value Init(napi_env env, napi_value exports) {
157+
napi_value fn1;
158+
NODE_API_CALL(env, napi_create_function(
159+
env, NULL, NAPI_AUTO_LENGTH, TestCallFunction, NULL, &fn1));
160+
161+
napi_value fn2;
162+
NODE_API_CALL(env, napi_create_function(
163+
env, "Name", NAPI_AUTO_LENGTH, TestFunctionName, NULL, &fn2));
164+
165+
napi_value fn3;
166+
NODE_API_CALL(env, napi_create_function(
167+
env, "Name_extra", 5, TestFunctionName, NULL, &fn3));
168+
169+
napi_value fn4;
170+
NODE_API_CALL(env,
171+
napi_create_function(
172+
env, "MakeTrackedFunction", NAPI_AUTO_LENGTH, MakeTrackedFunction,
173+
NULL, &fn4));
174+
175+
napi_value fn5;
176+
NODE_API_CALL(env,
177+
napi_create_function(
178+
env, "TestCreateFunctionParameters", NAPI_AUTO_LENGTH,
179+
TestCreateFunctionParameters, NULL, &fn5));
180+
181+
napi_value fn6;
182+
NODE_API_CALL(env,
183+
napi_create_function(
184+
env, "TestBadReturnExceptionPending", NAPI_AUTO_LENGTH,
185+
TestBadReturnExceptionPending, NULL, &fn6));
186+
187+
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestCall", fn1));
188+
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestName", fn2));
189+
NODE_API_CALL(env,
190+
napi_set_named_property(env, exports, "TestNameShort", fn3));
191+
NODE_API_CALL(env,
192+
napi_set_named_property(env, exports, "MakeTrackedFunction", fn4));
193+
194+
NODE_API_CALL(env,
195+
napi_set_named_property(
196+
env, exports, "TestCreateFunctionParameters", fn5));
197+
198+
NODE_API_CALL(env,
199+
napi_set_named_property(
200+
env, exports, "TestBadReturnExceptionPending", fn6));
201+
202+
return exports;
203+
}
204+
EXTERN_C_END

0 commit comments

Comments
 (0)