Skip to content

Commit d833299

Browse files
kraenhansenclaude
andcommitted
feat: port 6_object_wrap test to CTS
Port all three build targets and test files from upstream: - myobject (stable) — object wrap with getters/setters/methods - myobject_basic_finalizer (experimental) — basic finalizer verification - nested_wrap (NAPI_VERSION=10) — nested ref finalization The experimental target (myobject_basic_finalizer) exercises the add_node_api_cts_experimental_addon() CMake function for the first time, serving as end-to-end validation of the experimental infrastructure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a8f4477 commit d833299

File tree

9 files changed

+515
-1
lines changed

9 files changed

+515
-1
lines changed

PORTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
4444
| `3_callbacks` | Ported ✅ | Easy |
4545
| `4_object_factory` | Ported ✅ | Easy |
4646
| `5_function_factory` | Ported ✅ | Easy |
47-
| `6_object_wrap` | Not ported | Medium |
47+
| `6_object_wrap` | Ported ✅ | Medium |
4848
| `7_factory_wrap` | Ported ✅ | Easy |
4949
| `8_passing_wrapped` | Ported ✅ | Easy |
5050
| `test_array` | Ported ✅ | Easy |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
add_node_api_cts_addon(myobject myobject.cc)
2+
3+
add_node_api_cts_experimental_addon(myobject_basic_finalizer SOURCES myobject.cc)
4+
5+
add_node_api_cts_addon(nested_wrap nested_wrap.cc)
6+
target_compile_definitions(nested_wrap PRIVATE NAPI_VERSION=10)
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
#include "myobject.h"
2+
#include "../common.h"
3+
#include "../entry_point.h"
4+
#include "assert.h"
5+
6+
typedef int32_t FinalizerData;
7+
8+
napi_ref MyObject::constructor;
9+
10+
MyObject::MyObject(double value)
11+
: value_(value), env_(nullptr), wrapper_(nullptr) {}
12+
13+
MyObject::~MyObject() {
14+
napi_delete_reference(env_, wrapper_);
15+
}
16+
17+
void MyObject::Destructor(node_api_basic_env env,
18+
void* nativeObject,
19+
void* /*finalize_hint*/) {
20+
MyObject* obj = static_cast<MyObject*>(nativeObject);
21+
delete obj;
22+
23+
FinalizerData* data;
24+
NODE_API_BASIC_CALL_RETURN_VOID(
25+
env, napi_get_instance_data(env, reinterpret_cast<void**>(&data)));
26+
*data += 1;
27+
}
28+
29+
void MyObject::Init(napi_env env, napi_value exports) {
30+
napi_property_descriptor properties[] = {
31+
{"value", nullptr, nullptr, GetValue, SetValue, 0, napi_default, 0},
32+
{"valueReadonly",
33+
nullptr,
34+
nullptr,
35+
GetValue,
36+
nullptr,
37+
0,
38+
napi_default,
39+
0},
40+
DECLARE_NODE_API_PROPERTY("plusOne", PlusOne),
41+
DECLARE_NODE_API_PROPERTY("multiply", Multiply),
42+
};
43+
44+
napi_value cons;
45+
NODE_API_CALL_RETURN_VOID(
46+
env,
47+
napi_define_class(env,
48+
"MyObject",
49+
-1,
50+
New,
51+
nullptr,
52+
sizeof(properties) / sizeof(napi_property_descriptor),
53+
properties,
54+
&cons));
55+
56+
NODE_API_CALL_RETURN_VOID(env,
57+
napi_create_reference(env, cons, 1, &constructor));
58+
59+
NODE_API_CALL_RETURN_VOID(
60+
env, napi_set_named_property(env, exports, "MyObject", cons));
61+
}
62+
63+
napi_value MyObject::New(napi_env env, napi_callback_info info) {
64+
napi_value new_target;
65+
NODE_API_CALL(env, napi_get_new_target(env, info, &new_target));
66+
bool is_constructor = (new_target != nullptr);
67+
68+
size_t argc = 1;
69+
napi_value args[1];
70+
napi_value _this;
71+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
72+
73+
if (is_constructor) {
74+
// Invoked as constructor: `new MyObject(...)`
75+
double value = 0;
76+
77+
napi_valuetype valuetype;
78+
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
79+
80+
if (valuetype != napi_undefined) {
81+
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value));
82+
}
83+
84+
MyObject* obj = new MyObject(value);
85+
86+
obj->env_ = env;
87+
NODE_API_CALL(env,
88+
napi_wrap(env,
89+
_this,
90+
obj,
91+
MyObject::Destructor,
92+
nullptr /* finalize_hint */,
93+
&obj->wrapper_));
94+
95+
return _this;
96+
}
97+
98+
// Invoked as plain function `MyObject(...)`, turn into construct call.
99+
argc = 1;
100+
napi_value argv[1] = {args[0]};
101+
102+
napi_value cons;
103+
NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));
104+
105+
napi_value instance;
106+
NODE_API_CALL(env, napi_new_instance(env, cons, argc, argv, &instance));
107+
108+
return instance;
109+
}
110+
111+
napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {
112+
napi_value _this;
113+
NODE_API_CALL(env,
114+
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
115+
116+
MyObject* obj;
117+
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
118+
119+
napi_value num;
120+
NODE_API_CALL(env, napi_create_double(env, obj->value_, &num));
121+
122+
return num;
123+
}
124+
125+
napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {
126+
size_t argc = 1;
127+
napi_value args[1];
128+
napi_value _this;
129+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
130+
131+
MyObject* obj;
132+
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
133+
134+
NODE_API_CALL(env, napi_get_value_double(env, args[0], &obj->value_));
135+
136+
return nullptr;
137+
}
138+
139+
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
140+
napi_value _this;
141+
NODE_API_CALL(env,
142+
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
143+
144+
MyObject* obj;
145+
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
146+
147+
obj->value_ += 1;
148+
149+
napi_value num;
150+
NODE_API_CALL(env, napi_create_double(env, obj->value_, &num));
151+
152+
return num;
153+
}
154+
155+
napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {
156+
size_t argc = 1;
157+
napi_value args[1];
158+
napi_value _this;
159+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
160+
161+
double multiple = 1;
162+
if (argc >= 1) {
163+
NODE_API_CALL(env, napi_get_value_double(env, args[0], &multiple));
164+
}
165+
166+
MyObject* obj;
167+
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
168+
169+
napi_value cons;
170+
NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));
171+
172+
const int kArgCount = 1;
173+
napi_value argv[kArgCount];
174+
NODE_API_CALL(env, napi_create_double(env, obj->value_ * multiple, argv));
175+
176+
napi_value instance;
177+
NODE_API_CALL(env, napi_new_instance(env, cons, kArgCount, argv, &instance));
178+
179+
return instance;
180+
}
181+
182+
// This finalizer should never be invoked.
183+
void ObjectWrapDanglingReferenceFinalizer(node_api_basic_env env,
184+
void* finalize_data,
185+
void* finalize_hint) {
186+
assert(0 && "unreachable");
187+
}
188+
189+
napi_ref dangling_ref;
190+
napi_value ObjectWrapDanglingReference(napi_env env, napi_callback_info info) {
191+
size_t argc = 1;
192+
napi_value args[1];
193+
NODE_API_CALL(env,
194+
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
195+
196+
// Create a napi_wrap and remove it immediately, whilst leaving the out-param
197+
// ref dangling (not deleted).
198+
NODE_API_CALL(env,
199+
napi_wrap(env,
200+
args[0],
201+
nullptr,
202+
ObjectWrapDanglingReferenceFinalizer,
203+
nullptr,
204+
&dangling_ref));
205+
NODE_API_CALL(env, napi_remove_wrap(env, args[0], nullptr));
206+
207+
return args[0];
208+
}
209+
210+
napi_value ObjectWrapDanglingReferenceTest(napi_env env,
211+
napi_callback_info info) {
212+
napi_value out;
213+
napi_value ret;
214+
NODE_API_CALL(env, napi_get_reference_value(env, dangling_ref, &out));
215+
216+
if (out == nullptr) {
217+
// If the napi_ref has been invalidated, delete it.
218+
NODE_API_CALL(env, napi_delete_reference(env, dangling_ref));
219+
NODE_API_CALL(env, napi_get_boolean(env, true, &ret));
220+
} else {
221+
// The dangling napi_ref is still valid.
222+
NODE_API_CALL(env, napi_get_boolean(env, false, &ret));
223+
}
224+
return ret;
225+
}
226+
227+
static napi_value GetFinalizerCallCount(napi_env env, napi_callback_info info) {
228+
size_t argc = 1;
229+
napi_value argv[1];
230+
FinalizerData* data;
231+
napi_value result;
232+
233+
NODE_API_CALL(env,
234+
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
235+
NODE_API_CALL(env,
236+
napi_get_instance_data(env, reinterpret_cast<void**>(&data)));
237+
NODE_API_CALL(env, napi_create_int32(env, *data, &result));
238+
return result;
239+
}
240+
241+
static void finalizeData(napi_env env, void* data, void* hint) {
242+
delete reinterpret_cast<FinalizerData*>(data);
243+
}
244+
245+
EXTERN_C_START
246+
napi_value Init(napi_env env, napi_value exports) {
247+
FinalizerData* data = new FinalizerData;
248+
*data = 0;
249+
NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, nullptr));
250+
251+
MyObject::Init(env, exports);
252+
253+
napi_property_descriptor descriptors[] = {
254+
DECLARE_NODE_API_PROPERTY("objectWrapDanglingReference",
255+
ObjectWrapDanglingReference),
256+
DECLARE_NODE_API_PROPERTY("objectWrapDanglingReferenceTest",
257+
ObjectWrapDanglingReferenceTest),
258+
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", GetFinalizerCallCount),
259+
};
260+
261+
NODE_API_CALL(
262+
env,
263+
napi_define_properties(env,
264+
exports,
265+
sizeof(descriptors) / sizeof(*descriptors),
266+
descriptors));
267+
268+
return exports;
269+
}
270+
EXTERN_C_END
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_
2+
#define TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_
3+
4+
#include <js_native_api.h>
5+
6+
class MyObject {
7+
public:
8+
static void Init(napi_env env, napi_value exports);
9+
static void Destructor(node_api_basic_env env,
10+
void* nativeObject,
11+
void* finalize_hint);
12+
13+
private:
14+
explicit MyObject(double value_ = 0);
15+
~MyObject();
16+
17+
static napi_value New(napi_env env, napi_callback_info info);
18+
static napi_value GetValue(napi_env env, napi_callback_info info);
19+
static napi_value SetValue(napi_env env, napi_callback_info info);
20+
static napi_value PlusOne(napi_env env, napi_callback_info info);
21+
static napi_value Multiply(napi_env env, napi_callback_info info);
22+
static napi_ref constructor;
23+
double value_;
24+
napi_env env_;
25+
napi_ref wrapper_;
26+
};
27+
28+
#endif // TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_

0 commit comments

Comments
 (0)