From: Artem S. Povalyukhin Date: Fri, 19 Feb 2021 17:27:44 +0000 (+0000) Subject: Fixed Object.freeze() and friends according to the specification. X-Git-Tag: 0.5.2~10 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=8761ae6e8593b30b88e580c5f038980daffb16e7;p=njs.git Fixed Object.freeze() and friends according to the specification. This fixes #340 and also closes #374 issues on Github. --- diff --git a/src/njs_object.c b/src/njs_object.c index e1a02665..b9fadddc 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -8,6 +8,12 @@ #include +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); @@ -1507,10 +1513,13 @@ njs_object_set_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +/* 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; @@ -1519,11 +1528,26 @@ njs_object_freeze(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 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; @@ -1538,7 +1562,9 @@ njs_object_freeze(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, break; } - if (!njs_is_accessor_descriptor(prop)) { + if (level == NJS_OBJECT_INTEGRITY_FROZEN + && !njs_is_accessor_descriptor(prop)) + { prop->writable = 0; } @@ -1552,8 +1578,8 @@ njs_object_freeze(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 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; @@ -1564,7 +1590,7 @@ njs_object_is_frozen(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 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; } @@ -1572,60 +1598,18 @@ njs_object_is_frozen(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 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; @@ -1637,52 +1621,13 @@ njs_object_seal(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, 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; } } @@ -2055,7 +2000,8 @@ static const njs_object_prop_t njs_object_constructor_properties[] = { .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, }, @@ -2063,7 +2009,8 @@ static const njs_object_prop_t njs_object_constructor_properties[] = { .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, }, @@ -2071,7 +2018,8 @@ static const njs_object_prop_t njs_object_constructor_properties[] = { .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, }, @@ -2079,7 +2027,8 @@ static const njs_object_prop_t njs_object_constructor_properties[] = { .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, }, diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index eae27127..23588d22 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -13932,6 +13932,42 @@ static njs_unit_test_t njs_test[] = { 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") }, @@ -14032,28 +14068,40 @@ static njs_unit_test_t njs_test[] = "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}}))"), @@ -14086,9 +14134,30 @@ static njs_unit_test_t njs_test[] = { 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") }, @@ -14104,40 +14173,41 @@ static njs_unit_test_t njs_test[] = { 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}}))"),