]> git.kaiwu.me - njs.git/commitdiff
Object.freeze() method.
authorDmitry Volyntsev <xeioex@nginx.com>
Mon, 19 Jun 2017 11:39:56 +0000 (14:39 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Mon, 19 Jun 2017 11:39:56 +0000 (14:39 +0300)
njs/njs_array.c
njs/njs_builtin.c
njs/njs_date.c
njs/njs_function.c
njs/njs_object.c
njs/njs_regexp.c
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c

index 9cc980fd373007d505a400d17be465a696278d09..0fa1a1a5e57c21581f27a7f1c0f3676b6c880966 100644 (file)
@@ -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 },
index 35a7f42dc3dfa3baeef6d0d153f4d03b6e9685b1..58b64351779c01b1b02c0715d266f841fab5b3f4 100644 (file)
@@ -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;
index 73c1a21778d023e1560375667254f1fdfeee7b21..be3b9a12e4023917fce609c352d4b4eef56989aa 100644 (file)
@@ -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;
index 56a99ca8445471cd0bc46e7fe1d1c0592c044cd2..88eafad4cbd16be197351ec0d246b88635cd5a1f 100644 (file)
@@ -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;
index c4163d4e61c18ee06f00973591e55bb8f46e08ba..1c1831467f545541f9f6fc9515556344cf2596f8 100644 (file)
@@ -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),
+    },
 };
 
 
index 35599005f3ed8f95f0829544f8ea8306aa7bcc5a..8aa86a9454454d556c6669ef1c7c72b5c335bdbe 100644 (file)
@@ -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;
     }
index fedbcde02f3dabe4baae328c01e59c8ff202d83b..02498350b0f7c29db3f805b00025f1f6f0cc916d 100644 (file)
@@ -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;
index af97fb8ea0559c3a2812d579ac2c5226c393e875..5c6c89b018b99f9d6bc1fcd87a7a76822d1fa2fa 100644 (file)
@@ -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 },                                    \
         }                                                                     \
     }                                                                         \
 }
index 7f614bee48329291db9e1e5a24ad011efdaf6a10..53280d0169044b28a605cd62a388ad086696be4c 100644 (file)
@@ -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") },