Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

n-api: support for object freeze/seal #35359

Closed
wants to merge 2 commits into from
Closed
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
47 changes: 47 additions & 0 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4227,6 +4227,53 @@ this API will set the properties on the object one at a time, as defined by
`DefineOwnProperty()` (described in [Section 9.1.6][] of the ECMA-262
specification).

#### napi_object_freeze
<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_object_freeze(napi_env env,
napi_value object);
```

* `[in] env`: The environment that the N-API call is invoked under.
* `[in] object`: The object to freeze.

Returns `napi_ok` if the API succeeded.

This method freezes a given object. This prevents new properties from
being added to it, existing properties from being removed, prevents
changing the enumerability, configurability, or writability of existing
properties, and prevents the values of existing properties from being changed.
It also prevents the object's prototype from being changed. This is described
in [Section 19.1.2.6](https://tc39.es/ecma262/#sec-object.freeze) of the
ECMA-262 specification.

#### napi_object_seal
<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_object_seal(napi_env env,
napi_value object);
```

* `[in] env`: The environment that the N-API call is invoked under.
* `[in] object`: The object to seal.

Returns `napi_ok` if the API succeeded.

This method seals a given object. This prevents new properties from being
added to it, as well as marking all existing properties as non-configurable.
This is described in [Section 19.1.2.20](https://tc39.es/ecma262/#sec-object.seal)
of the ECMA-262 specification.

## Working with JavaScript functions

N-API provides a set of APIs that allow JavaScript code to
Expand Down
4 changes: 4 additions & 0 deletions src/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,10 @@ napi_check_object_type_tag(napi_env env,
napi_value value,
const napi_type_tag* type_tag,
bool* result);
NAPI_EXTERN napi_status napi_object_freeze(napi_env env,
napi_value object);
NAPI_EXTERN napi_status napi_object_seal(napi_env env,
napi_value object);
#endif // NAPI_EXPERIMENTAL

EXTERN_C_END
Expand Down
36 changes: 36 additions & 0 deletions src/js_native_api_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,42 @@ napi_status napi_define_properties(napi_env env,
return GET_RETURN_STATUS(env);
}

napi_status napi_object_freeze(napi_env env,
napi_value object) {
NAPI_PREAMBLE(env);

v8::Local<v8::Context> context = env->context();
v8::Local<v8::Object> obj;

CHECK_TO_OBJECT(env, context, obj, object);

v8::Maybe<bool> set_frozen =
obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);

RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env,
set_frozen.FromMaybe(false), napi_generic_failure);

return GET_RETURN_STATUS(env);
}

napi_status napi_object_seal(napi_env env,
napi_value object) {
NAPI_PREAMBLE(env);

v8::Local<v8::Context> context = env->context();
v8::Local<v8::Object> obj;

CHECK_TO_OBJECT(env, context, obj, object);

v8::Maybe<bool> set_sealed =
obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed);

RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env,
set_sealed.FromMaybe(false), napi_generic_failure);

return GET_RETURN_STATUS(env);
}

napi_status napi_is_array(napi_env env, napi_value value, bool* result) {
CHECK_ENV(env);
CHECK_ARG(env, value);
Expand Down
40 changes: 40 additions & 0 deletions test/js-native-api/test_object/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,43 @@ assert.deepStrictEqual(test_object.TestGetProperty(), {
keyIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument'
});

{
const obj = { x: 'a', y: 'b', z: 'c' };

test_object.TestSeal(obj);

assert.strictEqual(Object.isSealed(obj), true);

assert.throws(() => {
obj.w = 'd';
}, /Cannot add property w, object is not extensible/);

assert.throws(() => {
delete obj.x;
}, /Cannot delete property 'x' of #<Object>/);

// Sealed objects allow updating existing properties,
// so this should not throw.
obj.x = 'd';
}

{
const obj = { x: 10, y: 10, z: 10 };

test_object.TestFreeze(obj);

assert.strictEqual(Object.isFrozen(obj), true);

assert.throws(() => {
obj.x = 10;
}, /Cannot assign to read only property 'x' of object '#<Object>/);

assert.throws(() => {
obj.w = 15;
}, /Cannot add property w, object is not extensible/);

assert.throws(() => {
delete obj.x;
}, /Cannot delete property 'x' of #<Object>/);
}
codebytere marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 26 additions & 0 deletions test/js-native-api/test_object/test_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,30 @@ static napi_value TestGetProperty(napi_env env,
return object;
}

static napi_value TestFreeze(napi_env env,
napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

napi_value object = args[0];
NAPI_CALL(env, napi_object_freeze(env, object));

return object;
}

static napi_value TestSeal(napi_env env,
napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

napi_value object = args[0];
NAPI_CALL(env, napi_object_seal(env, object));

return object;
}

// We create two type tags. They are basically 128-bit UUIDs.
static const napi_type_tag type_tags[2] = {
{ 0xdaf987b3cc62481a, 0xb745b0497f299531 },
Expand Down Expand Up @@ -532,6 +556,8 @@ napi_value Init(napi_env env, napi_value exports) {
DECLARE_NAPI_PROPERTY("TypeTaggedInstance", TypeTaggedInstance),
DECLARE_NAPI_PROPERTY("CheckTypeTag", CheckTypeTag),
DECLARE_NAPI_PROPERTY("TestGetProperty", TestGetProperty),
DECLARE_NAPI_PROPERTY("TestFreeze", TestFreeze),
DECLARE_NAPI_PROPERTY("TestSeal", TestSeal),
};

init_test_null(env, exports);
Expand Down