Skip to content

Commit bbb94f9

Browse files
authored
feat: add node_api_set_prototype (#188)
1 parent 6fb16a8 commit bbb94f9

File tree

5 files changed

+84
-24
lines changed

5 files changed

+84
-24
lines changed

packages/emnapi/include/node/js_native_api.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_string(napi_env env,
203203
napi_value* result);
204204

205205
// Methods to work with Objects
206+
#ifdef NAPI_EXPERIMENTAL
207+
#define NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE
208+
NAPI_EXTERN napi_status NAPI_CDECL node_api_set_prototype(napi_env env,
209+
napi_value object,
210+
napi_value value);
211+
#endif
206212
NAPI_EXTERN napi_status NAPI_CDECL napi_get_prototype(napi_env env,
207213
napi_value object,
208214
napi_value* result);

packages/emnapi/src/value/convert2c.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@ export function napi_get_arraybuffer_info (env: napi_env, arraybuffer: napi_valu
4747
return envObject.clearLastError()
4848
}
4949

50+
/**
51+
* @__sig ippp
52+
*/
53+
export function node_api_set_prototype (env: napi_env, object: napi_value, value: napi_value): napi_status {
54+
return $PREAMBLE!(env, (envObject) => {
55+
$CHECK_ARG!(envObject, value)
56+
const obj = emnapiCtx.jsValueFromNapiValue(object)!
57+
if (obj == null) {
58+
throw new TypeError('Cannot convert undefined or null to object')
59+
}
60+
const type = typeof obj
61+
let v: any
62+
try {
63+
v = (type === 'object' && obj !== null) || type === 'function' ? obj : Object(obj)
64+
} catch (_) {
65+
return envObject.setLastError(napi_status.napi_object_expected)
66+
}
67+
const val = emnapiCtx.jsValueFromNapiValue(value)!
68+
Object.setPrototypeOf(v, val)
69+
70+
return $GET_RETURN_STATUS!(envObject)
71+
})
72+
}
73+
5074
/**
5175
* @__sig ippp
5276
*/

packages/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ add_test("callback" "./callback/binding.c" OFF)
297297
add_test("objfac" "./objfac/binding.c" OFF)
298298
add_test("fnfac" "./fnfac/binding.c" OFF)
299299
add_test("general" "./general/binding.c" OFF)
300+
target_compile_definitions("general" PRIVATE "NAPI_EXPERIMENTAL")
300301
add_test("filename" "./filename/binding.c" OFF)
301302
add_test("string" "./string/binding.c;./string/test_null.c" OFF)
302303
target_compile_definitions("string" PRIVATE "NAPI_VERSION=10")

packages/test/general/binding.c

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ static napi_value testStrictEquals(napi_env env, napi_callback_info info) {
2020
return result;
2121
}
2222

23+
static napi_value testSetPrototype(napi_env env, napi_callback_info info) {
24+
size_t argc = 2;
25+
napi_value args[2];
26+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
27+
28+
NODE_API_CALL(env, node_api_set_prototype(env, args[0], args[1]));
29+
30+
return NULL;
31+
}
32+
2333
static napi_value testGetPrototype(napi_env env, napi_callback_info info) {
2434
size_t argc = 1;
2535
napi_value args[1];
@@ -133,7 +143,7 @@ static napi_value testNapiTypeof(napi_env env, napi_callback_info info) {
133143
}
134144

135145
static bool deref_item_called = false;
136-
static void deref_item(napi_env env, void* data, void* hint) {
146+
static void deref_item(node_api_nogc_env env, void* data, void* hint) {
137147
(void) hint;
138148

139149
NODE_API_ASSERT_RETURN_VOID(env, data == &deref_item_called,
@@ -152,7 +162,7 @@ static napi_value deref_item_was_called(napi_env env, napi_callback_info info) {
152162

153163
static napi_value wrap_first_arg(napi_env env,
154164
napi_callback_info info,
155-
napi_finalize finalizer,
165+
node_api_basic_finalize finalizer,
156166
void* data) {
157167
size_t argc = 1;
158168
napi_value to_wrap;
@@ -191,7 +201,7 @@ static napi_value remove_wrap(napi_env env, napi_callback_info info) {
191201
}
192202

193203
static bool finalize_called = false;
194-
static void test_finalize(napi_env env, void* data, void* hint) {
204+
static void test_finalize(node_api_nogc_env env, void* data, void* hint) {
195205
finalize_called = true;
196206
}
197207

@@ -238,6 +248,15 @@ static void finalizer_only_callback(napi_env env, void* data, void* hint) {
238248
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, js_cb_ref));
239249
}
240250

251+
static void schedule_finalizer_only_callback(node_api_nogc_env env,
252+
void* data,
253+
void* hint) {
254+
NODE_API_CALL_RETURN_VOID(
255+
(napi_env)env,
256+
node_api_post_finalizer(
257+
(napi_env)env, finalizer_only_callback, data, NULL));
258+
}
259+
241260
static napi_value add_finalizer_only(napi_env env, napi_callback_info info) {
242261
size_t argc = 2;
243262
napi_value argv[2];
@@ -249,7 +268,7 @@ static napi_value add_finalizer_only(napi_env env, napi_callback_info info) {
249268
napi_add_finalizer(env,
250269
argv[0],
251270
js_cb_ref,
252-
finalizer_only_callback,
271+
schedule_finalizer_only_callback,
253272
NULL,
254273
NULL));
255274
return NULL;
@@ -264,7 +283,9 @@ static const char* env_cleanup_finalizer_messages[] = {
264283
};
265284
#endif
266285

267-
static void cleanup_env_finalizer(napi_env env, void* data, void* hint) {
286+
static void cleanup_env_finalizer(node_api_nogc_env env,
287+
void* data,
288+
void* hint) {
268289
(void) env;
269290
(void) hint;
270291
#if !defined(__wasm__) || (defined(__EMSCRIPTEN__) || defined(__wasi__))
@@ -304,25 +325,27 @@ static napi_value getDynamicallyInitialized(napi_env env) {
304325
EXTERN_C_START
305326
napi_value Init(napi_env env, napi_value exports) {
306327
napi_property_descriptor descriptors[] = {
307-
DECLARE_NODE_API_PROPERTY("testStrictEquals", testStrictEquals),
308-
DECLARE_NODE_API_PROPERTY("testGetPrototype", testGetPrototype),
309-
DECLARE_NODE_API_PROPERTY("testGetVersion", testGetVersion),
310-
DECLARE_NODE_API_PROPERTY("testNapiRun", testNapiRun),
311-
DECLARE_NODE_API_PROPERTY("doInstanceOf", doInstanceOf),
312-
DECLARE_NODE_API_PROPERTY("getUndefined", getUndefined),
313-
DECLARE_NODE_API_PROPERTY("getNull", getNull),
314-
DECLARE_NODE_API_PROPERTY("createNapiError", createNapiError),
315-
DECLARE_NODE_API_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup),
316-
DECLARE_NODE_API_PROPERTY("testNapiTypeof", testNapiTypeof),
317-
DECLARE_NODE_API_PROPERTY("wrap", wrap),
318-
DECLARE_NODE_API_PROPERTY("envCleanupWrap", env_cleanup_wrap),
319-
DECLARE_NODE_API_PROPERTY("unwrap", unwrap),
320-
DECLARE_NODE_API_PROPERTY("removeWrap", remove_wrap),
321-
DECLARE_NODE_API_PROPERTY("addFinalizerOnly", add_finalizer_only),
322-
DECLARE_NODE_API_PROPERTY("testFinalizeWrap", test_finalize_wrap),
323-
DECLARE_NODE_API_PROPERTY("finalizeWasCalled", finalize_was_called),
324-
DECLARE_NODE_API_PROPERTY("derefItemWasCalled", deref_item_was_called),
325-
DECLARE_NODE_API_PROPERTY("testAdjustExternalMemory", testAdjustExternalMemory),
328+
DECLARE_NODE_API_PROPERTY("testStrictEquals", testStrictEquals),
329+
DECLARE_NODE_API_PROPERTY("testSetPrototype", testSetPrototype),
330+
DECLARE_NODE_API_PROPERTY("testGetPrototype", testGetPrototype),
331+
DECLARE_NODE_API_PROPERTY("testGetVersion", testGetVersion),
332+
DECLARE_NODE_API_PROPERTY("testNapiRun", testNapiRun),
333+
DECLARE_NODE_API_PROPERTY("doInstanceOf", doInstanceOf),
334+
DECLARE_NODE_API_PROPERTY("getUndefined", getUndefined),
335+
DECLARE_NODE_API_PROPERTY("getNull", getNull),
336+
DECLARE_NODE_API_PROPERTY("createNapiError", createNapiError),
337+
DECLARE_NODE_API_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup),
338+
DECLARE_NODE_API_PROPERTY("testNapiTypeof", testNapiTypeof),
339+
DECLARE_NODE_API_PROPERTY("wrap", wrap),
340+
DECLARE_NODE_API_PROPERTY("envCleanupWrap", env_cleanup_wrap),
341+
DECLARE_NODE_API_PROPERTY("unwrap", unwrap),
342+
DECLARE_NODE_API_PROPERTY("removeWrap", remove_wrap),
343+
DECLARE_NODE_API_PROPERTY("addFinalizerOnly", add_finalizer_only),
344+
DECLARE_NODE_API_PROPERTY("testFinalizeWrap", test_finalize_wrap),
345+
DECLARE_NODE_API_PROPERTY("finalizeWasCalled", finalize_was_called),
346+
DECLARE_NODE_API_PROPERTY("derefItemWasCalled", deref_item_was_called),
347+
DECLARE_NODE_API_PROPERTY("testAdjustExternalMemory",
348+
testAdjustExternalMemory),
326349
#ifdef __wasm__
327350
{
328351
.utf8name = "dynamicallyInitialized",

packages/test/general/general.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = load('general').then(async test_general => {
1919

2020
const baseObject = new BaseClass()
2121
const extendedObject = new ExtendedClass()
22+
const nullProtoObject = { __proto__: null }
2223

2324
if (!process.env.EMNAPI_TEST_NATIVE) {
2425
assert.ok(test_general.dynamicallyInitialized)
@@ -30,6 +31,11 @@ module.exports = load('general').then(async test_general => {
3031
assert.ok(test_general.testStrictEquals(val2, val3))
3132
assert.strictEqual(test_general.testStrictEquals(NaN, NaN), false)
3233

34+
// Test napi_set_prototype
35+
test_general.testSetPrototype(nullProtoObject, Object.prototype)
36+
assert.strictEqual(Object.getPrototypeOf(nullProtoObject),
37+
Object.prototype)
38+
3339
// Test napi_get_prototype
3440
assert.strictEqual(test_general.testGetPrototype(baseObject),
3541
Object.getPrototypeOf(baseObject))

0 commit comments

Comments
 (0)