Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/emnapi/include/node/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_boolean(napi_env env,
// Methods to create Primitive types/Objects
NAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env,
napi_value* result);
#ifdef NAPI_EXPERIMENTAL
#define NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
NAPI_EXTERN napi_status NAPI_CDECL
napi_create_object_with_properties(napi_env env,
napi_value prototype_or_null,
napi_value* property_names,
napi_value* property_values,
size_t property_count,
napi_value* result);
#endif // NAPI_EXPERIMENTAL

NAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env,
napi_value* result);
NAPI_EXTERN napi_status NAPI_CDECL
Expand Down
57 changes: 55 additions & 2 deletions packages/emnapi/src/value/create.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { emnapiCtx } from 'emnapi:shared'
import { wasmMemory, _malloc } from 'emscripten:runtime'
import { from64, makeSetValue, to64 } from 'emscripten:parse-tools'
import { from64, makeGetValue, makeSetValue, POINTER_SIZE, to64 } from 'emscripten:parse-tools'
import { emnapiString } from '../string'
import { type MemoryViewDescriptor, emnapiExternalMemory } from '../memory'
import { emnapi_create_memory_view } from '../emnapi'
import { napi_add_finalizer } from '../wrap'
import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $GET_RETURN_STATUS, $PREAMBLE } from '../macro'
import { $CHECK_ARG, $CHECK_ENV_NOT_IN_GC, $GET_RETURN_STATUS, $PREAMBLE, $RETURN_STATUS_IF_FALSE } from '../macro'

/**
* @__sig ipp
Expand Down Expand Up @@ -196,6 +196,59 @@ export function napi_create_object (env: napi_env, result: Pointer<napi_value>):
return envObject.clearLastError()
}

/**
* @__sig ipppppp
*/
export function napi_create_object_with_properties (
env: napi_env,
prototype_or_null: napi_value,
property_names: Pointer<napi_value>,
property_values: Pointer<napi_value>,
property_count: size_t,
result: Pointer<napi_value>
): napi_status {
const envObject = $CHECK_ENV_NOT_IN_GC!(env)
$CHECK_ARG!(envObject, result)

from64('property_count')
property_count = property_count >>> 0

if (property_count > 0) {
$CHECK_ARG!(envObject, property_names)
$CHECK_ARG!(envObject, property_values)
}

const v8_prototype_or_null = prototype_or_null
? emnapiCtx.jsValueFromNapiValue(prototype_or_null)
: null

const properties: PropertyDescriptorMap = {}

from64('property_names')
from64('property_values')
for (let i = 0; i < property_count; i++) {
const name_value = emnapiCtx.jsValueFromNapiValue(makeGetValue('property_names', 'i * ' + POINTER_SIZE, '*'))
$RETURN_STATUS_IF_FALSE(envObject, typeof name_value === 'string' || typeof name_value === 'symbol', napi_status.napi_name_expected)
properties[name_value] = {
value: emnapiCtx.jsValueFromNapiValue(makeGetValue('property_values', 'i * ' + POINTER_SIZE, '*')),
writable: true,
enumerable: true,
configurable: true
}
}

let obj: any
try {
obj = Object.defineProperties(Object.create(v8_prototype_or_null), properties)
} catch (_) {
return envObject.setLastError(napi_status.napi_generic_failure)
}
const value = emnapiCtx.napiValueFromJsValue(obj)
from64('result')
makeSetValue('result', 0, 'value', '*')
return envObject.clearLastError()
}

/**
* @__sig ippp
*/
Expand Down
1 change: 1 addition & 0 deletions packages/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ target_compile_definitions("string" PRIVATE "NAPI_VERSION=10")
add_test("property" "./property/binding.c" OFF)
add_test("promise" "./promise/binding.c" OFF)
add_test("object" "./object/test_null.c;./object/test_object.c" OFF)
target_compile_definitions("object" PRIVATE "NAPI_EXPERIMENTAL")
add_test("object_exception" "./object/test_exceptions.c" OFF)
add_test("objwrap" "./objwrap/myobject.cc" OFF)
add_test("objwrapbasicfinalizer" "./objwrap/myobject.cc" OFF)
Expand Down
18 changes: 18 additions & 0 deletions packages/test/object/object.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,4 +392,22 @@ module.exports = load('object').then(test_object => {
delete obj.x
}, /Cannot delete property 'x' of #<Object>/)
}

