Skip to content

Commit c995242

Browse files
codebytereBethGriggs
authored andcommitted
n-api: support for object freeze/seal
PR-URL: #35359 Reviewed-By: Gabriel Schulhof <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent 8877430 commit c995242

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed

doc/api/n-api.md

+47
Original file line numberDiff line numberDiff line change
@@ -4209,6 +4209,53 @@ this API will set the properties on the object one at a time, as defined by
42094209
`DefineOwnProperty()` (described in [Section 9.1.6][] of the ECMA-262
42104210
specification).
42114211

4212+
#### napi_object_freeze
4213+
<!-- YAML
4214+
added: REPLACEME
4215+
-->
4216+
4217+
> Stability: 1 - Experimental
4218+
4219+
```c
4220+
napi_status napi_object_freeze(napi_env env,
4221+
napi_value object);
4222+
```
4223+
4224+
* `[in] env`: The environment that the N-API call is invoked under.
4225+
* `[in] object`: The object to freeze.
4226+
4227+
Returns `napi_ok` if the API succeeded.
4228+
4229+
This method freezes a given object. This prevents new properties from
4230+
being added to it, existing properties from being removed, prevents
4231+
changing the enumerability, configurability, or writability of existing
4232+
properties, and prevents the values of existing properties from being changed.
4233+
It also prevents the object's prototype from being changed. This is described
4234+
in [Section 19.1.2.6](https://tc39.es/ecma262/#sec-object.freeze) of the
4235+
ECMA-262 specification.
4236+
4237+
#### napi_object_seal
4238+
<!-- YAML
4239+
added: REPLACEME
4240+
-->
4241+
4242+
> Stability: 1 - Experimental
4243+
4244+
```c
4245+
napi_status napi_object_seal(napi_env env,
4246+
napi_value object);
4247+
```
4248+
4249+
* `[in] env`: The environment that the N-API call is invoked under.
4250+
* `[in] object`: The object to seal.
4251+
4252+
Returns `napi_ok` if the API succeeded.
4253+
4254+
This method seals a given object. This prevents new properties from being
4255+
added to it, as well as marking all existing properties as non-configurable.
4256+
This is described in [Section 19.1.2.20](https://tc39.es/ecma262/#sec-object.seal)
4257+
of the ECMA-262 specification.
4258+
42124259
## Working with JavaScript functions
42134260

42144261
N-API provides a set of APIs that allow JavaScript code to

src/js_native_api.h

+4
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@ napi_check_object_type_tag(napi_env env,
550550
napi_value value,
551551
const napi_type_tag* type_tag,
552552
bool* result);
553+
NAPI_EXTERN napi_status napi_object_freeze(napi_env env,
554+
napi_value object);
555+
NAPI_EXTERN napi_status napi_object_seal(napi_env env,
556+
napi_value object);
553557
#endif // NAPI_EXPERIMENTAL
554558

555559
EXTERN_C_END

src/js_native_api_v8.cc

+36
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,42 @@ napi_status napi_define_properties(napi_env env,
13621362
return GET_RETURN_STATUS(env);
13631363
}
13641364

1365+
napi_status napi_object_freeze(napi_env env,
1366+
napi_value object) {
1367+
NAPI_PREAMBLE(env);
1368+
1369+
v8::Local<v8::Context> context = env->context();
1370+
v8::Local<v8::Object> obj;
1371+
1372+
CHECK_TO_OBJECT(env, context, obj, object);
1373+
1374+
v8::Maybe<bool> set_frozen =
1375+
obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
1376+
1377+
RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env,
1378+
set_frozen.FromMaybe(false), napi_generic_failure);
1379+
1380+
return GET_RETURN_STATUS(env);
1381+
}
1382+
1383+
napi_status napi_object_seal(napi_env env,
1384+
napi_value object) {
1385+
NAPI_PREAMBLE(env);
1386+
1387+
v8::Local<v8::Context> context = env->context();
1388+
v8::Local<v8::Object> obj;
1389+
1390+
CHECK_TO_OBJECT(env, context, obj, object);
1391+
1392+
v8::Maybe<bool> set_sealed =
1393+
obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed);
1394+
1395+
RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env,
1396+
set_sealed.FromMaybe(false), napi_generic_failure);
1397+
1398+
return GET_RETURN_STATUS(env);
1399+
}
1400+
13651401
napi_status napi_is_array(napi_env env, napi_value value, bool* result) {
13661402
CHECK_ENV(env);
13671403
CHECK_ARG(env, value);

test/js-native-api/test_object/test.js

+40
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,43 @@ assert.deepStrictEqual(test_object.TestGetProperty(), {
275275
keyIsNull: 'Invalid argument',
276276
resultIsNull: 'Invalid argument'
277277
});
278+
279+
{
280+
const obj = { x: 'a', y: 'b', z: 'c' };
281+
282+
test_object.TestSeal(obj);
283+
284+
assert.strictEqual(Object.isSealed(obj), true);
285+
286+
assert.throws(() => {
287+
obj.w = 'd';
288+
}, /Cannot add property w, object is not extensible/);
289+
290+
assert.throws(() => {
291+
delete obj.x;
292+
}, /Cannot delete property 'x' of #<Object>/);
293+
294+
// Sealed objects allow updating existing properties,
295+
// so this should not throw.
296+
obj.x = 'd';
297+
}
298+
299+
{
300+
const obj = { x: 10, y: 10, z: 10 };
301+
302+
test_object.TestFreeze(obj);
303+
304+
assert.strictEqual(Object.isFrozen(obj), true);
305+
306+
assert.throws(() => {
307+
obj.x = 10;
308+
}, /Cannot assign to read only property 'x' of object '#<Object>/);
309+
310+
assert.throws(() => {
311+
obj.w = 15;
312+
}, /Cannot add property w, object is not extensible/);
313+
314+
assert.throws(() => {
315+
delete obj.x;
316+
}, /Cannot delete property 'x' of #<Object>/);
317+
}

test/js-native-api/test_object/test_object.c

+26
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,30 @@ static napi_value TestGetProperty(napi_env env,
472472
return object;
473473
}
474474

475+
static napi_value TestFreeze(napi_env env,
476+
napi_callback_info info) {
477+
size_t argc = 1;
478+
napi_value args[1];
479+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
480+
481+
napi_value object = args[0];
482+
NAPI_CALL(env, napi_object_freeze(env, object));
483+
484+
return object;
485+
}
486+
487+
static napi_value TestSeal(napi_env env,
488+
napi_callback_info info) {
489+
size_t argc = 1;
490+
napi_value args[1];
491+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
492+
493+
napi_value object = args[0];
494+
NAPI_CALL(env, napi_object_seal(env, object));
495+
496+
return object;
497+
}
498+
475499
// We create two type tags. They are basically 128-bit UUIDs.
476500
static const napi_type_tag type_tags[2] = {
477501
{ 0xdaf987b3cc62481a, 0xb745b0497f299531 },
@@ -532,6 +556,8 @@ napi_value Init(napi_env env, napi_value exports) {
532556
DECLARE_NAPI_PROPERTY("TypeTaggedInstance", TypeTaggedInstance),
533557
DECLARE_NAPI_PROPERTY("CheckTypeTag", CheckTypeTag),
534558
DECLARE_NAPI_PROPERTY("TestGetProperty", TestGetProperty),
559+
DECLARE_NAPI_PROPERTY("TestFreeze", TestFreeze),
560+
DECLARE_NAPI_PROPERTY("TestSeal", TestSeal),
535561
};
536562

537563
init_test_null(env, exports);

0 commit comments

Comments
 (0)