n-api: support for object freeze/seal

PR-URL: https://github.com/nodejs/node/pull/35359
Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
This commit is contained in:
Shelley Vohr 2020-09-25 22:11:13 -07:00
parent c208a20f9c
commit 19f14517c7
No known key found for this signature in database
GPG Key ID: F13993A75599653C
5 changed files with 153 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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>/);
}

View File

@ -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 },
@ -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);