From: Dmitry Volyntsev Date: Mon, 19 Jun 2017 11:39:56 +0000 (+0300) Subject: Object.freeze() method. X-Git-Tag: 0.1.11~13 X-Git-Url: http://git.kaiwu.me/sitemap.xml?a=commitdiff_plain;h=bbf41ed9ade3cc0bf55dfd7ee3d11deef02bfda5;p=njs.git Object.freeze() method. --- diff --git a/njs/njs_array.c b/njs/njs_array.c index 9cc980fd..0fa1a1a5 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -149,6 +149,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t length, uint32_t spare) array->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_ARRAY].object; array->object.type = NJS_ARRAY; array->object.shared = 0; + array->object.extensible = 1; array->size = size; array->length = length; @@ -1941,7 +1942,7 @@ njs_array_string_sort(njs_vm_t *vm, njs_value_t *args, static const njs_function_t njs_array_string_sort_function = { - .object.shared = 1, + .object = { .type = NJS_FUNCTION, .shared = 1, .extensible = 1 }, .native = 1, .continuation_size = NJS_CONTINUATION_SIZE, .args_types = { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_STRING_ARG }, diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 35a7f42d..58b64351 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -206,6 +206,7 @@ njs_builtin_objects_create(njs_vm_t *vm) } functions[i].object.shared = 1; + functions[i].object.extensible = 1; functions[i].native = 1; functions[i].args_offset = 1; functions[i].u.native = native_functions[i].native; @@ -236,6 +237,7 @@ njs_builtin_objects_create(njs_vm_t *vm) for (i = NJS_CONSTRUCTOR_OBJECT; i < NJS_CONSTRUCTOR_MAX; i++) { constructors[i].object.shared = 0; + constructors[i].object.extensible = 1; constructors[i].native = 1; constructors[i].ctor = 1; constructors[i].args_offset = 1; diff --git a/njs/njs_date.c b/njs/njs_date.c index 73c1a217..be3b9a12 100644 --- a/njs/njs_date.c +++ b/njs/njs_date.c @@ -154,6 +154,7 @@ njs_date_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, nxt_lvlhsh_init(&date->object.shared_hash); date->object.type = NJS_DATE; date->object.shared = 0; + date->object.extensible = 1; date->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_DATE].object; date->time = time; diff --git a/njs/njs_function.c b/njs/njs_function.c index 56a99ca8..88eafad4 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -44,6 +44,7 @@ njs_function_alloc(njs_vm_t *vm) function->object.shared_hash = vm->shared->function_prototype_hash; function->object.type = NJS_FUNCTION; function->object.shared = 1; + function->object.extensible = 1; function->args_offset = 1; function->u.lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool, @@ -635,6 +636,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object; function->object.shared = 0; + function->object.extensible = 1; if (nargs == 1) { args = (njs_value_t *) &njs_value_void; diff --git a/njs/njs_object.c b/njs/njs_object.c index c4163d4e..1c183146 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -43,6 +43,7 @@ njs_object_alloc(njs_vm_t *vm) object->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT].object; object->type = NJS_OBJECT; object->shared = 0; + object->extensible = 1; } return object; @@ -86,6 +87,7 @@ njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, nxt_uint_t type) nxt_lvlhsh_init(&ov->object.shared_hash); ov->object.type = njs_object_value_type(type); ov->object.shared = 0; + ov->object.extensible = 1; index = njs_primitive_prototype_index(type); ov->object.__proto__ = &vm->prototypes[index].object; @@ -416,6 +418,11 @@ njs_object_define_property(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, return NXT_ERROR; } + if (!args[1].data.u.object->extensible) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + ret = njs_define_property(vm, args[1].data.u.object, &args[2], args[3].data.u.object); @@ -444,6 +451,11 @@ njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, return NXT_ERROR; } + if (!args[1].data.u.object->extensible) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); object = args[1].data.u.object; @@ -704,6 +716,44 @@ njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, } +static njs_ret_t +njs_object_freeze(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_lvlhsh_t *hash; + njs_object_t *object; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + object = args[1].data.u.object; + object->extensible = 0; + + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + + hash = &object->hash; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + prop->writable = 0; + prop->configurable = 0; + } + + vm->retval = args[1]; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -881,6 +931,14 @@ static const njs_object_prop_t njs_object_constructor_properties[] = .value = njs_native_function(njs_object_get_prototype_of, 0, NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + + /* Object.freeze(). */ + { + .type = NJS_METHOD, + .name = njs_string("freeze"), + .value = njs_native_function(njs_object_freeze, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff --git a/njs/njs_regexp.c b/njs/njs_regexp.c index 35599005..8aa86a94 100644 --- a/njs/njs_regexp.c +++ b/njs/njs_regexp.c @@ -469,6 +469,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern) regexp->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_REGEXP].object; regexp->object.type = NJS_REGEXP; regexp->object.shared = 0; + regexp->object.extensible = 1; regexp->last_index = 0; regexp->pattern = pattern; } diff --git a/njs/njs_vm.c b/njs/njs_vm.c index fedbcde0..02498350 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -425,6 +425,7 @@ njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) function->u.lambda = lambda; function->object.shared_hash = vm->shared->function_prototype_hash; function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object; + function->object.extensible = 1; function->args_offset = 1; if (nesting != 0) { @@ -683,6 +684,10 @@ njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object, break; case NXT_DECLINED: + if (!object->data.u.object->extensible) { + return sizeof(njs_vmcode_prop_set_t); + } + prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_void, 1); if (nxt_slow_path(prop == NULL)) { return NXT_ERROR; diff --git a/njs/njs_vm.h b/njs/njs_vm.h index af97fb8e..5c6c89b0 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -208,7 +208,8 @@ struct njs_object_s { /* The type is used in constructor prototypes. */ njs_value_type_t type:8; - uint8_t shared; /* 1 bit */ + uint8_t shared; /* 1 bit */ + uint8_t extensible; /* 1 bit */ }; @@ -339,7 +340,9 @@ typedef union { .args_types = { __VA_ARGS__ }, \ .args_offset = 1, \ .u.native = _function, \ - .object = { .type = NJS_FUNCTION, .shared = 1 }, \ + .object = { .type = NJS_FUNCTION, \ + .shared = 1, \ + .extensible = 1 }, \ } \ } \ } diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 7f614bee..53280d01 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -6071,6 +6071,99 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.getOwnPropertyDescriptor(1, '0')"), nxt_string("TypeError") }, + { nxt_string("Object.defineProperty(Object.freeze({}), 'b', {})"), + nxt_string("TypeError") }, + + { nxt_string("Object.defineProperties(Object.freeze({}), {b:{}})"), + nxt_string("TypeError") }, + + { nxt_string("var o = Object.freeze({a:1}); o.a = 2; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.freeze({a:1}); delete o.a; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.freeze({a:1}); o.b = 1; o.b"), + nxt_string("undefined") }, + + { nxt_string("var o = Object.freeze(Object.create({a:1})); o.a = 2; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.freeze({a:{b:1}}); o.a.b = 2; o.a.b"), + nxt_string("2") }, + + { nxt_string("Object.defineProperty([1,2], 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var a = Object.freeze([1,2]);" + "Object.defineProperty(a, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var a = [1,2]; a.a = 1; Object.freeze(a);" + "delete a.a; a.a"), + nxt_string("1") }, + + { nxt_string("var a = [1,2]; a.a = 1; Object.freeze(a);" + "a.a = 2; a.a"), + nxt_string("1") }, + + { nxt_string("var a = Object.freeze([1,2]); a.a = 1; a.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty(function() {}, 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var f = Object.freeze(function() {});" + "Object.defineProperty(f, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var f = function() {}; f.a = 1; Object.freeze(f);" + "delete f.a; f.a"), + nxt_string("1") }, + + { nxt_string("var f = function() {}; f.a = 1; Object.freeze(f);" + "f.a = 2; f.a"), + nxt_string("1") }, + + { nxt_string("var f = Object.freeze(function() {}); f.a = 1; f.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty(new Date(''), 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var d = Object.freeze(new Date(''));" + "Object.defineProperty(d, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var d = new Date(''); d.a = 1; Object.freeze(d);" + "delete d.a; d.a"), + nxt_string("1") }, + + { nxt_string("var d = new Date(''); d.a = 1; Object.freeze(d);" + "d.a = 2; d.a"), + nxt_string("1") }, + + { nxt_string("var d = Object.freeze(new Date('')); d.a = 1; d.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty(new RegExp(''), 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var r = Object.freeze(new RegExp(''));" + "Object.defineProperty(r, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var r = new RegExp(''); r.a = 1; Object.freeze(r);" + "delete r.a; r.a"), + nxt_string("1") }, + + { nxt_string("var r = new RegExp(''); r.a = 1; Object.freeze(r);" + "r.a = 2; r.a"), + nxt_string("1") }, + + { nxt_string("var r = Object.freeze(new RegExp('')); r.a = 1; r.a"), + nxt_string("undefined") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") },