{
const objectWithProperties = test_object.TestCreateObjectWithProperties();
assert.strictEqual(typeof objectWithProperties, 'object');
assert.strictEqual(objectWithProperties.name, 'Foo');
assert.strictEqual(objectWithProperties.age, 42);
assert.strictEqual(objectWithProperties.active, true);

const emptyObject = test_object.TestCreateObjectWithPropertiesEmpty();
assert.strictEqual(typeof emptyObject, 'object');
assert.strictEqual(Object.keys(emptyObject).length, 0);

const objectWithCustomPrototype = test_object.TestCreateObjectWithCustomPrototype();
assert.strictEqual(typeof objectWithCustomPrototype, 'object');
assert.deepStrictEqual(Object.getOwnPropertyNames(objectWithCustomPrototype), ['value']);
assert.strictEqual(objectWithCustomPrototype.value, 42);
assert.strictEqual(typeof objectWithCustomPrototype.test, 'function');
}
})
79 changes: 79 additions & 0 deletions packages/test/object/test_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,79 @@ CheckTypeTag(napi_env env, napi_callback_info info) {
return js_result;
}

static napi_value TestCreateObjectWithProperties(napi_env env,
napi_callback_info info) {
napi_value names[3];
napi_value values[3];
napi_value result;

NODE_API_CALL(
env, napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &names[0]));
NODE_API_CALL(
env, napi_create_string_utf8(env, "Foo", NAPI_AUTO_LENGTH, &values[0]));

NODE_API_CALL(
env, napi_create_string_utf8(env, "age", NAPI_AUTO_LENGTH, &names[1]));
NODE_API_CALL(env, napi_create_int32(env, 42, &values[1]));

NODE_API_CALL(
env, napi_create_string_utf8(env, "active", NAPI_AUTO_LENGTH, &names[2]));
NODE_API_CALL(env, napi_get_boolean(env, true, &values[2]));

napi_value null_prototype;
NODE_API_CALL(env, napi_get_null(env, &null_prototype));
NODE_API_CALL(env,
napi_create_object_with_properties(
env, null_prototype, names, values, 3, &result));

return result;
}

static napi_value TestCreateObjectWithPropertiesEmpty(napi_env env,
napi_callback_info info) {
napi_value result;

NODE_API_CALL(
env,
napi_create_object_with_properties(env, NULL, NULL, NULL, 0, &result));

return result;
}

static napi_value TestCreateObjectWithCustomPrototype(napi_env env,
napi_callback_info info) {
napi_value prototype;
napi_value method_name;
napi_value method_func;
napi_value names[1];
napi_value values[1];
napi_value result;

NODE_API_CALL(env, napi_create_object(env, &prototype));
NODE_API_CALL(
env,
napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &method_name));
NODE_API_CALL(env,
napi_create_function(env,
"test",
NAPI_AUTO_LENGTH,
TestCreateObjectWithProperties,
NULL,
&method_func));
NODE_API_CALL(env,
napi_set_property(env, prototype, method_name, method_func));

NODE_API_CALL(
env, napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &names[0]));
NODE_API_CALL(env, napi_create_int32(env, 42, &values[0]));

NODE_API_CALL(env,
napi_create_object_with_properties(
env, prototype, names, values, 1, &result));

return result;
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
Expand Down Expand Up @@ -754,6 +827,12 @@ napi_value Init(napi_env env, napi_value exports) {
DECLARE_NODE_API_PROPERTY("TestGetProperty", TestGetProperty),
DECLARE_NODE_API_PROPERTY("TestFreeze", TestFreeze),
DECLARE_NODE_API_PROPERTY("TestSeal", TestSeal),
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithProperties",
TestCreateObjectWithProperties),
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithPropertiesEmpty",
TestCreateObjectWithPropertiesEmpty),
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithCustomPrototype",
TestCreateObjectWithCustomPrototype),
};

init_test_null(env, exports);
Expand Down