diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index 38b5bc205..f04e6f6f3 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t) */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 135, +JERRY_STATIC_ASSERT (CBC_EXT_END == 136, number_of_cbc_ext_opcodes_changed); #if ENABLED (JERRY_PARSER) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 1e8a3cf97..936dbc6fa 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -606,6 +606,8 @@ VM_OC_LINE) \ CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \ VM_OC_THROW_REFERENCE_ERROR) \ + CBC_OPCODE (CBC_EXT_THROW_SYNTAX_ERROR, CBC_HAS_LITERAL_ARG, 1, \ + VM_OC_THROW_SYNTAX_ERROR | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_THROW_ASSIGN_CONST_ERROR, CBC_NO_FLAG, 0, \ VM_OC_THROW_CONST_ERROR) \ CBC_OPCODE (CBC_EXT_REQUIRE_OBJECT_COERCIBLE, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index e6da94389..256e39e1a 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -94,6 +94,42 @@ lexer_hex_to_code_point (const uint8_t *source_p, /**< current source position * #if ENABLED (JERRY_ESNEXT) +/** + * Find a string literal in the literal pool matching with the given buffer's content + * + * @return PARSER_INVALID_LITERAL_INDEX - if the literal is not present in the literal pool + * literal's index in the pool - otherwise + */ +static uint16_t +parser_find_string_literal (parser_context_t *context_p, /**< context */ + lexer_literal_t **out_literal_p, /**< [out] found literal */ + uint8_t *buffer_p, /**< character buffer */ + lit_utf8_size_t size) /**< buffer's size */ +{ + JERRY_ASSERT (out_literal_p != NULL); + JERRY_ASSERT (buffer_p != NULL); + + uint16_t literal_index = 0; + lexer_literal_t *literal_p; + parser_list_iterator_t literal_iterator; + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (literal_p->type == LEXER_STRING_LITERAL + && literal_p->prop.length == size + && memcmp (literal_p->u.char_p, buffer_p, size) == 0) + { + *out_literal_p = literal_p; + return literal_index; + } + + literal_index++; + } + + return PARSER_INVALID_LITERAL_INDEX; +} /* parser_find_string_literal */ + /** * Parse hexadecimal character sequence enclosed in braces * @@ -2697,6 +2733,9 @@ lexer_construct_function_object (parser_context_t *context_p, /**< context */ /** * Construct a regular expression object. + * + * Note: In ESNEXT the constructed literal's type can be LEXER_STRING_LITERAL which represents + * invalid pattern. The string literal contains the thrown error message. */ void lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ @@ -2708,7 +2747,6 @@ lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ const uint8_t *regex_end_p = regex_start_p; const uint8_t *source_end_p = context_p->source_end_p; parser_line_counter_t column = context_p->column; - lexer_literal_t *literal_p; bool in_class = false; uint16_t current_flags; lit_utf8_size_t length; @@ -2865,13 +2903,6 @@ lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } - literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - literal_p->prop.length = (prop_length_t) length; - literal_p->type = LEXER_UNUSED_LITERAL; - literal_p->status_flags = 0; - - context_p->literal_count++; - /* Compile the RegExp literal and store the RegExp bytecode pointer */ ecma_string_t *pattern_str_p = NULL; @@ -2888,13 +2919,57 @@ lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ re_compiled_code_t *re_bytecode_p = re_compile_bytecode (pattern_str_p, current_flags); ecma_deref_ecma_string (pattern_str_p); + lexer_literal_t *literal_p = NULL; + uint8_t literal_type = LEXER_REGEXP_LITERAL; + if (JERRY_UNLIKELY (re_bytecode_p == NULL)) { +#if ENABLED (JERRY_ESNEXT) + ecma_value_t error = jcontext_take_exception (); + ecma_property_t *prop_p = ecma_find_named_property (ecma_get_object_from_value (error), + ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE)); + ecma_free_value (error); + const char default_msg[] = "Invalid regular expression"; + lit_utf8_byte_t *buffer_p = (lit_utf8_byte_t *) default_msg; + lit_utf8_size_t size = sizeof (buffer_p) - 1; + + if (prop_p != NULL) + { + ecma_string_t *message_p = ecma_get_string_from_value (ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + JERRY_ASSERT (!ECMA_IS_DIRECT_STRING (message_p)); + JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (message_p) == ECMA_STRING_CONTAINER_HEAP_ASCII_STRING); + buffer_p = ECMA_ASCII_STRING_GET_BUFFER (message_p); + size = ((ecma_ascii_string_t *) message_p)->size; + } + + uint16_t literal_index = parser_find_string_literal (context_p, &literal_p, buffer_p, size); + + if (literal_index != PARSER_INVALID_LITERAL_INDEX) + { + context_p->lit_object.literal_p = literal_p; + context_p->lit_object.index = literal_index; + return; + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->u.char_p = (uint8_t *) jmem_heap_alloc_block (size); + memcpy ((uint8_t *) literal_p->u.char_p, buffer_p, size); + literal_type = LEXER_STRING_LITERAL; + length = size; +#else /* !ENABLED (JERRY_ESNEXT) */ parser_raise_error (context_p, PARSER_ERR_INVALID_REGEXP); +#endif /* ENABLED (JERRY_ESNEXT) */ + } + else + { + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->u.bytecode_p = (ecma_compiled_code_t *) re_bytecode_p; } - literal_p->type = LEXER_REGEXP_LITERAL; - literal_p->u.bytecode_p = (ecma_compiled_code_t *) re_bytecode_p; + literal_p->type = literal_type; + literal_p->prop.length = (prop_length_t) length; + literal_p->status_flags = 0; + context_p->literal_count++; context_p->token.type = LEXER_LITERAL; context_p->token.keyword_type = LEXER_EOS; diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 6dd6e2221..a9d4429ad 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -782,8 +782,8 @@ parser_parse_class (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS); - uint16_t class_ident_index = UINT16_MAX; - uint16_t class_name_index = UINT16_MAX; + uint16_t class_ident_index = PARSER_INVALID_LITERAL_INDEX; + uint16_t class_name_index = PARSER_INVALID_LITERAL_INDEX; parser_class_literal_opts_t opts = PARSER_CLASS_LITERAL_NO_OPTS; if (context_p->next_scanner_info_p->source_p == context_p->source_p) @@ -830,7 +830,7 @@ parser_parse_class (parser_context_t *context_p, /**< context */ } } - if (class_name_index != UINT16_MAX) + if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_NAMED_CLASS_ENV, class_name_index); } @@ -864,7 +864,7 @@ parser_parse_class (parser_context_t *context_p, /**< context */ /* ClassDeclaration is parsed. Continue with class body. */ parser_parse_class_literal (context_p, opts); - if (class_name_index != UINT16_MAX) + if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_FINALIZE_NAMED_CLASS, class_name_index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_FUNCTION_NAME, class_name_index); @@ -1976,6 +1976,14 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ uint16_t literal_index = (uint16_t) (context_p->literal_count - 1); +#if ENABLED (JERRY_ESNEXT) + if (JERRY_UNLIKELY (context_p->lit_object.literal_p->type == LEXER_STRING_LITERAL)) + { + parser_emit_cbc_ext_literal (context_p, CBC_EXT_THROW_SYNTAX_ERROR, literal_index); + break; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; @@ -2892,7 +2900,7 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ opcode = (cbc_opcode_t) context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); - uint16_t index = UINT16_MAX; + uint16_t index = PARSER_INVALID_LITERAL_INDEX; if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG) { @@ -2923,7 +2931,7 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ESNEXT) */ - if (index != UINT16_MAX) + if (index != PARSER_INVALID_LITERAL_INDEX) { #if ENABLED (JERRY_ESNEXT) if (!group_expr_assingment) @@ -3039,7 +3047,7 @@ typedef struct /** * Literal index should not be emitted while processing rhs target value */ -#define PARSER_PATTERN_RHS_NO_LIT UINT16_MAX +#define PARSER_PATTERN_RHS_NO_LIT PARSER_INVALID_LITERAL_INDEX /** * Process the target of an initializer pattern. @@ -3161,7 +3169,7 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ { JERRY_UNUSED (ident_line_counter); - uint16_t name_index = UINT16_MAX; + uint16_t name_index = PARSER_INVALID_LITERAL_INDEX; if ((flags & PARSER_PATTERN_BINDING) || (context_p->last_cbc_opcode == CBC_PUSH_LITERAL @@ -3191,7 +3199,7 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); - if (name_index != UINT16_MAX) + if (name_index != PARSER_INVALID_LITERAL_INDEX) { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 902647aa7..898eebad6 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -433,10 +433,15 @@ typedef struct */ #define PARSER_REGISTER_START 0x8000 +/** + * Invalid literal index + */ +#define PARSER_INVALID_LITERAL_INDEX UINT16_MAX + /** * Lastly emitted opcode is not a function literal */ -#define PARSER_NOT_FUNCTION_LITERAL UINT16_MAX +#define PARSER_NOT_FUNCTION_LITERAL PARSER_INVALID_LITERAL_INDEX /** * Lastly emitted opcode is not a named function literal diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 06ad46ba1..78d134588 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -393,7 +393,7 @@ parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */ bool is_negative_number) /**< sign is negative */ { uint16_t value = context_p->lit_object.index; - uint16_t lit_value = UINT16_MAX; + uint16_t lit_value = PARSER_INVALID_LITERAL_INDEX; if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) { @@ -420,7 +420,7 @@ parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */ if (value == 0) { - if (lit_value == UINT16_MAX) + if (lit_value == PARSER_INVALID_LITERAL_INDEX) { context_p->last_cbc_opcode = CBC_PUSH_NUMBER_0; return; @@ -433,7 +433,7 @@ parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */ uint16_t opcode; - if (lit_value == UINT16_MAX) + if (lit_value == PARSER_INVALID_LITERAL_INDEX) { opcode = (is_negative_number ? CBC_PUSH_NUMBER_NEG_BYTE : CBC_PUSH_NUMBER_POS_BYTE); diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 9573a828e..9d7e488ab 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2924,18 +2924,27 @@ parser_parse_script (const uint8_t *arg_list_p, /**< function argument list */ return NULL; } +#if ENABLED (JERRY_ERROR_MESSAGES) + ecma_string_t *err_str_p; + +#if !ENABLED (JERRY_ESNEXT) if (parser_error.error == PARSER_ERR_INVALID_REGEXP) { - /* The RegExp compiler has already raised an exception. */ - JERRY_ASSERT (jcontext_has_pending_exception ()); - return NULL; + ecma_value_t error = jcontext_take_exception (); + ecma_property_t *prop_p = ecma_find_named_property (ecma_get_object_from_value (error), + ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE)); + ecma_free_value (error); + JERRY_ASSERT (prop_p); + err_str_p = ecma_get_string_from_value (ECMA_PROPERTY_VALUE_PTR (prop_p)->value); + ecma_ref_ecma_string (err_str_p); + } + else +#endif /* !ENABLED (JERRY_ESNEXT) */ + { + const lit_utf8_byte_t *err_bytes_p = (const lit_utf8_byte_t *) parser_error_to_string (parser_error.error); + lit_utf8_size_t err_bytes_size = lit_zt_utf8_string_size (err_bytes_p); + err_str_p = ecma_new_ecma_string_from_utf8 (err_bytes_p, err_bytes_size); } - -#if ENABLED (JERRY_ERROR_MESSAGES) - const lit_utf8_byte_t *err_bytes_p = (const lit_utf8_byte_t *) parser_error_to_string (parser_error.error); - lit_utf8_size_t err_bytes_size = lit_zt_utf8_string_size (err_bytes_p); - - ecma_string_t *err_str_p = ecma_new_ecma_string_from_utf8 (err_bytes_p, err_bytes_size); ecma_value_t err_str_val = ecma_make_string_value (err_str_p); ecma_value_t line_str_val = ecma_make_uint32_value (parser_error.line); ecma_value_t col_str_val = ecma_make_uint32_value (parser_error.column); @@ -2949,9 +2958,14 @@ parser_parse_script (const uint8_t *arg_list_p, /**< function argument list */ ecma_free_value (col_str_val); ecma_free_value (line_str_val); - ecma_free_value (err_str_val); + ecma_deref_ecma_string (err_str_p); #else /* !ENABLED (JERRY_ERROR_MESSAGES) */ - ecma_raise_syntax_error (""); +#if !ENABLED (JERRY_ESNEXT) + if (parser_error.error != PARSER_ERR_INVALID_REGEXP) +#endif /* !ENABLED (JERRY_ESNEXT) */ + { + ecma_raise_syntax_error (""); + } #endif /* ENABLED (JERRY_ERROR_MESSAGES) */ return NULL; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 513d4d38b..707c02c01 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1640,6 +1640,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ result = ecma_raise_type_error (ECMA_ERR_MSG ("Constant bindings cannot be reassigned.")); goto error; } + case VM_OC_THROW_SYNTAX_ERROR: + { + ecma_string_t *msg_p = ecma_get_string_from_value (left_value); + ecma_object_t *error_obj_p = ecma_new_standard_error_with_message (ECMA_ERROR_SYNTAX, msg_p); + jcontext_raise_exception (ecma_make_object_value (error_obj_p)); + result = ECMA_VALUE_ERROR; + goto error; + } case VM_OC_COPY_TO_GLOBAL: { uint32_t literal_index; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 421724798..056322ec5 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -241,6 +241,7 @@ typedef enum VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ VM_OC_INIT_BINDING, /**< create and intialize a binding */ VM_OC_THROW_CONST_ERROR, /**< throw invalid assignment to const variable error */ + VM_OC_THROW_SYNTAX_ERROR, /**< throw syntax error */ VM_OC_COPY_TO_GLOBAL, /**< copy value to global lex env */ VM_OC_COPY_FROM_ARG, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ @@ -316,6 +317,7 @@ typedef enum VM_OC_ASSIGN_LET_CONST = VM_OC_NONE, /**< assign values to let/const declarations */ VM_OC_INIT_BINDING = VM_OC_NONE, /**< create and intialize a binding */ VM_OC_THROW_CONST_ERROR = VM_OC_NONE, /**< throw invalid assignment to const variable error */ + VM_OC_THROW_SYNTAX_ERROR = VM_OC_NONE, /**< throw syntax error */ VM_OC_COPY_TO_GLOBAL = VM_OC_NONE, /**< copy value to global lex env */ VM_OC_COPY_FROM_ARG = VM_OC_NONE, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */ diff --git a/tests/jerry/fail/regression-test-issue-2058.js b/tests/jerry/es.next/regression-test-issue-2058.js similarity index 88% rename from tests/jerry/fail/regression-test-issue-2058.js rename to tests/jerry/es.next/regression-test-issue-2058.js index 651e7fadc..7906b6066 100644 --- a/tests/jerry/fail/regression-test-issue-2058.js +++ b/tests/jerry/es.next/regression-test-issue-2058.js @@ -12,4 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -/?:/ +try { + /?:/ + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +} diff --git a/tests/jerry/es5.1/regression-test-issue-2058.js b/tests/jerry/es5.1/regression-test-issue-2058.js new file mode 100644 index 000000000..061a2686a --- /dev/null +++ b/tests/jerry/es5.1/regression-test-issue-2058.js @@ -0,0 +1,20 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +try { + eval("/?:/"); + assert(false); +} catch (e) { + assert(e instanceof SyntaxError); +}