Skip to content

Commit db6b25f

Browse files
authored
feat: add napi_create_object_with_properties method (#180)
1 parent 74d9437 commit db6b25f

File tree

5 files changed

+164
-2
lines changed

5 files changed

+164
-2
lines changed

packages/emnapi/include/node/js_native_api.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_boolean(napi_env env,
7272
// Methods to create Primitive types/Objects
7373
NAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env,
7474
napi_value* result);
75+
#ifdef NAPI_EXPERIMENTAL
76+
#define NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
77+
NAPI_EXTERN napi_status NAPI_CDECL
78+
napi_create_object_with_properties(napi_env env,
79+
napi_value prototype_or_null,
80+
napi_value* property_names,
81+
napi_value* property_values,
82+
size_t property_count,
83+
napi_value* result);
84+
#endif // NAPI_EXPERIMENTAL
85+
7586
NAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env,
7687
napi_value* result);
7788
NAPI_EXTERN napi_status NAPI_CDECL

packages/emnapi/src/value/create.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { emnapiCtx } from 'emnapi:shared'
22
import { wasmMemory, _malloc } from 'emscripten:runtime'
3-
import { from64, makeSetValue, to64 } from 'emscripten:parse-tools'
3+
import { from64, makeGetValue, makeSetValue, POINTER_SIZE, to64 } from 'emscripten:parse-tools'
44
import { emnapiString } from '../string'
55
import { type MemoryViewDescriptor, emnapiExternalMemory } from '../memory'
66
import { emnapi_create_memory_view } from '../emnapi'
77
import { napi_add_finalizer } from '../wrap'
8-
import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $GET_RETURN_STATUS, $PREAMBLE } from '../macro'
8+
import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $GET_RETURN_STATUS, $PREAMBLE, $RETURN_STATUS_IF_FALSE } from '../macro'
99

1010
/**
1111
* @__sig ipp
@@ -196,6 +196,59 @@ export function napi_create_object (env: napi_env, result: Pointer<napi_value>):
196196
return envObject.clearLastError()
197197
}
198198

199+
/**
200+
* @__sig ipppppp
201+
*/
202+
export function napi_create_object_with_properties (
203+
env: napi_env,
204+
prototype_or_null: napi_value,
205+
property_names: Pointer<napi_value>,
206+
property_values: Pointer<napi_value>,
207+
property_count: size_t,
208+
result: Pointer<napi_value>
209+
): napi_status {
210+
const envObject = $CHECK_ENV_NOT_IN_GC!(env)
211+
$CHECK_ARG!(envObject, result)
212+
213+
from64('property_count')
214+
property_count = property_count >>> 0
215+
216+
if (property_count > 0) {
217+
$CHECK_ARG!(envObject, property_names)
218+
$CHECK_ARG!(envObject, property_values)
219+
}
220+
221+
const v8_prototype_or_null = prototype_or_null
222+
? emnapiCtx.jsValueFromNapiValue(prototype_or_null)
223+
: null
224+
225+
const properties: PropertyDescriptorMap = {}
226+
227+
from64('property_names')
228+
from64('property_values')
229+
for (let i = 0; i < property_count; i++) {
230+
const name_value = emnapiCtx.jsValueFromNapiValue(makeGetValue('property_names', 'i * ' + POINTER_SIZE, '*'))
231+
$RETURN_STATUS_IF_FALSE(envObject, typeof name_value === 'string' || typeof name_value === 'symbol', napi_status.napi_name_expected)
232+
properties[name_value] = {
233+
value: emnapiCtx.jsValueFromNapiValue(makeGetValue('property_values', 'i * ' + POINTER_SIZE, '*')),
234+
writable: true,
235+
enumerable: true,
236+
configurable: true
237+
}
238+
}
239+
240+
let obj: any
241+
try {
242+
obj = Object.defineProperties(Object.create(v8_prototype_or_null), properties)
243+
} catch (_) {
244+
return envObject.setLastError(napi_status.napi_generic_failure)
245+
}
246+
const value = emnapiCtx.napiValueFromJsValue(obj)
247+
from64('result')
248+
makeSetValue('result', 0, 'value', '*')
249+
return envObject.clearLastError()
250+
}
251+
199252
/**
200253
* @__sig ippp
201254
*/

packages/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ target_compile_definitions("string" PRIVATE "NAPI_VERSION=10")
302302
add_test("property" "./property/binding.c" OFF)
303303
add_test("promise" "./promise/binding.c" OFF)
304304
add_test("object" "./object/test_null.c;./object/test_object.c" OFF)
305+
target_compile_definitions("object" PRIVATE "NAPI_EXPERIMENTAL")
305306
add_test("object_exception" "./object/test_exceptions.c" OFF)
306307
add_test("objwrap" "./objwrap/myobject.cc" OFF)
307308
add_test("objwrapbasicfinalizer" "./objwrap/myobject.cc" OFF)

