From 40a9f273143f7c3d6c0073064a857a65f2e99344 Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Mon, 4 May 2020 14:49:15 +0200 Subject: [PATCH] Fix __proto__ parsing in object literals (#3704) Co-authored-by: Rafal Walczyna r.walczyna@samsung.com JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- jerry-core/parser/js/byte-code.h | 2 + jerry-core/parser/js/js-parser-expr.c | 23 ++++++++--- jerry-core/vm/vm.c | 41 ++++++++------------ jerry-core/vm/vm.h | 2 + tests/jerry/es2015/object-prototype-proto.js | 35 ++++++++++++++++- 5 files changed, 71 insertions(+), 32 deletions(-) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 2a7c1311b..b102b4e22 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -604,6 +604,8 @@ VM_OC_SET_GETTER | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_STATIC_COMPUTED_SETTER, CBC_HAS_LITERAL_ARG, -1, \ VM_OC_SET_SETTER | VM_OC_GET_STACK_LITERAL) \ + CBC_OPCODE (CBC_EXT_SET__PROTO__, CBC_NO_FLAG, -1, \ + VM_OC_SET__PROTO__ | VM_OC_GET_STACK) \ \ /* Class related opcodes. */ \ CBC_OPCODE (CBC_EXT_PUSH_NAMED_CLASS_ENV, CBC_HAS_LITERAL_ARG, 1, \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index fbf242b85..4da83f2ee 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -1041,9 +1041,12 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ default: { #if ENABLED (JERRY_ES2015) - if ((context_p->token.lit_location.type == LEXER_IDENT_LITERAL - || context_p->token.lit_location.type == LEXER_STRING_LITERAL) - && lexer_compare_literal_to_string (context_p, "__proto__", 9)) + const lexer_lit_location_t *literal_p = (const lexer_lit_location_t *) context_p->lit_object.literal_p; + bool is_proto = ((context_p->token.lit_location.type == LEXER_IDENT_LITERAL + || context_p->token.lit_location.type == LEXER_STRING_LITERAL) + && lexer_compare_identifier_to_string (literal_p, (uint8_t *) "__proto__", 9) + && lexer_check_next_character (context_p, LIT_CHAR_COLON)); + if (is_proto) { if (proto_seen) { @@ -1068,7 +1071,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ lexer_next_token (context_p); #if ENABLED (JERRY_ES2015) - if (context_p->token.type == LEXER_LEFT_PAREN) + if (context_p->token.type == LEXER_LEFT_PAREN && !is_proto) { parser_parse_object_method (context_p); @@ -1078,8 +1081,8 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ break; } - if (context_p->token.type == LEXER_RIGHT_BRACE - || context_p->token.type == LEXER_COMMA) + if ((context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_COMMA) + && !is_proto) { parser_reparse_as_common_identifier (context_p, start_line, start_column); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); @@ -1100,6 +1103,14 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); +#if ENABLED (JERRY_ES2015) + if (is_proto) + { + parser_emit_cbc_ext (context_p, CBC_EXT_SET__PROTO__); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 86ef37877..80441dd10 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -92,15 +92,6 @@ vm_op_get_value (ecma_value_t object, /**< base object */ else if (ecma_is_value_string (property)) { property_name_p = ecma_get_string_from_value (property); - -#if ENABLED (JERRY_ES2015) - if (JERRY_UNLIKELY (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING__PROTO__))) - { - ecma_object_t *obj_p = ecma_get_object_from_value (object); - - return ecma_builtin_object_object_get_prototype_of (obj_p); - } -#endif /* ENABLED (JERRY_ES2015) */ } #if ENABLED (JERRY_ES2015) @@ -1367,6 +1358,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ continue; } +#if ENABLED (JERRY_SNAPSHOT_EXEC) + case VM_OC_SET_BYTECODE_PTR: + { + memcpy (&byte_code_p, byte_code_p++, sizeof (uint8_t *)); + frame_ctx_p->byte_code_start_p = byte_code_p; + continue; + } +#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ case VM_OC_INIT_LOCAL: { uint32_t literal_index, value_index; @@ -1576,6 +1575,15 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ frame_ctx_p->lex_env_p = ecma_clone_decl_lexical_environment (frame_ctx_p->lex_env_p, copy_values); continue; } + case VM_OC_SET__PROTO__: + { + result = ecma_builtin_object_object_set_proto (stack_top_p[-1], left_value); + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + goto free_left_value; + } case VM_OC_SET_COMPUTED_PROPERTY: { /* Swap values. */ @@ -1585,14 +1593,6 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ /* FALLTHRU */ } #endif /* ENABLED (JERRY_ES2015) */ -#if ENABLED (JERRY_SNAPSHOT_EXEC) - case VM_OC_SET_BYTECODE_PTR: - { - memcpy (&byte_code_p, byte_code_p++, sizeof (uint8_t *)); - frame_ctx_p->byte_code_start_p = byte_code_p; - continue; - } -#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ case VM_OC_SET_PROPERTY: { JERRY_STATIC_ASSERT (VM_OC_NON_STATIC_FLAG == VM_OC_BACKWARD_BRANCH, @@ -1617,13 +1617,6 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } const int index = (int) (opcode_data >> VM_OC_NON_STATIC_SHIFT) - 2; - - if (JERRY_UNLIKELY (ecma_compare_ecma_string_to_magic_id (prop_name_p, LIT_MAGIC_STRING__PROTO__))) - { - result = ecma_builtin_object_object_set_proto (stack_top_p[index], left_value); - ecma_deref_ecma_string (prop_name_p); - goto free_both_values; - } #else /* !ENABLED (JERRY_ES2015) */ const int index = -1; #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 22d794aee..8d8dd7c59 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -271,6 +271,7 @@ typedef enum VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE,/**< RequireObjectCoercible opretaion */ VM_OC_ASSIGN_SUPER, /**< assign super reference */ + VM_OC_SET__PROTO__, /**< set prototpe when __proto__: form is used */ #endif /* ENABLED (JERRY_ES2015) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -333,6 +334,7 @@ typedef enum VM_OC_PUSH_NEW_TARGET = VM_OC_NONE, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE = VM_OC_NONE,/**< RequireObjectCoercible opretaion */ VM_OC_ASSIGN_SUPER = VM_OC_NONE, /**< assign super reference */ + VM_OC_SET__PROTO__ = VM_OC_NONE, /**< set prototpe when __proto__: form is used */ #endif /* !ENABLED (JERRY_ES2015) */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ diff --git a/tests/jerry/es2015/object-prototype-proto.js b/tests/jerry/es2015/object-prototype-proto.js index 9166b7b43..6e6bde60c 100644 --- a/tests/jerry/es2015/object-prototype-proto.js +++ b/tests/jerry/es2015/object-prototype-proto.js @@ -26,7 +26,7 @@ var o = {}; o.__proto__ = Array.prototype; assert(o instanceof Array); -var obj = Object.create(null) +var obj = Object.create(null) p = {}; obj.__proto__ = p; assert(Object.getPrototypeOf(obj) !== p); @@ -54,5 +54,36 @@ try { assert(e instanceof TypeError); } -var o2 = { ["__proto__"] : null }; +var o2 = { ["__proto__"] : null }; assert(o2.__proto__ === null); +assert(Object.getPrototypeOf(o2) === Object.prototype); + +var o3 = { __proto__ : null }; +assert(o3.__proto__ === undefined); +assert(Object.getPrototypeOf(o3) === null); + +var o4 = { "__proto__" : null }; +assert(o4.__proto__ === undefined); +assert(Object.getPrototypeOf(o4) === null); + +var __proto__ = []; +var o5 = { __proto__ }; +assert(o5.__proto__ === __proto__); +assert(Object.getPrototypeOf(o5) === Object.prototype); + +var o6 = { __proto__() { return "42" } }; +assert(o6.__proto__() === "42"); +assert(Object.getPrototypeOf(o6) === Object.prototype); + +var o7 = { __\u0070r\u006ft\u006f__: null }; +assert(o7.__proto__ === undefined); +assert(Object.getPrototypeOf(o7) === null); + +var o8 = { }; +o8.__proto__ = Array.prototype; +assert(Object.getPrototypeOf(o8) === Array.prototype); + +var str1 = '{"__proto__": [] }'; +var obj1 = JSON.parse(str1); +assert(Object.getPrototypeOf(obj1) === Object.prototype); +assert(Array.isArray(obj1.__proto__));