diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 455cf50aac..e6014f0c1d 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -67,9 +67,6 @@ static void parse_statement (jsp_label_t *outermost_stmt_label_p); static operand parse_assignment_expression (bool); static void parse_source_element_list (bool); static operand parse_argument_list (varg_list_type, operand, uint8_t *, operand *); -static void process_keyword_names (void); -static void skip_braces (void); -static void skip_parens (void); static bool token_is (token_type tt) @@ -174,6 +171,116 @@ is_strict_mode (void) return scopes_tree_strict_mode (STACK_TOP (scopes)); } +/** + * Skip block, defined with braces of specified type + * + * Note: + * Missing corresponding brace is considered a syntax error + * + * Note: + * Opening brace of the block to skip should be set as current + * token when the routine is called + */ +static void +jsp_skip_braces (token_type brace_type) /**< type of the opening brace */ +{ + current_token_must_be (brace_type); + + token_type closing_bracket_type; + + if (brace_type == TOK_OPEN_PAREN) + { + closing_bracket_type = TOK_CLOSE_PAREN; + } + else if (brace_type == TOK_OPEN_BRACE) + { + closing_bracket_type = TOK_CLOSE_BRACE; + } + else + { + JERRY_ASSERT (brace_type == TOK_OPEN_SQUARE); + closing_bracket_type = TOK_CLOSE_SQUARE; + } + + skip_newlines (); + + while (!token_is (closing_bracket_type) + && !token_is (TOK_EOF)) + { + if (token_is (TOK_OPEN_PAREN) + || token_is (TOK_OPEN_BRACE) + || token_is (TOK_OPEN_SQUARE)) + { + jsp_skip_braces (tok.type); + } + + skip_newlines (); + } + + current_token_must_be (closing_bracket_type); +} /* jsp_skip_braces */ + +/** + * Find next token of specified type before the specified location + * + * Note: + * If skip_brace_blocks is true, every { should correspond to } brace before search end location, + * otherwise a syntax error is raised. + * + * @return true - if token was found (in the case, it is the current token, + * and lexer locus points to it), + * false - otherwise (in the case, lexer locus points to end_loc). + */ +static bool +jsp_find_next_token_before_the_locus (token_type token_to_find, /**< token to search for + * (except TOK_NEWLINE and TOK_EOF) */ + locus end_loc, /**< location to search before */ + bool skip_brace_blocks) /**< skip blocks, surrounded with { and } braces */ +{ + JERRY_ASSERT (token_to_find != TOK_NEWLINE + && token_to_find != TOK_EOF); + + while (tok.loc < end_loc) + { + if (skip_brace_blocks) + { + if (token_is (TOK_OPEN_BRACE)) + { + jsp_skip_braces (TOK_OPEN_BRACE); + + JERRY_ASSERT (token_is (TOK_CLOSE_BRACE)); + skip_newlines (); + + if (tok.loc >= end_loc) + { + lexer_seek (end_loc); + tok = lexer_next_token (); + + return false; + } + } + else if (token_is (TOK_CLOSE_BRACE)) + { + EMIT_ERROR ("Unmatched } brace"); + } + } + + if (token_is (token_to_find)) + { + return true; + } + else + { + JERRY_ASSERT (!token_is (TOK_EOF)); + } + + skip_newlines (); + } + + JERRY_ASSERT (tok.loc == end_loc); + return false; +} /* jsp_find_next_token_before_the_locus */ + /* property_name : Identifier | Keyword @@ -1680,88 +1787,146 @@ parse_variable_declaration (void) (LT!* ',' LT!* variable_declaration)* ; */ static void -parse_variable_declaration_list (bool *several_decls) +parse_variable_declaration_list (void) { + JERRY_ASSERT (is_keyword (KW_VAR)); + while (true) { + skip_newlines (); + parse_variable_declaration (); skip_newlines (); if (!token_is (TOK_COMMA)) { lexer_save_token (tok); - return; - } - - skip_newlines (); - if (several_decls) - { - *several_decls = true; + break; } } } +/** + * Parse for statement + * + * See also: + * ECMA-262 v5, 12.6.3 + * + * Note: + * Syntax: + * Initializer Condition Increment Body LoopEnd + * - for ([ExpressionNoIn]; [Expression]; [Expression]) Statement + * - for (var VariableDeclarationListNoIn; [Expression]; [Expression]) Statement + * + * Note: + * Layout of generated byte-code is the following: + * Initializer ([ExpressionNoIn] / VariableDeclarationListNoIn) + * Jump -> ConditionCheck + * NextIteration: + * Body (Statement) + * ContinueTarget: + * Increment ([Expression]) + * ConditionCheck: + * Condition ([Expression]) + * If Condition is evaluted to true, jump -> NextIteration + */ static void -parse_plain_for (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to - * the statement (or NULL, if there are no named - * labels associated with the statement) */ +jsp_parse_for_statement (jsp_label_t *outermost_stmt_label_p, /**< outermost (first) label, corresponding to + * the statement (or NULL, if there are no named + * labels associated with the statement) */ + locus for_body_statement_loc) /**< locus of loop body statement */ { - dump_jump_to_end_for_rewrite (); - - // Skip till body - JERRY_ASSERT (token_is (TOK_SEMICOLON)); + current_token_must_be (TOK_OPEN_PAREN); skip_newlines (); - const locus cond_loc = tok.loc; - while (!token_is (TOK_SEMICOLON)) + + // Initializer + if (is_keyword (KW_VAR)) { - skip_newlines (); + parse_variable_declaration_list (); + skip_token (); } - skip_newlines (); - const locus incr_loc = tok.loc; - while (!token_is (TOK_CLOSE_PAREN)) + else if (!token_is (TOK_SEMICOLON)) { - skip_newlines (); + parse_expression (false, JSP_EVAL_RET_STORE_NOT_DUMP); + skip_token (); } + else + { + // Initializer is empty + } + + // Jump -> ConditionCheck + dump_jump_to_end_for_rewrite (); dumper_set_next_interation_target (); - // Parse body + current_token_must_be (TOK_SEMICOLON); + skip_token (); + + // Save Condition locus + const locus condition_loc = tok.loc; + + if (!jsp_find_next_token_before_the_locus (TOK_SEMICOLON, + for_body_statement_loc, + true)) + { + EMIT_ERROR ("Invalid for statement"); + } + + current_token_must_be (TOK_SEMICOLON); + skip_token (); + + // Save Increment locus + const locus increment_loc = tok.loc; + + // Body + lexer_seek (for_body_statement_loc); skip_newlines (); + parse_statement (NULL); - const locus end_loc = tok.loc; + // Save LoopEnd locus + const locus loop_end_loc = tok.loc; + // Setup ContinueTarget jsp_label_setup_continue_target (outermost_stmt_label_p, serializer_get_current_opcode_counter ()); - lexer_seek (incr_loc); - skip_token (); + // Increment + lexer_seek (increment_loc); + skip_newlines (); + if (!token_is (TOK_CLOSE_PAREN)) { parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); } + current_token_must_be (TOK_CLOSE_PAREN); + + // Setup ConditionCheck rewrite_jump_to_end (); - lexer_seek (cond_loc); - skip_token (); + // Condition + lexer_seek (condition_loc); + skip_newlines (); + if (token_is (TOK_SEMICOLON)) { dump_continue_iterations_check (empty_operand ()); } else { - const operand cond = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); + operand cond = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); dump_continue_iterations_check (cond); } - lexer_seek (end_loc); - skip_token (); + lexer_seek (loop_end_loc); + skip_newlines (); if (tok.type != TOK_CLOSE_BRACE) { lexer_save_token (tok); } -} +} /* jsp_parse_for_statement */ static void parse_for_in (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to @@ -1773,92 +1938,48 @@ parse_for_in (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, EMIT_SORRY ("'for in' loops are not supported yet"); } -/* for_statement - : 'for' LT!* '(' (LT!* for_statement_initialiser_part)? LT!* ';' - (LT!* expression)? LT!* ';' (LT!* expression)? LT!* ')' LT!* statement - ; - - for_statement_initialiser_part - : expression - | 'var' LT!* variable_declaration_list - ; - - for_in_statement - : 'for' LT!* '(' LT!* for_in_statement_initialiser_part LT!* 'in' - LT!* expression LT!* ')' LT!* statement - ; - - for_in_statement_initialiser_part - : left_hand_side_expression - | 'var' LT!* variable_declaration - ;*/ - +/** + * Parse for/for-in statements + * + * See also: + * ECMA-262 v5, 12.6.3 and 12.6.4 + */ static void -parse_for_or_for_in_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to - * the statement (or NULL, if there are no named - * labels associated with the statement) */ +jsp_parse_for_or_for_in_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, + * corresponding to the statement + * (or NULL, if there are no name + * labels associated with the statement) */ { assert_keyword (KW_FOR); token_after_newlines_must_be (TOK_OPEN_PAREN); - skip_newlines (); - if (token_is (TOK_SEMICOLON)) - { - parse_plain_for (outermost_stmt_label_p); - return; - } - /* Both for_statement_initialiser_part and for_in_statement_initialiser_part - contains 'var'. Check it first. */ - if (is_keyword (KW_VAR)) - { - bool several_decls = false; - skip_newlines (); - parse_variable_declaration_list (&several_decls); - if (several_decls) - { - token_after_newlines_must_be (TOK_SEMICOLON); - parse_plain_for (outermost_stmt_label_p); - return; - } - else - { - skip_newlines (); - if (token_is (TOK_SEMICOLON)) - { - parse_plain_for (outermost_stmt_label_p); - return; - } - else if (is_keyword (KW_IN)) - { - parse_for_in (outermost_stmt_label_p); - return; - } - else - { - EMIT_ERROR ("Expected either ';' or 'in' token"); - } - } - } + locus for_open_paren_loc, for_body_statement_loc; - /* expression contains left_hand_side_expression. */ - parse_expression (false, JSP_EVAL_RET_STORE_NOT_DUMP); + for_open_paren_loc = tok.loc; + jsp_skip_braces (TOK_OPEN_PAREN); skip_newlines (); - if (token_is (TOK_SEMICOLON)) - { - parse_plain_for (outermost_stmt_label_p); - return; - } - else if (is_keyword (KW_IN)) + + for_body_statement_loc = tok.loc; + + lexer_seek (for_open_paren_loc); + tok = lexer_next_token (); + + bool is_plain_for = jsp_find_next_token_before_the_locus (TOK_SEMICOLON, + for_body_statement_loc, + true); + lexer_seek (for_open_paren_loc); + tok = lexer_next_token (); + + if (is_plain_for) { - parse_for_in (outermost_stmt_label_p); - return; + jsp_parse_for_statement (outermost_stmt_label_p, for_body_statement_loc); } else { - EMIT_ERROR ("Expected either ';' or 'in' token"); + parse_for_in (outermost_stmt_label_p); } -} +} /* jsp_parse_for_or_for_in_statement */ static operand parse_expression_inside_parens (void) @@ -1965,7 +2086,7 @@ parse_while_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (firs token_after_newlines_must_be (TOK_OPEN_PAREN); const locus cond_loc = tok.loc; - skip_parens (); + jsp_skip_braces (TOK_OPEN_PAREN); dump_jump_to_end_for_rewrite (); @@ -2021,7 +2142,7 @@ skip_case_clause_body (void) { if (token_is (TOK_OPEN_BRACE)) { - skip_braces (); + jsp_skip_braces (TOK_OPEN_BRACE); } skip_newlines (); } @@ -2272,7 +2393,7 @@ parse_iterational_statement (jsp_label_t *outermost_named_stmt_label_p) /**< out else { JERRY_ASSERT (is_keyword (KW_FOR)); - parse_for_or_for_in_statement (outermost_stmt_label_p); + jsp_parse_for_or_for_in_statement (outermost_stmt_label_p); } jsp_label_rewrite_jumps_and_pop (&label, @@ -2366,8 +2487,7 @@ parse_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab } if (is_keyword (KW_VAR)) { - skip_newlines (); - parse_variable_declaration_list (NULL); + parse_variable_declaration_list (); return; } if (is_keyword (KW_FUNCTION)) @@ -2566,121 +2686,13 @@ skip_optional_name_and_parens (void) } } -static void process_keyword_names () -{ - if (token_is (TOK_KEYWORD)) - { - keyword kw = (keyword) token_data (); - skip_newlines (); - if (token_is (TOK_COLON)) - { - const char *s = lexer_keyword_to_string (kw); - lit_find_or_create_literal_from_charset ((const ecma_char_t *) s, (ecma_length_t) strlen (s)); - } - else - { - lexer_save_token (tok); - } - } - else if (token_is (TOK_NAME)) - { - if (lit_literal_equal_type_zt (lit_get_literal_by_cp (token_data_as_lit_cp ()), (const ecma_char_t *) "get") - || lit_literal_equal_type_zt (lit_get_literal_by_cp (token_data_as_lit_cp ()), (const ecma_char_t *) "set")) - { - skip_newlines (); - if (token_is (TOK_KEYWORD)) - { - keyword kw = (keyword) token_data (); - skip_newlines (); - if (token_is (TOK_OPEN_PAREN)) - { - const char *s = lexer_keyword_to_string (kw); - lit_find_or_create_literal_from_charset ((const ecma_char_t *) s, (ecma_length_t) strlen (s)); - } - else - { - lexer_save_token (tok); - } - } - else - { - lexer_save_token (tok); - } - } - } -} - -static void -skip_braces (void) -{ - current_token_must_be (TOK_OPEN_BRACE); - - uint8_t nesting_level = 1; - while (nesting_level > 0) - { - skip_newlines (); - if (token_is (TOK_OPEN_BRACE)) - { - nesting_level++; - } - else if (token_is (TOK_CLOSE_BRACE)) - { - nesting_level--; - } - else - { - process_keyword_names (); - } - } -} - static void skip_function (void) { skip_newlines (); skip_optional_name_and_parens (); skip_newlines (); - skip_braces (); -} - -static void -skip_squares (void) -{ - current_token_must_be (TOK_OPEN_SQUARE); - - uint8_t nesting_level = 1; - while (nesting_level > 0) - { - skip_newlines (); - if (token_is (TOK_OPEN_SQUARE)) - { - nesting_level++; - } - else if (token_is (TOK_CLOSE_SQUARE)) - { - nesting_level--; - } - } -} - -static void -skip_parens (void) -{ - current_token_must_be (TOK_OPEN_PAREN); - - uint8_t nesting_level = 1; - while (nesting_level > 0) - { - skip_newlines (); - if (token_is (TOK_OPEN_PAREN)) - { - nesting_level++; - } - else if (token_is (TOK_CLOSE_PAREN)) - { - nesting_level--; - } - } + jsp_skip_braces (TOK_OPEN_BRACE); } static bool @@ -2719,15 +2731,15 @@ preparse_var_decls (void) } else if (token_is (TOK_OPEN_BRACE)) { - skip_braces (); + jsp_skip_braces (TOK_OPEN_BRACE); } else if (token_is (TOK_OPEN_SQUARE)) { - skip_squares (); + jsp_skip_braces (TOK_OPEN_SQUARE); } else if (token_is (TOK_OPEN_PAREN)) { - skip_parens (); + jsp_skip_braces (TOK_OPEN_PAREN); } skip_token (); } @@ -2788,7 +2800,7 @@ preparse_scope (bool is_global) } else if (token_is (TOK_OPEN_BRACE)) { - skip_braces (); + jsp_skip_braces (TOK_OPEN_BRACE); } else { @@ -2805,8 +2817,6 @@ preparse_scope (bool is_global) is_ref_eval_identifier = true; } } - - process_keyword_names (); } skip_newlines (); } diff --git a/tests/jerry/for.js b/tests/jerry/for.js index e1d7cc5d7a..ce33b98dc6 100644 --- a/tests/jerry/for.js +++ b/tests/jerry/for.js @@ -1,4 +1,5 @@ -// Copyright 2014 Samsung Electronics Co., Ltd. +// Copyright 2014-2015 Samsung Electronics Co., Ltd. +// Copyright 2015 University of Szeged. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,15 +13,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +// 1. var i = 0; for (; i < 100; i++) { } assert(i == 100); +// 2. for (var j = 0; j < 100; j++) { } assert(j == 100); +// 3. for (i = 0; ; ) { if (i == 100) { break; @@ -30,6 +34,7 @@ for (i = 0; ; ) { } assert(i == 100); +// 4. for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { } @@ -38,3 +43,36 @@ assert(i != 100); assert(j != 100); assert(i == 10); assert(j == 10); + +// 5. +s = ''; +for ( +var i = {x: 0}; + + i.x < 2 +; + i.x++ + +) + { + s += i.x; +} + +assert (s === '01'); + +// 6. +s = ''; +for ( +var i = {x: 0}; + + i.x < 2 +; + + i.x++ + +) + { + s += i.x; +} + +assert (s === '01'); diff --git a/tests/jerry/regression-test-issue-156.js b/tests/jerry/regression-test-issue-156.js new file mode 100644 index 0000000000..1dfbd36082 --- /dev/null +++ b/tests/jerry/regression-test-issue-156.js @@ -0,0 +1,19 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// Copyright 2015 University of Szeged. +// +// 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. + +function dec(x) { return x - 1 }; +for (var i = 5; i > 0; i = dec(i)) {} +for (var i = 11; i = dec (i); i--) {} +for (var i = dec (12); i > 0; i--) {}