diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index c1e2c508c..e6014f0c1 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -220,6 +220,67 @@ jsp_skip_braces (token_type brace_type) /**< type of the opening brace */ 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 @@ -1726,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 (); + current_token_must_be (TOK_OPEN_PAREN); + skip_newlines (); - // Skip till body - JERRY_ASSERT (token_is (TOK_SEMICOLON)); - 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 @@ -1819,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)) + + 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_plain_for (outermost_stmt_label_p); - return; - } - else if (is_keyword (KW_IN)) - { - 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) @@ -2318,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, @@ -2412,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)) diff --git a/tests/jerry/for.js b/tests/jerry/for.js index e1d7cc5d7..ce33b98dc 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 000000000..1dfbd3608 --- /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--) {}