[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