packages/test/object/object.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,4 +392,22 @@ module.exports = load('object').then(test_object => {
392392
delete obj.x
393393
}, /Cannot delete property 'x' of #<Object>/)
394394
}
395+
396+
{
397+
const objectWithProperties = test_object.TestCreateObjectWithProperties();
398+
assert.strictEqual(typeof objectWithProperties, 'object');
399+
assert.strictEqual(objectWithProperties.name, 'Foo');
400+
assert.strictEqual(objectWithProperties.age, 42);
401+
assert.strictEqual(objectWithProperties.active, true);
402+
403+
const emptyObject = test_object.TestCreateObjectWithPropertiesEmpty();
404+
assert.strictEqual(typeof emptyObject, 'object');
405+
assert.strictEqual(Object.keys(emptyObject).length, 0);
406+
407+
const objectWithCustomPrototype = test_object.TestCreateObjectWithCustomPrototype();
408+
assert.strictEqual(typeof objectWithCustomPrototype, 'object');
409+
assert.deepStrictEqual(Object.getOwnPropertyNames(objectWithCustomPrototype), ['value']);
410+
assert.strictEqual(objectWithCustomPrototype.value, 42);
411+
assert.strictEqual(typeof objectWithCustomPrototype.test, 'function');
412+
}
395413
})

packages/test/object/test_object.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,79 @@ CheckTypeTag(napi_env env, napi_callback_info info) {
721721
return js_result;
722722
}
723723

724+
static napi_value TestCreateObjectWithProperties(napi_env env,
725+
napi_callback_info info) {
726+
napi_value names[3];
727+
napi_value values[3];
728+
napi_value result;
729+
730+
NODE_API_CALL(
731+
env, napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &names[0]));
732+
NODE_API_CALL(
733+
env, napi_create_string_utf8(env, "Foo", NAPI_AUTO_LENGTH, &values[0]));
734+
735+
NODE_API_CALL(
736+
env, napi_create_string_utf8(env, "age", NAPI_AUTO_LENGTH, &names[1]));
737+
NODE_API_CALL(env, napi_create_int32(env, 42, &values[1]));
738+
739+
NODE_API_CALL(
740+
env, napi_create_string_utf8(env, "active", NAPI_AUTO_LENGTH, &names[2]));
741+
NODE_API_CALL(env, napi_get_boolean(env, true, &values[2]));
742+
743+
napi_value null_prototype;
744+
NODE_API_CALL(env, napi_get_null(env, &null_prototype));
745+
NODE_API_CALL(env,
746+
napi_create_object_with_properties(
747+
env, null_prototype, names, values, 3, &result));
748+
749+
return result;
750+
}
751+
752+
static napi_value TestCreateObjectWithPropertiesEmpty(napi_env env,
753+
napi_callback_info info) {
754+
napi_value result;
755+
756+
NODE_API_CALL(
757+
env,
758+
napi_create_object_with_properties(env, NULL, NULL, NULL, 0, &result));
759+
760+
return result;
761+
}
762+
763+
static napi_value TestCreateObjectWithCustomPrototype(napi_env env,
764+
napi_callback_info info) {
765+
napi_value prototype;
766+
napi_value method_name;
767+
napi_value method_func;
768+
napi_value names[1];
769+
napi_value values[1];
770+
napi_value result;
771+
772+
NODE_API_CALL(env, napi_create_object(env, &prototype));
773+
NODE_API_CALL(
774+
env,
775+
napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &method_name));
776+
NODE_API_CALL(env,
777+
napi_create_function(env,
778+
"test",
779+
NAPI_AUTO_LENGTH,
780+
TestCreateObjectWithProperties,
781+
NULL,
782+
&method_func));
783+
NODE_API_CALL(env,
784+
napi_set_property(env, prototype, method_name, method_func));
785+
786+
NODE_API_CALL(
787+
env, napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &names[0]));
788+
NODE_API_CALL(env, napi_create_int32(env, 42, &values[0]));
789+
790+
NODE_API_CALL(env,
791+
napi_create_object_with_properties(
792+
env, prototype, names, values, 1, &result));
793+
794+
return result;
795+
}
796+
724797
EXTERN_C_START
725798
napi_value Init(napi_env env, napi_value exports) {
726799
napi_property_descriptor descriptors[] = {
@@ -754,6 +827,12 @@ napi_value Init(napi_env env, napi_value exports) {
754827
DECLARE_NODE_API_PROPERTY("TestGetProperty", TestGetProperty),
755828
DECLARE_NODE_API_PROPERTY("TestFreeze", TestFreeze),
756829
DECLARE_NODE_API_PROPERTY("TestSeal", TestSeal),
830+
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithProperties",
831+
TestCreateObjectWithProperties),
832+
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithPropertiesEmpty",
833+
TestCreateObjectWithPropertiesEmpty),
834+
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithCustomPrototype",
835+
TestCreateObjectWithCustomPrototype),
757836
};
758837

759838
init_test_null(env, exports);

0 commit comments

Comments
 (0)