[njs] Fixed prototype mutation for object literals.

Dmitry Volyntsev xeioex at nginx.com
Tue Aug 20 17:51:53 UTC 2019


details:   https://hg.nginx.org/njs/rev/13161c0357e4
branches:  
changeset: 1137:13161c0357e4
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Aug 20 20:51:39 2019 +0300
description:
Fixed prototype mutation for object literals.

diffstat:

 src/njs_disassembler.c    |   2 +
 src/njs_generator.c       |  13 ++++++-
 src/njs_lexer.h           |   1 +
 src/njs_object_hash.h     |  13 +++++++
 src/njs_parser_terminal.c |  41 +++++++++++++++++++---
 src/njs_vmcode.c          |  84 +++++++++++++++++++++++++++++++---------------
 src/njs_vmcode.h          |   1 +
 src/test/njs_unit_test.c  |  27 +++++++++++++++
 8 files changed, 146 insertions(+), 36 deletions(-)

diffs (367 lines):

diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_disassembler.c
--- a/src/njs_disassembler.c	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_disassembler.c	Tue Aug 20 20:51:39 2019 +0300
@@ -39,6 +39,8 @@ static njs_code_name_t  code_names[] = {
           njs_str("PROP GET        ") },
     { NJS_VMCODE_PROPERTY_INIT, sizeof(njs_vmcode_prop_set_t),
           njs_str("PROP INIT       ") },
+    { NJS_VMCODE_PROTO_INIT, sizeof(njs_vmcode_prop_set_t),
+          njs_str("PROTO INIT      ") },
     { NJS_VMCODE_PROPERTY_SET, sizeof(njs_vmcode_prop_set_t),
           njs_str("PROP SET        ") },
     { NJS_VMCODE_PROPERTY_IN, sizeof(njs_vmcode_3addr_t),
diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_generator.c
--- a/src/njs_generator.c	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_generator.c	Tue Aug 20 20:51:39 2019 +0300
@@ -1741,10 +1741,19 @@ njs_generate_assignment(njs_vm_t *vm, nj
         return ret;
     }
 
-    if (lvalue->token == NJS_TOKEN_PROPERTY_INIT) {
+    switch (lvalue->token) {
+    case NJS_TOKEN_PROPERTY_INIT:
         njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
                           NJS_VMCODE_PROPERTY_INIT, 3);
-    } else {
+        break;
+
+    case NJS_TOKEN_PROTO_INIT:
+        njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
+                          NJS_VMCODE_PROTO_INIT, 3);
+        break;
+
+    default:
+        /* NJS_VMCODE_PROPERTY_SET */
         njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
                           NJS_VMCODE_PROPERTY_SET, 3);
     }
diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_lexer.h
--- a/src/njs_lexer.h	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_lexer.h	Tue Aug 20 20:51:39 2019 +0300
@@ -127,6 +127,7 @@ typedef enum {
     NJS_TOKEN_PROPERTY_DELETE,
     NJS_TOKEN_PROPERTY_GETTER,
     NJS_TOKEN_PROPERTY_SETTER,
+    NJS_TOKEN_PROTO_INIT,
 
     NJS_TOKEN_ARRAY,
 
diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_object_hash.h
--- a/src/njs_object_hash.h	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_object_hash.h	Tue Aug 20 20:51:39 2019 +0300
@@ -8,6 +8,19 @@
 #define _NJS_OBJECT_HASH_H_INCLUDED_
 
 
+#define NJS___PROTO___HASH                                                    \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        '_'), '_'), 'p'), 'r'), 'o'), 't'), 'o'), '_'), '_')
+
+
 #define NJS_ARGV_HASH                                                         \
     njs_djb_hash_add(                                                         \
     njs_djb_hash_add(                                                         \
diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_parser_terminal.c
--- a/src/njs_parser_terminal.c	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_parser_terminal.c	Tue Aug 20 20:51:39 2019 +0300
@@ -18,7 +18,7 @@ static njs_token_t njs_parser_object(njs
     njs_parser_node_t *obj);
 static njs_int_t njs_parser_object_property(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *parent, njs_parser_node_t *property,
-    njs_parser_node_t *value);
+    njs_parser_node_t *value, njs_bool_t proto_init);
 static njs_int_t njs_parser_property_accessor(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *parent,
     njs_parser_node_t *property, njs_parser_node_t *value,
@@ -475,18 +475,22 @@ static njs_token_t
 njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
 {
     uint32_t               hash, token_line;
-    njs_int_t              ret;
+    njs_int_t              ret, __proto__;
     njs_str_t              name;
+    njs_bool_t             computed, proto_init;
     njs_token_t            token, accessor;
     njs_lexer_t            *lexer;
     njs_parser_node_t      *object, *property, *expression;
     njs_function_lambda_t  *lambda;
 
+    const njs_str_t proto_string = njs_str("__proto__");
+
     lexer = parser->lexer;
 
     /* GCC and Clang complain about uninitialized values. */
     hash = 0;
     token_line = 0;
+    __proto__ = 0;
     property = NULL;
 
     object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
@@ -503,6 +507,8 @@ njs_parser_object(njs_vm_t *vm, njs_pars
         }
 
         accessor = 0;
+        computed = 0;
+        proto_init = 0;
         njs_memzero(&name, sizeof(njs_str_t));
 
         if (token == NJS_TOKEN_NAME || lexer->keyword) {
@@ -558,6 +564,9 @@ njs_parser_object(njs_vm_t *vm, njs_pars
 
             token = njs_parser_match(vm, parser, token,
                                      NJS_TOKEN_CLOSE_BRACKET);
+
+            computed = 1;
+
             break;
 
         case NJS_TOKEN_NUMBER:
@@ -657,6 +666,23 @@ njs_parser_object(njs_vm_t *vm, njs_pars
                 break;
 
             case NJS_TOKEN_COLON:
+                if (!computed) {
+                    if (njs_is_string(&property->u.value)) {
+                        njs_string_get(&property->u.value, &name);
+                    }
+
+                    if (njs_slow_path(njs_strstr_eq(&name, &proto_string))) {
+                        if (++__proto__ > 1) {
+                            njs_parser_syntax_error(vm, parser,
+                                "Duplicate __proto__ fields are not allowed "
+                                "in object literals");
+                            return NJS_TOKEN_ILLEGAL;
+                        }
+
+                        proto_init = 1;
+                    }
+                }
+
                 token = njs_parser_token(vm, parser);
                 if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
                     return token;
@@ -699,7 +725,7 @@ njs_parser_object(njs_vm_t *vm, njs_pars
             }
 
             ret = njs_parser_object_property(vm, parser, obj, property,
-                                             expression);
+                                             expression, proto_init);
             if (njs_slow_path(ret != NJS_OK)) {
                 return NJS_TOKEN_ERROR;
             }
@@ -725,8 +751,9 @@ done:
 static njs_int_t
 njs_parser_object_property(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *parent, njs_parser_node_t *property,
-    njs_parser_node_t *value)
+    njs_parser_node_t *value, njs_bool_t proto_init)
 {
+    njs_token_t        token;
     njs_parser_node_t  *stmt, *assign, *object, *propref;
 
     object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
@@ -736,7 +763,9 @@ njs_parser_object_property(njs_vm_t *vm,
 
     object->u.object = parent;
 
-    propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY_INIT);
+    token = proto_init ? NJS_TOKEN_PROTO_INIT : NJS_TOKEN_PROPERTY_INIT;
+
+    propref = njs_parser_node_new(vm, parser, token);
     if (njs_slow_path(propref == NULL)) {
         return NJS_ERROR;
     }
@@ -870,7 +899,7 @@ njs_parser_array_item(njs_vm_t *vm, njs_
 
     njs_set_number(&number->u.value, array->u.length);
 
-    ret = njs_parser_object_property(vm, parser, array, number, value);
+    ret = njs_parser_object_property(vm, parser, array, number, value, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_vmcode.c
--- a/src/njs_vmcode.c	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_vmcode.c	Tue Aug 20 20:51:39 2019 +0300
@@ -23,8 +23,10 @@ static njs_jump_off_t njs_vmcode_templat
 static njs_jump_off_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *invld);
 
-static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm,
-    njs_value_t *value, njs_value_t *key, njs_value_t *retval);
+static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *key, njs_value_t *retval);
+static njs_jump_off_t njs_vmcode_proto_init(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *key, njs_value_t *retval);
 static njs_jump_off_t njs_vmcode_property_in(njs_vm_t *vm,
     njs_value_t *value, njs_value_t *key);
 static njs_jump_off_t njs_vmcode_property_delete(njs_vm_t *vm,
@@ -786,6 +788,16 @@ next:
 
                 break;
 
+            case NJS_VMCODE_PROTO_INIT:
+                set = (njs_vmcode_prop_set_t *) pc;
+                retval = njs_vmcode_operand(vm, set->value);
+                ret = njs_vmcode_proto_init(vm, value1, value2, retval);
+                if (njs_slow_path(ret == NJS_ERROR)) {
+                    goto error;
+                }
+
+                break;
+
             case NJS_VMCODE_TRY_START:
                 ret = njs_vmcode_try_start(vm, value1, value2, pc);
                 if (njs_slow_path(ret == NJS_ERROR)) {
@@ -1106,7 +1118,6 @@ njs_vmcode_property_init(njs_vm_t *vm, n
     uint32_t            index, size;
     njs_array_t         *array;
     njs_value_t         *val, name;
-    njs_object_t        *obj;
     njs_jump_off_t      ret;
     njs_object_prop_t   *prop;
     njs_lvlhsh_query_t  lhq;
@@ -1157,30 +1168,6 @@ njs_vmcode_property_init(njs_vm_t *vm, n
         lhq.proto = &njs_object_hash_proto;
         lhq.pool = vm->mem_pool;
 
-        obj = njs_object(value);
-
-        if (obj->__proto__ != NULL) {
-            /* obj->__proto__ can be NULL after __proto__: null assignment */
-            ret = njs_lvlhsh_find(&obj->__proto__->shared_hash, &lhq);
-            if (ret == NJS_OK) {
-                prop = lhq.value;
-
-                if (prop->type == NJS_PROPERTY_HANDLER) {
-                    ret = prop->value.data.u.prop_handler(vm, value, init,
-                                                          &vm->retval);
-                    if (njs_slow_path(ret == NJS_ERROR)) {
-                        return ret;
-                    }
-
-                    if (ret == NJS_OK) {
-                        break;
-                    }
-
-                    /* NJS_DECLINED */
-                }
-            }
-        }
-
         prop = njs_object_prop_alloc(vm, &name, init, 1);
         if (njs_slow_path(prop == NULL)) {
             return NJS_ERROR;
@@ -1189,7 +1176,7 @@ njs_vmcode_property_init(njs_vm_t *vm, n
         lhq.value = prop;
         lhq.replace = 1;
 
-        ret = njs_lvlhsh_insert(&obj->hash, &lhq);
+        ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq);
         if (njs_slow_path(ret != NJS_OK)) {
             njs_internal_error(vm, "lvlhsh insert/replace failed");
             return NJS_ERROR;
@@ -1210,6 +1197,47 @@ njs_vmcode_property_init(njs_vm_t *vm, n
 
 
 static njs_jump_off_t
+njs_vmcode_proto_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *unused,
+    njs_value_t *init)
+{
+    njs_object_t        *obj;
+    njs_jump_off_t      ret;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
+
+    lhq.key = njs_str_value("__proto__");
+    lhq.key_hash = NJS___PROTO___HASH;
+    lhq.proto = &njs_object_hash_proto;
+    lhq.pool = vm->mem_pool;
+
+    obj = njs_object(value);
+
+    ret = njs_lvlhsh_find(&obj->__proto__->shared_hash, &lhq);
+    if (njs_slow_path(ret != NJS_OK)) {
+        goto fail;
+    }
+
+    prop = lhq.value;
+
+    if (prop->type != NJS_PROPERTY_HANDLER) {
+        goto fail;
+    }
+
+    ret = prop->value.data.u.prop_handler(vm, value, init, &vm->retval);
+    if (njs_slow_path(ret != NJS_OK)) {
+        goto fail;
+    }
+
+    return sizeof(njs_vmcode_prop_set_t);
+
+fail:
+
+    njs_internal_error(vm, "\"__proto__\" init failed");
+    return NJS_ERROR;
+}
+
+
+static njs_jump_off_t
 njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *key)
 {
     njs_int_t             ret;
diff -r 7b1a00168b25 -r 13161c0357e4 src/njs_vmcode.h
--- a/src/njs_vmcode.h	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/njs_vmcode.h	Tue Aug 20 20:51:39 2019 +0300
@@ -52,6 +52,7 @@ typedef uint8_t                         
 #define NJS_VMCODE_PROPERTY_NEXT        VMCODE0(16)
 #define NJS_VMCODE_THIS                 VMCODE0(17)
 #define NJS_VMCODE_ARGUMENTS            VMCODE0(18)
+#define NJS_VMCODE_PROTO_INIT           VMCODE0(19)
 
 #define NJS_VMCODE_TRY_START            VMCODE0(32)
 #define NJS_VMCODE_THROW                VMCODE0(33)
diff -r 7b1a00168b25 -r 13161c0357e4 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Tue Aug 20 20:51:39 2019 +0300
+++ b/src/test/njs_unit_test.c	Tue Aug 20 20:51:39 2019 +0300
@@ -8810,6 +8810,33 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var o = {__proto__: Array.prototype, length:3}; o.fill('a')[2]"),
       njs_str("a") },
 
+    { njs_str("({__proto__:null, __proto__: null})"),
+      njs_str("SyntaxError: Duplicate __proto__ fields are not allowed in object literals in 1") },
+
+    { njs_str("({__proto__:null, '__proto__': null})"),
+      njs_str("SyntaxError: Duplicate __proto__ fields are not allowed in object literals in 1") },
+
+    { njs_str("({__proto__:null, '\\x5f_proto__': null})"),
+      njs_str("SyntaxError: Duplicate __proto__ fields are not allowed in object literals in 1") },
+
+    { njs_str("var __proto__ = 'a'; ({__proto__}).__proto__"),
+      njs_str("a") },
+
+    { njs_str("var __proto__ = 'a'; ({__proto__, '__proto__':'b'}).__proto__"),
+      njs_str("a") },
+
+    { njs_str("var __proto__ = 'a'; ({__proto__:null, __proto__}).__proto__"),
+      njs_str("a") },
+
+    { njs_str("({__proto__:null, ['__proto__']:'a'}).__proto__"),
+      njs_str("a") },
+
+    { njs_str("({__proto__() { return 123; }}).__proto__()"),
+      njs_str("123") },
+
+    { njs_str("({['__prot' + 'o__']: 123}).__proto__"),
+      njs_str("123") },
+
     { njs_str("({}).__proto__.constructor === Object"),
       njs_str("true") },
 


More information about the nginx-devel mailing list