Skip to content

Commit

Permalink
n-api: support for object freeze/seal
Browse files Browse the repository at this point in the history
PR-URL: nodejs#35359
Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
  • Loading branch information
codebytere authored and joesepi committed Oct 22, 2020
1 parent fd1d270 commit 87480a1
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 0 deletions.
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>/);
}
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

0 comments on commit 87480a1

Please sign in to comment.