[njs] Fixed setting of object properties.

Valentin Bartenev vbart at nginx.com
Thu May 23 12:54:47 UTC 2019


details:   https://hg.nginx.org/njs/rev/ff591ba3e780
branches:  
changeset: 979:ff591ba3e780
user:      Valentin Bartenev <vbart at nginx.com>
date:      Thu May 23 15:05:50 2019 +0300
description:
Fixed setting of object properties.

Now writability of prototype properties is properly taken into account.

diffstat:

 njs/njs_disassembler.c    |    2 +
 njs/njs_generator.c       |   12 +-
 njs/njs_lexer.h           |    1 +
 njs/njs_object.c          |   48 ++-------
 njs/njs_object.h          |    2 +
 njs/njs_parser_terminal.c |    2 +-
 njs/njs_vm.c              |  240 +++++++++++++++++++++++++++++++++++----------
 njs/njs_vm.h              |    2 +
 njs/test/njs_unit_test.c  |   70 +++++++++++--
 9 files changed, 269 insertions(+), 110 deletions(-)

diffs (697 lines):

diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_disassembler.c
--- a/njs/njs_disassembler.c	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_disassembler.c	Thu May 23 15:05:50 2019 +0300
@@ -36,6 +36,8 @@ static njs_code_name_t  code_names[] = {
 
     { njs_vmcode_property_get, sizeof(njs_vmcode_prop_get_t),
           nxt_string("PROPERTY GET    ") },
+    { njs_vmcode_property_init, sizeof(njs_vmcode_prop_set_t),
+          nxt_string("PROPERTY INIT   ") },
     { njs_vmcode_property_set, sizeof(njs_vmcode_prop_set_t),
           nxt_string("PROPERTY SET    ") },
     { njs_vmcode_property_in, sizeof(njs_vmcode_3addr_t),
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_generator.c
--- a/njs/njs_generator.c	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_generator.c	Thu May 23 15:05:50 2019 +0300
@@ -1682,7 +1682,7 @@ njs_generate_assignment(njs_vm_t *vm, nj
         return NXT_OK;
     }
 
-    /* lvalue->token == NJS_TOKEN_PROPERTY */
+    /* lvalue->token == NJS_TOKEN_PROPERTY(_INIT) */
 
     /* Object. */
 
@@ -1735,8 +1735,14 @@ njs_generate_assignment(njs_vm_t *vm, nj
         return ret;
     }
 
-    njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
-                      njs_vmcode_property_set, 3, 0);
+    if (lvalue->token == NJS_TOKEN_PROPERTY_INIT) {
+        njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
+                          njs_vmcode_property_init, 3, 0);
+    } else {
+        njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
+                          njs_vmcode_property_set, 3, 0);
+    }
+
     prop_set->value = expr->index;
     prop_set->object = object->index;
     prop_set->property = property->index;
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_lexer.h
--- a/njs/njs_lexer.h	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_lexer.h	Thu May 23 15:05:50 2019 +0300
@@ -123,6 +123,7 @@ typedef enum {
     NJS_TOKEN_OBJECT,
     NJS_TOKEN_OBJECT_VALUE,
     NJS_TOKEN_PROPERTY,
+    NJS_TOKEN_PROPERTY_INIT,
     NJS_TOKEN_PROPERTY_DELETE,
 
     NJS_TOKEN_ARRAY,
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_object.c
--- a/njs/njs_object.c	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_object.c	Thu May 23 15:05:50 2019 +0300
@@ -22,8 +22,6 @@ static njs_ret_t njs_external_property_s
     njs_value_t *setval, njs_value_t *retval);
 static njs_ret_t njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *setval, njs_value_t *retval);
-static njs_ret_t njs_object_query_prop_handler(njs_property_query_t *pq,
-    njs_object_t *object);
 static njs_ret_t njs_define_property(njs_vm_t *vm, njs_value_t *object,
     const njs_value_t *name, const njs_object_t *descriptor);
 
