[njs] Object.freeze() method.

Dmitry Volyntsev xeioex at nginx.com
Mon Jun 19 11:49:29 UTC 2017


details:   http://hg.nginx.org/njs/rev/824fbb7fcd35
branches:  
changeset: 366:824fbb7fcd35
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Jun 19 14:39:56 2017 +0300
description:
Object.freeze() method.

diffstat:

 njs/njs_array.c          |   3 +-
 njs/njs_builtin.c        |   2 +
 njs/njs_date.c           |   1 +
 njs/njs_function.c       |   2 +
 njs/njs_object.c         |  58 +++++++++++++++++++++++++++++
 njs/njs_regexp.c         |   1 +
 njs/njs_vm.c             |   5 ++
 njs/njs_vm.h             |   7 ++-
 njs/test/njs_unit_test.c |  93 ++++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 169 insertions(+), 3 deletions(-)

diffs (332 lines):

diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_array.c
--- a/njs/njs_array.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_array.c	Mon Jun 19 14:39:56 2017 +0300
@@ -149,6 +149,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t l
     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_
 
 
 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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_builtin.c
--- a/njs/njs_builtin.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_builtin.c	Mon Jun 19 14:39:56 2017 +0300
@@ -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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_date.c
--- a/njs/njs_date.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_date.c	Mon Jun 19 14:39:56 2017 +0300
@@ -154,6 +154,7 @@ njs_date_constructor(njs_vm_t *vm, njs_v
         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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_function.c
--- a/njs/njs_function.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_function.c	Mon Jun 19 14:39:56 2017 +0300
@@ -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
 
     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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_object.c
--- a/njs/njs_object.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_object.c	Mon Jun 19 14:39:56 2017 +0300
@@ -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, con
         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,
         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 *v
         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
 }
 
 
+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_obje
         .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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_regexp.c
--- a/njs/njs_regexp.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_regexp.c	Mon Jun 19 14:39:56 2017 +0300
@@ -469,6 +469,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regex
         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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_vm.c
--- a/njs/njs_vm.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_vm.c	Mon Jun 19 14:39:56 2017 +0300
@@ -425,6 +425,7 @@ njs_vmcode_function(njs_vm_t *vm, njs_va
     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, nj
         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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_vm.h
--- a/njs/njs_vm.h	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/njs_vm.h	Mon Jun 19 14:39:56 2017 +0300
@@ -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 -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Wed Jun 14 17:58:10 2017 +0300
+++ b/njs/test/njs_unit_test.c	Mon Jun 19 14:39:56 2017 +0300
@@ -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") },
 


More information about the nginx-devel mailing list