Skip to content

Commit 4271254

Browse files
jasonginmhdawson
authored andcommitted
n-api: Reference and external tests
- Add a test project to addons-napi that covers the N-API reference and external APIs - Fix a bug in napi_typeof that was found by the new tests PR-URL: #12551 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent 892ce06 commit 4271254

File tree

5 files changed

+255
-2
lines changed

5 files changed

+255
-2
lines changed

src/node_api.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,10 @@ napi_status napi_typeof(napi_env env,
14641464
// This test has to come before IsObject because IsFunction
14651465
// implies IsObject
14661466
*result = napi_function;
1467+
} else if (v->IsExternal()) {
1468+
// This test has to come before IsObject because IsExternal
1469+
// implies IsObject
1470+
*result = napi_external;
14671471
} else if (v->IsObject()) {
14681472
*result = napi_object;
14691473
} else if (v->IsBoolean()) {
@@ -1474,8 +1478,6 @@ napi_status napi_typeof(napi_env env,
14741478
*result = napi_symbol;
14751479
} else if (v->IsNull()) {
14761480
*result = napi_null;
1477-
} else if (v->IsExternal()) {
1478-
*result = napi_external;
14791481
} else {
14801482
// Should not get here unless V8 has added some new kind of value.
14811483
return napi_set_last_error(env, napi_invalid_arg);

test/addons-napi/common.h

+3
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,6 @@
5050

5151
#define DECLARE_NAPI_PROPERTY(name, func) \
5252
{ (name), 0, (func), 0, 0, 0, napi_default, 0 }
53+
54+
#define DECLARE_NAPI_GETTER(name, func) \
55+
{ (name), 0, 0, (func), 0, 0, napi_default, 0 }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_reference",
5+
"sources": [ "test_reference.c" ]
6+
}
7+
]
8+
}
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
// Flags: --expose-gc
3+
4+
const common = require('../../common');
5+
const assert = require('assert');
6+
7+
const test_reference = require(`./build/${common.buildType}/test_reference`);
8+
9+
// This test script uses external values with finalizer callbacks
10+
// in order to track when values get garbage-collected. Each invocation
11+
// of a finalizer callback increments the finalizeCount property.
12+
assert.strictEqual(test_reference.finalizeCount, 0);
13+
14+
{
15+
// External value without a finalizer
16+
let value = test_reference.createExternal();
17+
assert.strictEqual(test_reference.finalizeCount, 0);
18+
assert.strictEqual(typeof value, 'object');
19+
test_reference.checkExternal(value);
20+
value = null;
21+
global.gc();
22+
assert.strictEqual(test_reference.finalizeCount, 0);
23+
}
24+
25+
{
26+
// External value with a finalizer
27+
let value = test_reference.createExternalWithFinalize();
28+
assert.strictEqual(test_reference.finalizeCount, 0);
29+
assert.strictEqual(typeof value, 'object');
30+
test_reference.checkExternal(value);
31+
value = null;
32+
global.gc();
33+
assert.strictEqual(test_reference.finalizeCount, 1);
34+
}
35+
36+
{
37+
// Weak reference
38+
let value = test_reference.createExternalWithFinalize();
39+
assert.strictEqual(test_reference.finalizeCount, 0);
40+
test_reference.createReference(value, 0);
41+
assert.strictEqual(test_reference.referenceValue, value);
42+
value = null;
43+
global.gc(); // Value should be GC'd because there is only a weak ref
44+
assert.strictEqual(test_reference.referenceValue, undefined);
45+
assert.strictEqual(test_reference.finalizeCount, 1);
46+
test_reference.deleteReference();
47+
}
48+
49+
{
50+
// Strong reference
51+
let value = test_reference.createExternalWithFinalize();
52+
assert.strictEqual(test_reference.finalizeCount, 0);
53+
test_reference.createReference(value, 1);
54+
assert.strictEqual(test_reference.referenceValue, value);
55+
value = null;
56+
global.gc(); // Value should NOT be GC'd because there is a strong ref
57+
assert.strictEqual(test_reference.finalizeCount, 0);
58+
test_reference.deleteReference();
59+
global.gc(); // Value should be GC'd because the strong ref was deleted
60+
assert.strictEqual(test_reference.finalizeCount, 1);
61+
}
62+
63+
{
64+
// Strong reference, increment then decrement to weak reference
65+
let value = test_reference.createExternalWithFinalize();
66+
assert.strictEqual(test_reference.finalizeCount, 0);
67+
test_reference.createReference(value, 1);
68+
value = null;
69+
global.gc(); // Value should NOT be GC'd because there is a strong ref
70+
assert.strictEqual(test_reference.finalizeCount, 0);
71+
72+
assert.strictEqual(test_reference.incrementRefcount(), 2);
73+
global.gc(); // Value should NOT be GC'd because there is a strong ref
74+
assert.strictEqual(test_reference.finalizeCount, 0);
75+
76+
assert.strictEqual(test_reference.decrementRefcount(), 1);
77+
global.gc(); // Value should NOT be GC'd because there is a strong ref
78+
assert.strictEqual(test_reference.finalizeCount, 0);
79+
80+
assert.strictEqual(test_reference.decrementRefcount(), 0);
81+
global.gc(); // Value should be GC'd because the ref is now weak!
82+
assert.strictEqual(test_reference.finalizeCount, 1);
83+
84+
test_reference.deleteReference();
85+
global.gc(); // Value was already GC'd
86+
assert.strictEqual(test_reference.finalizeCount, 1);
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include <node_api.h>
2+
#include "../common.h"
3+
#include <stdlib.h>
4+
5+
static int test_value = 1;
6+
static int finalize_count = 0;
7+
static napi_ref test_reference = NULL;
8+
9+
napi_value GetFinalizeCount(napi_env env, napi_callback_info info) {
10+
napi_value result;
11+
NAPI_CALL(env, napi_create_number(env, finalize_count, &result));
12+
return result;
13+
}
14+
15+
void FinalizeExternal(napi_env env, void* data, void* hint) {
16+
free(data);
17+
finalize_count++;
18+
}
19+
20+
napi_value CreateExternal(napi_env env, napi_callback_info info) {
21+
int* data = &test_value;
22+
23+
napi_value result;
24+
NAPI_CALL(env,
25+
napi_create_external(env,
26+
data,
27+
NULL, /* finalize_cb */
28+
NULL, /* finalize_hint */
29+
&result));
30+
31+
finalize_count = 0;
32+
return result;
33+
}
34+
35+
napi_value CreateExternalWithFinalize(napi_env env, napi_callback_info info) {
36+
int* data = malloc(sizeof(int));
37+
*data = test_value;
38+
39+
napi_value result;
40+
NAPI_CALL(env,
41+
napi_create_external(env,
42+
data,
43+
FinalizeExternal,
44+
NULL, /* finalize_hint */
45+
&result));
46+
47+
finalize_count = 0;
48+
return result;
49+
}
50+
51+
napi_value CheckExternal(napi_env env, napi_callback_info info) {
52+
size_t argc = 1;
53+
napi_value arg;
54+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL));
55+
56+
NAPI_ASSERT(env, argc == 1, "Expected one argument.");
57+
58+
napi_valuetype argtype;
59+
NAPI_CALL(env, napi_typeof(env, arg, &argtype));
60+
61+
NAPI_ASSERT(env, argtype == napi_external, "Expected an external value.")
62+
63+
int* data;
64+
NAPI_CALL(env, napi_get_value_external(env, arg, &data));
65+
66+
NAPI_ASSERT(env, data != NULL && *data == test_value,
67+
"An external data value of 1 was expected.")
68+
69+
return NULL;
70+
}
71+
72+
napi_value CreateReference(napi_env env, napi_callback_info info) {
73+
NAPI_ASSERT(env, test_reference == NULL,
74+
"The test allows only one reference at a time.");
75+
76+
size_t argc = 2;
77+
napi_value args[2];
78+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
79+
NAPI_ASSERT(env, argc == 2, "Expected two arguments.");
80+
81+
uint32_t initial_refcount;
82+
NAPI_CALL(env, napi_get_value_uint32(env, args[1], &initial_refcount));
83+
84+
NAPI_CALL(env,
85+
napi_create_reference(env, args[0], initial_refcount, &test_reference));
86+
87+
NAPI_ASSERT(env, test_reference != NULL,
88+
"A reference should have been created.");
89+
90+
return NULL;
91+
}
92+
93+
napi_value DeleteReference(napi_env env, napi_callback_info info) {
94+
NAPI_ASSERT(env, test_reference != NULL,
95+
"A reference must have been created.");
96+
97+
NAPI_CALL(env, napi_delete_reference(env, test_reference));
98+
test_reference = NULL;
99+
return NULL;
100+
}
101+
102+
napi_value IncrementRefcount(napi_env env, napi_callback_info info) {
103+
NAPI_ASSERT(env, test_reference != NULL,
104+
"A reference must have been created.");
105+
106+
uint32_t refcount;
107+
NAPI_CALL(env, napi_reference_ref(env, test_reference, &refcount));
108+
109+
napi_value result;
110+
NAPI_CALL(env, napi_create_number(env, refcount, &result));
111+
return result;
112+
}
113+
114+
napi_value DecrementRefcount(napi_env env, napi_callback_info info) {
115+
NAPI_ASSERT(env, test_reference != NULL,
116+
"A reference must have been created.");
117+
118+
uint32_t refcount;
119+
NAPI_CALL(env, napi_reference_unref(env, test_reference, &refcount));
120+
121+
napi_value result;
122+
NAPI_CALL(env, napi_create_number(env, refcount, &result));
123+
return result;
124+
}
125+
126+
napi_value GetReferenceValue(napi_env env, napi_callback_info info) {
127+
NAPI_ASSERT(env, test_reference != NULL,
128+
"A reference must have been created.");
129+
130+
napi_value result;
131+
NAPI_CALL(env, napi_get_reference_value(env, test_reference, &result));
132+
return result;
133+
}
134+
135+
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
136+
napi_property_descriptor descriptors[] = {
137+
DECLARE_NAPI_GETTER("finalizeCount", GetFinalizeCount),
138+
DECLARE_NAPI_PROPERTY("createExternal", CreateExternal),
139+
DECLARE_NAPI_PROPERTY("createExternalWithFinalize",
140+
CreateExternalWithFinalize),
141+
DECLARE_NAPI_PROPERTY("checkExternal", CheckExternal),
142+
DECLARE_NAPI_PROPERTY("createReference", CreateReference),
143+
DECLARE_NAPI_PROPERTY("deleteReference", DeleteReference),
144+
DECLARE_NAPI_PROPERTY("incrementRefcount", IncrementRefcount),
145+
DECLARE_NAPI_PROPERTY("decrementRefcount", DecrementRefcount),
146+
DECLARE_NAPI_GETTER("referenceValue", GetReferenceValue),
147+
};
148+
149+
NAPI_CALL_RETURN_VOID(env, napi_define_properties(
150+
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
151+
}
152+
153+
NAPI_MODULE(addon, Init)

0 commit comments

Comments
 (0)