@@ -379,6 +377,7 @@ njs_property_query(njs_vm_t *vm, njs_pro
         pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
 
         if (obj == NULL) {
+            pq->own = 1;
             return njs_external_property_query(vm, pq, object);
         }
 
@@ -395,6 +394,7 @@ njs_object_property_query(njs_vm_t *vm, 
 {
     uint32_t            index;
     njs_ret_t           ret;
+    nxt_bool_t          own;
     njs_array_t         *array;
     njs_object_t        *proto;
     njs_object_prop_t   *prop;
@@ -402,12 +402,8 @@ njs_object_property_query(njs_vm_t *vm, 
 
     pq->lhq.proto = &njs_object_hash_proto;
 
-    if (pq->query == NJS_PROPERTY_QUERY_SET) {
-        ret = njs_object_query_prop_handler(pq, object);
-        if (ret == NXT_OK) {
-            return ret;
-        }
-    }
+    own = pq->own;
+    pq->own = 1;
 
     proto = object;
 
@@ -450,6 +446,10 @@ njs_object_property_query(njs_vm_t *vm, 
                 return ret;
             }
 
+            if (pq->own) {
+                pq->own_whiteout = prop;
+            }
+
         } else {
             ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
 
@@ -460,10 +460,11 @@ njs_object_property_query(njs_vm_t *vm, 
             }
         }
 
-        if (pq->own || pq->query > NJS_PROPERTY_QUERY_GET) {
+        if (own) {
             return NXT_DECLINED;
         }
 
+        pq->own = 0;
         proto = proto->__proto__;
 
     } while (proto != NULL);
@@ -706,33 +707,6 @@ njs_external_property_delete(njs_vm_t *v
 }
 
 
-static njs_ret_t
-njs_object_query_prop_handler(njs_property_query_t *pq, njs_object_t *object)
-{
-    njs_ret_t          ret;
-    njs_object_prop_t  *prop;
-
-    do {
-        pq->prototype = object;
-
-        ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
-
-        if (ret == NXT_OK) {
-            prop = pq->lhq.value;
-
-            if (prop->type == NJS_PROPERTY_HANDLER) {
-                return NXT_OK;
-            }
-        }
-
-        object = object->__proto__;
-
-    } while (object != NULL);
-
-    return NXT_DECLINED;
-}
-
-
 njs_ret_t
 njs_method_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
 {
@@ -1862,7 +1836,7 @@ njs_define_property(njs_vm_t *vm, njs_va
     pq.lhq.key_hash = nxt_djb_hash(pq.lhq.key.start, pq.lhq.key.length);
     pq.lhq.proto = &njs_object_hash_proto;
 
-    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
+    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
 
     ret = njs_property_query(vm, &pq, object, name);
 
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_object.h
--- a/njs/njs_object.h	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_object.h	Thu May 23 15:05:50 2019 +0300
@@ -53,6 +53,7 @@ typedef struct {
 
     njs_value_t                 value;
     njs_object_t                *prototype;
+    njs_object_prop_t           *own_whiteout;
     uint8_t                     query;
     uint8_t                     shared;
     uint8_t                     own;
@@ -63,6 +64,7 @@ typedef struct {
     do {                                                                      \
         (pq)->lhq.key.length = 0;                                             \
         (pq)->lhq.value = NULL;                                               \
+        (pq)->own_whiteout = NULL;                                            \
         (pq)->query = _query;                                                 \
         (pq)->shared = 0;                                                     \
         (pq)->own = _own;                                                     \
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_parser_terminal.c
--- a/njs/njs_parser_terminal.c	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_parser_terminal.c	Thu May 23 15:05:50 2019 +0300
@@ -599,7 +599,7 @@ njs_parser_object_property(njs_vm_t *vm,
 
     object->u.object = parent;
 
-    propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY);
+    propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY_INIT);
     if (nxt_slow_path(propref == NULL)) {
         return NXT_ERROR;
     }
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_vm.c
--- a/njs/njs_vm.c	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_vm.c	Thu May 23 15:05:50 2019 +0300
@@ -545,12 +545,122 @@ njs_vmcode_property_get(njs_vm_t *vm, nj
 
 
 njs_ret_t
+njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *object,
+    njs_value_t *property)
+{
+    uint32_t               index, size;
+    njs_ret_t              ret;
+    njs_array_t            *array;
+    njs_value_t            *init, *value, name;
+    njs_object_t           *obj;
+    njs_object_prop_t      *prop;
+    nxt_lvlhsh_query_t     lhq;
+    njs_vmcode_prop_set_t  *code;
+
+    code = (njs_vmcode_prop_set_t *) vm->current;
+    init = njs_vmcode_operand(vm, code->value);
+
+    switch (object->type) {
+    case NJS_ARRAY:
+        index = njs_value_to_index(property);
+        if (nxt_slow_path(index == NJS_ARRAY_INVALID_INDEX)) {
+            njs_internal_error(vm,
+                               "invalid index while property initialization");
+            return NXT_ERROR;
+        }
+
+        array = object->data.u.array;
+
+        if (index >= array->length) {
+            size = index - array->length;
+
+            ret = njs_array_expand(vm, array, 0, size + 1);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
+            value = &array->start[array->length];
+
+            while (size != 0) {
+                njs_set_invalid(value);
+                value++;
+                size--;
+            }
+
+            array->length = index + 1;
+        }
+
+        /* GC: retain. */
+        array->start[index] = *init;
+
+        break;
+
+    case NJS_OBJECT:
+        ret = njs_primitive_value_to_string(vm, &name, property);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_internal_error(vm, "failed conversion of type \"%s\" "
+                               "to string while property initialization",
+                               njs_type_string(property->type));
+            return NXT_ERROR;
+        }
+
+        njs_string_get(&name, &lhq.key);
+        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+        lhq.proto = &njs_object_hash_proto;
+        lhq.pool = vm->mem_pool;
+
+        obj = object->data.u.object;
+
+        ret = nxt_lvlhsh_find(&obj->__proto__->shared_hash, &lhq);
+        if (ret == NXT_OK) {
+            prop = lhq.value;
+
+            if (prop->type == NJS_PROPERTY_HANDLER) {
+                ret = prop->value.data.u.prop_handler(vm, object, init,
+                                                      &vm->retval);
+                if (nxt_slow_path(ret != NXT_OK)) {
+                    return ret;
+                }
+
+                break;
+            }
+        }
+
+        prop = njs_object_prop_alloc(vm, &name, init, 1);
+        if (nxt_slow_path(prop == NULL)) {
+            return NXT_ERROR;
+        }
+
+        lhq.value = prop;
+        lhq.replace = 1;
+
+        ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_internal_error(vm, "lvlhsh insert/replace failed");
+            return NXT_ERROR;
+        }
+
+        break;
+
+    default:
+        njs_internal_error(vm, "unexpected object type \"%s\" "
+                           "while property initialization",
+                           njs_type_string(object->type));
+
+        return NXT_ERROR;
+    }
+
+    return sizeof(njs_vmcode_prop_set_t);
+}
+
+
+njs_ret_t
 njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *property)
 {
     njs_ret_t              ret;
     njs_value_t            *value;
-    njs_object_prop_t      *prop;
+    njs_object_prop_t      *prop, *shared;
     njs_property_query_t   pq;
     njs_vmcode_prop_set_t  *code;
 
@@ -563,6 +673,8 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
     code = (njs_vmcode_prop_set_t *) vm->current;
     value = njs_vmcode_operand(vm, code->value);
 
+    shared = NULL;
+
     njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
 
     ret = njs_property_query(vm, &pq, object, property);
@@ -572,70 +684,62 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
     case NXT_OK:
         prop = pq.lhq.value;
 
-        switch (prop->type) {
-        case NJS_PROPERTY:
-            break;
-
-        case NJS_PROPERTY_REF:
-            *prop->value.data.u.value = *value;
+        if (nxt_slow_path(!prop->writable)) {
+            njs_type_error(vm,
+                           "Cannot assign to read-only property \"%V\" of %s",
+                           &pq.lhq.key, njs_type_string(object->type));
+            return NXT_ERROR;
+        }
+
+        if (prop->type == NJS_PROPERTY_HANDLER) {
+            ret = prop->value.data.u.prop_handler(vm, object, value,
+                                                  &vm->retval);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
             return sizeof(njs_vmcode_prop_set_t);
-
-        case NJS_PROPERTY_HANDLER:
-            if (prop->writable) {
-                ret = prop->value.data.u.prop_handler(vm, object, value,
-                                                      &vm->retval);
-                if (nxt_slow_path(ret != NXT_OK)) {
-                    return ret;
+        }
+
+        if (pq.own) {
+            switch (prop->type) {
+            case NJS_PROPERTY:
+            case NJS_METHOD:
+                if (nxt_slow_path(pq.shared)) {
+                    shared = prop;
+                    break;
                 }
 
+                goto found;
+
+            case NJS_PROPERTY_REF:
+                *prop->value.data.u.value = *value;
                 return sizeof(njs_vmcode_prop_set_t);
+
+            default:
+                njs_internal_error(vm, "unexpected property type \"%s\" "
+                                   "while setting",
+                                   njs_prop_type_string(prop->type));
+
+                return NXT_ERROR;
             }
 
             break;
-
-        default:
-            njs_internal_error(vm, "unexpected property type \"%s\" "
-                               "while setting",
-                               njs_prop_type_string(prop->type));
-
-            return NXT_ERROR;
         }
 
-        break;
+        /* Fall through. */
 
     case NXT_DECLINED:
-        if (nxt_slow_path(!object->data.u.object->extensible)) {
-            njs_type_error(vm, "Cannot add property \"%V\", "
-                           "object is not extensible", &pq.lhq.key);
-            return NXT_ERROR;
-        }
-
-        if (nxt_slow_path(pq.lhq.value != NULL)) {
-            prop = pq.lhq.value;
-
-            if (nxt_slow_path(prop->type == NJS_WHITEOUT)) {
-                /* Previously deleted property.  */
-                prop->type = NJS_PROPERTY;
-                prop->enumerable = 1;
-                prop->configurable = 1;
-                prop->writable = 1;
-                break;
-            }
-        }
-
-        prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_undefined, 1);
-        if (nxt_slow_path(prop == NULL)) {
-            return NXT_ERROR;
-        }
-
-        pq.lhq.replace = 0;
-        pq.lhq.value = prop;
-        pq.lhq.pool = vm->mem_pool;
-
-        ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            njs_internal_error(vm, "lvlhsh insert failed");
-            return NXT_ERROR;
+        if (nxt_slow_path(pq.own_whiteout != NULL)) {
+            /* Previously deleted property. */
+            prop = pq.own_whiteout;
+
+            prop->type = NJS_PROPERTY;
+            prop->enumerable = 1;
+            prop->configurable = 1;
+            prop->writable = 1;
+
+            goto found;
         }
 
         break;
@@ -647,12 +751,34 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
         return ret;
     }
 
-    if (nxt_slow_path(!prop->writable)) {
-        njs_type_error(vm, "Cannot assign to read-only property \"%V\" of %s",
-                       &pq.lhq.key, njs_type_string(object->type));
+    if (nxt_slow_path(!object->data.u.object->extensible)) {
+        njs_type_error(vm, "Cannot add property \"%V\", "
+                       "object is not extensible", &pq.lhq.key);
+        return NXT_ERROR;
+    }
+
+    prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_undefined, 1);
+    if (nxt_slow_path(prop == NULL)) {
         return NXT_ERROR;
     }
 
+    if (nxt_slow_path(shared != NULL)) {
+        prop->enumerable = shared->enumerable;
+        prop->configurable = shared->configurable;
+    }
+
+    pq.lhq.replace = 0;
+    pq.lhq.value = prop;
+    pq.lhq.pool = vm->mem_pool;
+
+    ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+found:
+
     prop->value = *value;
 
     return sizeof(njs_vmcode_prop_set_t);
diff -r 88c5787352b2 -r ff591ba3e780 njs/njs_vm.h
--- a/njs/njs_vm.h	Wed May 22 21:01:39 2019 +0300
+++ b/njs/njs_vm.h	Thu May 23 15:05:50 2019 +0300
@@ -1194,6 +1194,8 @@ njs_ret_t njs_vmcode_object_copy(njs_vm_
 
 njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *property);
+njs_ret_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *object,
+    njs_value_t *property);
 njs_ret_t njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *property);
 njs_ret_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property,
diff -r 88c5787352b2 -r ff591ba3e780 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Wed May 22 21:01:39 2019 +0300
+++ b/njs/test/njs_unit_test.c	Thu May 23 15:05:50 2019 +0300
@@ -3408,6 +3408,10 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.create(['α','β'])[false]"),
       nxt_string("undefined") },
 
+    { nxt_string("var a = ['abc']; var o = Object.create(a); o[0] = 32;"
+                 "[a,o[0]]"),
+      nxt_string("abc,32") },
+
     /* Array.length setter */
 
     { nxt_string("[].length = {}"),
@@ -3481,6 +3485,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = []; a.concat([])"),
       nxt_string("") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("var s = { toString: function() { return 'S' } };"
                  "var v = { toString: 8, valueOf: function() { return 'V' } };"
                  "var o = [9]; o.join = function() { return 'O' };"
@@ -3495,7 +3500,6 @@ static njs_unit_test_t  njs_test[] =
 
     /* Array.toString(). */
 
-# if 0
     { nxt_string("var a = [1,2,3]; a.join = 'NO';"
                  "Object.prototype.toString = function () { return 'A' }; a"),
       nxt_string("[object Array]") },
@@ -6162,9 +6166,11 @@ static njs_unit_test_t  njs_test[] =
                     "(function(){(function(){(function(){})})})})})})})"),
       nxt_string("SyntaxError: The maximum function nesting level is \"5\" in 1") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("Function.prototype.toString = function () {return 'X'};"
                  "eval"),
       nxt_string("X") },
+#endif
 
     { nxt_string("var o = {f:function(x){ return x**2}}; o.f\n(2)"),
       nxt_string("4") },
@@ -7128,6 +7134,9 @@ static njs_unit_test_t  njs_test[] =
      { nxt_string("var f = (a,b) => 0; f.length"),
        nxt_string("2") },
 
+    { nxt_string("var o = Object.create(f => 1); o.length = 3"),
+      nxt_string("TypeError: Cannot assign to read-only property \"length\" of object") },
+
     /* Scopes. */
 
     { nxt_string("function f(x) { a = x } var a; f(5); a"),
@@ -7420,6 +7429,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Error('e')"),
       nxt_string("Error: e") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("var e = Error('e'); e.name = 'E'; e"),
       nxt_string("E: e") },
 
@@ -7431,6 +7441,7 @@ static njs_unit_test_t  njs_test[] =
 
     { nxt_string("var e = Error(); e.name = ''; e.message = 'e'; e"),
       nxt_string("e") },
+#endif
 
     { nxt_string("Error('e').name + ': ' + Error('e').message"),
       nxt_string("Error: e") },
@@ -7504,6 +7515,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("URIError('e').name + ': ' + URIError('e').message"),
       nxt_string("URIError: e") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("var e = EvalError('e'); e.name = 'E'; e"),
       nxt_string("E: e") },
 
@@ -7529,6 +7541,7 @@ static njs_unit_test_t  njs_test[] =
 
     { nxt_string("var e = MemoryError('e'); e.name = 'E'"),
       nxt_string("TypeError: Cannot add property \"name\", object is not extensible") },
+#endif
 
     { nxt_string("EvalError.prototype.name"),
       nxt_string("EvalError") },
@@ -8279,17 +8292,6 @@ static njs_unit_test_t  njs_test[] =
                  "var a = []; for(var p in x) a.push(p); a"),
       nxt_string("a,b,0,1,2") },
 
