#include <njs_main.h>
+typedef enum {
+ NJS_OBJECT_INTEGRITY_SEALED,
+ NJS_OBJECT_INTEGRITY_FROZEN,
+} njs_object_integrity_level_t;
+
+
static njs_int_t njs_object_hash_test(njs_lvlhsh_query_t *lhq, void *data);
static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin,
const njs_object_t *end, njs_lvlhsh_query_t *lhq);
}
+/* 7.3.15 SetIntegrityLevel */
+
static njs_int_t
-njs_object_freeze(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
+njs_object_set_integrity_level(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t level)
{
+ njs_int_t ret;
njs_value_t *value;
njs_lvlhsh_t *hash;
njs_object_t *object;
value = njs_arg(args, nargs, 1);
- if (!njs_is_object(value)) {
- njs_set_undefined(&vm->retval);
+ if (njs_slow_path(!njs_is_object(value))) {
+ vm->retval = *value;
return NJS_OK;
}
+ if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN
+ && njs_is_typed_array(value)
+ && njs_typed_array_length(njs_typed_array(value)) != 0))
+ {
+ njs_type_error(vm, "Cannot freeze array buffer views with elements");
+ return NJS_ERROR;
+ }
+
+ if (njs_is_fast_array(value)) {
+ ret = njs_array_convert_to_slow_array(vm, njs_array(value));
+ if (ret != NJS_OK) {
+ return ret;
+ }
+ }
+
object = njs_object(value);
object->extensible = 0;
break;
}
- if (!njs_is_accessor_descriptor(prop)) {
+ if (level == NJS_OBJECT_INTEGRITY_FROZEN
+ && !njs_is_accessor_descriptor(prop))
+ {
prop->writable = 0;
}
static njs_int_t
-njs_object_is_frozen(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
+njs_object_test_integrity_level(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t level)
{
njs_value_t *value;
njs_lvlhsh_t *hash;
value = njs_arg(args, nargs, 1);
- if (!njs_is_object(value)) {
+ if (njs_slow_path(!njs_is_object(value))) {
vm->retval = njs_value_true;
return NJS_OK;
}
retval = &njs_value_false;
object = njs_object(value);
- njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
-
- hash = &object->hash;
if (object->extensible) {
goto done;
}
- for ( ;; ) {
- prop = njs_lvlhsh_each(hash, &lhe);
-
- if (prop == NULL) {
- break;
- }
-
- if (prop->configurable) {
- goto done;
- }
-
- if (njs_is_data_descriptor(prop) && prop->writable) {
- goto done;
- }
- }
-
- retval = &njs_value_true;
-
-done:
-
- vm->retval = *retval;
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_object_seal(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
-{
- njs_value_t *value;
- njs_lvlhsh_t *hash;
- njs_object_t *object;
- njs_object_prop_t *prop;
- njs_lvlhsh_each_t lhe;
-
- value = njs_arg(args, nargs, 1);
-
- if (!njs_is_object(value)) {
- vm->retval = *value;
- return NJS_OK;
+ if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN)
+ && njs_is_typed_array(value)
+ && njs_typed_array_length(njs_typed_array(value)) != 0)
+ {
+ goto done;
}
- object = njs_object(value);
- object->extensible = 0;
-
njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
hash = &object->hash;
break;
}
- prop->configurable = 0;
- }
-
- vm->retval = *value;
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_object_is_sealed(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
-{
- njs_value_t *value;
- njs_lvlhsh_t *hash;
- njs_object_t *object;
- njs_object_prop_t *prop;
- njs_lvlhsh_each_t lhe;
- const njs_value_t *retval;
-
- value = njs_arg(args, nargs, 1);
-
- if (!njs_is_object(value)) {
- vm->retval = njs_value_true;
- return NJS_OK;
- }
-
- retval = &njs_value_false;
-
- object = njs_object(value);
- njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
-
- hash = &object->hash;
-
- if (object->extensible) {
- goto done;
- }
-
- for ( ;; ) {
- prop = njs_lvlhsh_each(hash, &lhe);
-
- if (prop == NULL) {
- break;
+ if (prop->configurable) {
+ goto done;
}
- if (prop->configurable) {
+ if (level == NJS_OBJECT_INTEGRITY_FROZEN
+ && njs_is_data_descriptor(prop) && prop->writable)
+ {
goto done;
}
}
{
.type = NJS_PROPERTY,
.name = njs_string("freeze"),
- .value = njs_native_function(njs_object_freeze, 1),
+ .value = njs_native_function2(njs_object_set_integrity_level,
+ 1, NJS_OBJECT_INTEGRITY_FROZEN),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("isFrozen"),
- .value = njs_native_function(njs_object_is_frozen, 1),
+ .value = njs_native_function2(njs_object_test_integrity_level,
+ 1, NJS_OBJECT_INTEGRITY_FROZEN),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("seal"),
- .value = njs_native_function(njs_object_seal, 1),
+ .value = njs_native_function2(njs_object_set_integrity_level,
+ 1, NJS_OBJECT_INTEGRITY_SEALED),
.writable = 1,
.configurable = 1,
},
{
.type = NJS_PROPERTY,
.name = njs_string("isSealed"),
- .value = njs_native_function(njs_object_is_sealed, 1),
+ .value = njs_native_function2(njs_object_test_integrity_level,
+ 1, NJS_OBJECT_INTEGRITY_SEALED),
.writable = 1,
.configurable = 1,
},
{ njs_str("Object.getOwnPropertyNames(Array.isArray)"),
njs_str("name,length") },
+ /* Object.freeze() */
+
+ { njs_str("[undefined, null, false, NaN, '', Symbol()]"
+ ".every((x) => Object.is(Object.freeze(x), x))"),
+ njs_str("true")
+ },
+
+ { njs_str("var buf = new ArrayBuffer(8);"
+ NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => {Object.freeze(new ctr([])); "
+ " Object.freeze(new ctr(buf, 8)); return true; })"),
+ njs_str("true")
+ },
+
+ { njs_str("var buf = new ArrayBuffer(8);"
+ NJS_TYPED_ARRAY_LIST
+ ".map((ctr) => { try { Object.freeze(new ctr(buf)); } catch(e) { return e; } })"
+ ".every((x) => x instanceof TypeError)"),
+ njs_str("true")
+ },
+
+ { njs_str("Object.freeze([1]).pop()"),
+ njs_str("TypeError: Cannot delete property \"0\" of array") },
+
+ { njs_str("var a = Object.freeze([1]); a[0] = 2;"),
+ njs_str("TypeError: Cannot assign to read-only property \"0\" of array") },
+
+ { njs_str("var a = Object.freeze([1]); a[1] = 2;"),
+ njs_str("TypeError: Cannot add property \"1\", object is not extensible") },
+
+ { njs_str("var a = Object.freeze([1,,3]); a[1] = 2;"),
+ njs_str("TypeError: Cannot add property \"1\", object is not extensible") },
+
+ { njs_str("var o = { a: 1 }; delete o.a; Object.freeze(o).a = 2;"),
+ njs_str("TypeError: Cannot add property \"a\", object is not extensible") },
+
{ njs_str("Object.defineProperty(Object.freeze({}), 'b', {})"),
njs_str("TypeError: Cannot add property \"b\", object is not extensible") },
"Object.getOwnPropertyDescriptor(o, 'x').writable"),
njs_str("undefined") },
- { njs_str("Object.isFrozen({a:1})"),
- njs_str("false") },
+ /* Object.isFrozen() */
- { njs_str("Object.isFrozen([1,2])"),
- njs_str("false") },
+ { njs_str("[undefined, null, false, NaN, '', Symbol()]"
+ ".every((x) => Object.isFrozen(x))"),
+ njs_str("true") },
- { njs_str("Object.isFrozen(function() {})"),
- njs_str("false") },
+ { njs_str("[[], {}]"
+ ".every((x) => Object.isFrozen(Object.preventExtensions(x)))"),
+ njs_str("true") },
- { njs_str("Object.isFrozen(new Date(''))"),
- njs_str("false") },
+ { njs_str(NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => !Object.isFrozen(new ctr([])))"),
+ njs_str("true") },
- { njs_str("Object.isFrozen(new RegExp(''))"),
- njs_str("false") },
+ { njs_str(NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => Object.isFrozen(Object.preventExtensions(new ctr([]))))"),
+ njs_str("true") },
- { njs_str("Object.isFrozen()"),
+ { njs_str(NJS_TYPED_ARRAY_LIST
+ ".map((ctr) => new ctr([]))"
+ ".map((x) => { x.broken = true; return x; })"
+ ".every((x) => !Object.isFrozen(Object.preventExtensions(x)))"),
+ njs_str("true") },
+
+ { njs_str("var buf = new ArrayBuffer(8);"
+ NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => !Object.isFrozen(Object.preventExtensions(new ctr(buf))))"),
njs_str("true") },
- { njs_str("Object.isFrozen(1)"),
+ { njs_str("[{a:1}, [1,2], function() {}, new Date(''), new RegExp('')]"
+ ".every((x) => !Object.isFrozen(x))"),
njs_str("true") },
- { njs_str("Object.isFrozen('')"),
+ { njs_str("Object.isFrozen()"),
njs_str("true") },
{ njs_str("Object.isFrozen(Object.defineProperties({}, {a:{value:1}}))"),
{ njs_str("var o = Object.freeze({a:1}); Object.isFrozen(o)"),
njs_str("true") },
- { njs_str("Object.isFrozen(undefined)"),
+ /* Object.seal() */
+
+ { njs_str("[undefined, null, false, NaN, '', Symbol()]"
+ ".every((x) => Object.is(Object.seal(x), x))"),
njs_str("true") },
+ { njs_str("Object.seal()"),
+ njs_str("undefined") },
+
+ { njs_str("Object.seal([1]).pop()"),
+ njs_str("TypeError: Cannot delete property \"0\" of array") },
+
+ { njs_str("var a = Object.seal([1]); a[0] = 2; a"),
+ njs_str("2") },
+
+ { njs_str("var a = Object.seal([1]); a[1] = 2;"),
+ njs_str("TypeError: Cannot add property \"1\", object is not extensible") },
+
+ { njs_str("var a = Object.seal([1,,3]); a[1] = 2;"),
+ njs_str("TypeError: Cannot add property \"1\", object is not extensible") },
+
+ { njs_str("var o = { a: 1 }; delete o.a; Object.seal(o).a = 2"),
+ njs_str("TypeError: Cannot add property \"a\", object is not extensible") },
+
{ njs_str("var o = Object.seal({a:1}); o.a = 2; o.a"),
njs_str("2") },
{ njs_str("var o = Object.seal({a:{b:1}}); o.a.b = 2; o.a.b"),
njs_str("2") },
- { njs_str("Object.seal()"),
- njs_str("undefined") },
-
- { njs_str("Object.seal(1)"),
- njs_str("1") },
-
- { njs_str("Object.seal('')"),
- njs_str("") },
-
- { njs_str("Object.seal(undefined)"),
- njs_str("undefined") },
+ /* Object.isSealed() */
- { njs_str("Object.isSealed({a:1})"),
- njs_str("false") },
+ { njs_str("[undefined, null, false, NaN, '', Symbol()]"
+ ".every((x) => Object.isSealed(x))"),
+ njs_str("true") },
- { njs_str("Object.isSealed([1,2])"),
- njs_str("false") },
+ { njs_str("[[], {}]"
+ ".every((x) => Object.isSealed(Object.preventExtensions(x)))"),
+ njs_str("true") },
- { njs_str("Object.isSealed(function() {})"),
- njs_str("false") },
+ { njs_str(NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => !Object.isSealed(new ctr([])))"),
+ njs_str("true") },
- { njs_str("Object.isSealed(new Date(''))"),
- njs_str("false") },
+ { njs_str(NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => Object.isSealed(Object.preventExtensions(new ctr([]))))"),
+ njs_str("true") },
- { njs_str("Object.isSealed(new RegExp(''))"),
- njs_str("false") },
+ { njs_str("var buf = new ArrayBuffer(8);"
+ NJS_TYPED_ARRAY_LIST
+ ".every((ctr) => Object.isSealed(Object.preventExtensions(new ctr(buf))))"),
+ njs_str("true") },
- { njs_str("Object.isSealed()"),
+ { njs_str("var buf = new ArrayBuffer(8);"
+ NJS_TYPED_ARRAY_LIST
+ ".map((ctr) => new ctr(buf))"
+ ".map((x) => { x.broken = true; return x; })"
+ ".every((x) => !Object.isSealed(Object.preventExtensions(x)))"),
njs_str("true") },
- { njs_str("Object.isSealed(1)"),
+ { njs_str("[{a:1}, [1,2], function() {}, new Date(''), new RegExp('')]"
+ ".every((x) => !Object.isSealed(x))"),
njs_str("true") },
- { njs_str("Object.isSealed('')"),
+ { njs_str("Object.isSealed()"),
njs_str("true") },
{ njs_str("Object.isSealed(Object.defineProperties({}, {a:{value:1}}))"),