From: Dmitry Volyntsev Date: Tue, 20 Aug 2019 17:51:39 +0000 (+0300) Subject: Fixed prototype mutation for object literals. X-Git-Tag: 0.3.6~55 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=1ba0d5be434a3bc026236f94bcf9424a8db517ea;p=njs.git Fixed prototype mutation for object literals. --- diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c index 4d60ff0b..ed02285d 100644 --- a/src/njs_disassembler.c +++ b/src/njs_disassembler.c @@ -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 --git a/src/njs_generator.c b/src/njs_generator.c index 7a2a6172..11bbcf0f 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -1741,10 +1741,19 @@ njs_generate_assignment(njs_vm_t *vm, njs_generator_t *generator, 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 --git a/src/njs_lexer.h b/src/njs_lexer.h index 5222e292..52f0e02a 100644 --- a/src/njs_lexer.h +++ b/src/njs_lexer.h @@ -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 --git a/src/njs_object_hash.h b/src/njs_object_hash.h index b9d8a7fc..9f8fcce7 100644 --- a/src/njs_object_hash.h +++ b/src/njs_object_hash.h @@ -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 --git a/src/njs_parser_terminal.c b/src/njs_parser_terminal.c index 0525cdcb..11f3bbf7 100644 --- a/src/njs_parser_terminal.c +++ b/src/njs_parser_terminal.c @@ -18,7 +18,7 @@ static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, 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_parser_t *parser, njs_parser_node_t *obj) } 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_parser_t *parser, njs_parser_node_t *obj) 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_parser_t *parser, njs_parser_node_t *obj) 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_parser_t *parser, njs_parser_node_t *obj) } 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, njs_parser_t *parser, 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_parser_t *parser, 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 --git a/src/njs_vmcode.c b/src/njs_vmcode.c index d4b8878b..7c86ba0e 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -23,8 +23,10 @@ static njs_jump_off_t njs_vmcode_template_literal(njs_vm_t *vm, 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, njs_value_t *value, njs_value_t *key, 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, njs_value_t *value, njs_value_t *key, 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, njs_value_t *value, njs_value_t *key, 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; @@ -1209,6 +1196,47 @@ njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, } +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) { diff --git a/src/njs_vmcode.h b/src/njs_vmcode.h index 8d4ecdbd..a2ba25da 100644 --- a/src/njs_vmcode.h +++ b/src/njs_vmcode.h @@ -52,6 +52,7 @@ typedef uint8_t njs_vmcode_operation_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 --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index c28a7d84..24a43538 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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") },