-#if 0
-    /* TODO: No properties implementation for array type
-     * (enumerable, writable, configurable).
-     */
-
-    { nxt_string("var o = Object("abc"); var x = Object.create(o);"
-                 "x['sd'] = 44; x[1] = 8; x[55] = 8;"
-                 "Object.keys(x)"),
-      nxt_string("55,sd") },
-#endif
-
     { nxt_string("Object.prototype.toString.call(Object.prototype)"),
       nxt_string("[object Object]") },
 
@@ -8405,11 +8407,21 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Array.prototype.length"),
       nxt_string("0") },
 
+    { nxt_string("Array.prototype.length = 3, Array.prototype"),
+      nxt_string(",,") },
+
+    { nxt_string("var o = Object.create(Array.prototype);"
+                 "Object.defineProperty(o, 'length', {value: 3});"
+                 "[Array.prototype, Array.prototype.length, o.length]"),
+      nxt_string(",0,3") },
+
     { nxt_string("Array.constructor === Function"),
       nxt_string("true") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("var a = []; a.join = 'OK'; a"),
       nxt_string("[object Array]") },
+#endif
 
     { nxt_string("[].__proto__ === Array.prototype"),
       nxt_string("true") },
@@ -8751,6 +8763,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var s = new String('αβ'); s[1] = 'b'"),
       nxt_string("TypeError: Cannot assign to read-only property \"1\" of object string") },
 
+    { nxt_string("var o = Object.create(new String('αβ')); o[1] = 'a'"),
+      nxt_string("TypeError: Cannot assign to read-only property \"1\" of object") },
+
     { nxt_string("var s = new String('αβ'); s[4] = 'ab'; s[4]"),
       nxt_string("ab") },
 
@@ -8828,6 +8843,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'test'.constructor.prototype === String.prototype"),
       nxt_string("true") },
 
+    { nxt_string("var o = Object.create(String.prototype); o.length = 1"),
+      nxt_string("TypeError: Cannot assign to read-only property \"length\" of object") },
+
     { nxt_string("Function.name"),
       nxt_string("Function") },
 
@@ -9127,6 +9145,30 @@ static njs_unit_test_t  njs_test[] =
                  "o.a = 1; o.a"),
       nxt_string("1") },
 
+    { nxt_string("Object.defineProperty(Object.prototype, 'a', {writable:false});"
+                 "var o = {a: 1}; [o.a++, o.a]"),
+      nxt_string("1,2") },
+
+    { nxt_string("var o = {};"
+                 "Object.defineProperty(Object.prototype, 'a', {writable:false});"
+                 "o.a = 1"),
+      nxt_string("TypeError: Cannot assign to read-only property \"a\" of object") },
+
+    { nxt_string("var o = {};"
+                 "Object.defineProperty(Object.prototype, 'a', {writable:true});"
+                 "o.a = 1; o.a"),
+      nxt_string("1") },
+
+    { nxt_string("var p = Object.create(Function);"
+                 "Object.defineProperty(p, 'length', {writable: true});"
+                 "p.length = 32; p.length"),
+      nxt_string("32") },
+
+    { nxt_string("var p = Object.create(Math.abs);"
+                 "Object.defineProperty(p, 'length', {writable: true});"
+                 "p.length = 23; p.length"),
+      nxt_string("23") },
+
     { nxt_string("var o = {};"
                  "Object.defineProperty(o, 'a', {value:1}); delete o.a"),
       nxt_string("TypeError: Cannot delete property \"a\" of object") },
@@ -9418,6 +9460,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Object.create(Math).hasOwnProperty('abs')"),
       nxt_string("false") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("var m = Object.create(Math); m.abs = 3;"
                  "[m.hasOwnProperty('abs'), m.abs]"),
       nxt_string("true,3") },
@@ -9425,6 +9468,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var m = Object.create(Math); m.abs = Math.floor;"
                  "[m.hasOwnProperty('abs'), delete m.abs, m.abs(-1)]"),
       nxt_string("true,true,1") },
+#endif
 
     { nxt_string("Object.getOwnPropertyDescriptor({a:1}, 'a').value"),
       nxt_string("1") },
@@ -11840,8 +11884,10 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("JSON.stringify(URIError('e'))"),
       nxt_string("{}") },
 
+#if 0  /* Most built-in methods and properties must be writable. */
     { nxt_string("var e = URIError('e'); e.name = 'E'; JSON.stringify(e)"),
       nxt_string("{\"name\":\"E\"}") },
+#endif
 
     { nxt_string("var e = URIError('e'); e.message = 'E'; JSON.stringify(e)"),
       nxt_string("{}") },


More information about the nginx-devel mailing list