diff --git a/jerry-core/parser/js/jsp-label.cpp b/jerry-core/parser/js/jsp-label.cpp new file mode 100644 index 0000000000..1d2848980b --- /dev/null +++ b/jerry-core/parser/js/jsp-label.cpp @@ -0,0 +1,295 @@ +/* Copyright 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include "jsp-label.h" +#include "lexer.h" +#include "opcodes-dumper.h" + +/** \addtogroup jsparser ECMAScript parser + * @{ + * + * \addtogroup labels Jump labels + * @{ + */ + +/** + * Stack, containing current label set + */ +jsp_label_t *label_set_p = NULL; + +/** + * Initialize jumps labels mechanism + */ +void +jsp_label_init (void) +{ + JERRY_ASSERT (label_set_p == NULL); +} /* jsp_label_init */ + +/** + * Finalize jumps labels mechanism + */ +void +jsp_label_finalize (void) +{ + JERRY_ASSERT (label_set_p == NULL); +} /* jsp_label_finalize */ + +/** + * Add label to the current label set + */ +void +jsp_label_push (jsp_label_t *out_label_p, /**< out: place where label structure + * should be initialized and + * linked into label set stack */ + jsp_label_type_flag_t type_mask, /**< label's type mask */ + token id) /**< identifier of the label (TOK_NAME) + * if mask includes JSP_LABEL_TYPE_NAMED, + * or empty token - otherwise */ +{ + JERRY_ASSERT (out_label_p != NULL); + + if (type_mask & JSP_LABEL_TYPE_NAMED) + { + JERRY_ASSERT (id.type == TOK_NAME); + + JERRY_ASSERT (jsp_label_find (JSP_LABEL_TYPE_NAMED, id, NULL) == NULL); + } + else + { + JERRY_ASSERT (id.type == TOK_EMPTY); + } + + out_label_p->type_mask = type_mask; + out_label_p->id = id; + out_label_p->continue_tgt_oc = MAX_OPCODES; + out_label_p->breaks_list_oc = MAX_OPCODES; + out_label_p->breaks_number = 0; + out_label_p->continues_list_oc = MAX_OPCODES; + out_label_p->continues_number = 0; + out_label_p->next_label_p = label_set_p; + out_label_p->is_nested_jumpable_border = false; + + label_set_p = out_label_p; +} /* jsp_label_push */ + +/** + * Rewrite jumps to the label, if there any, + * and remove it from the current label set + * + * @return the label should be on top of label set stack + */ +void +jsp_label_rewrite_jumps_and_pop (jsp_label_t *label_p, /**< label to remove (should be on top of stack) */ + opcode_counter_t break_tgt_oc) /**< target opcode counter + * for breaks on the label */ +{ + JERRY_ASSERT (label_p != NULL); + JERRY_ASSERT (break_tgt_oc != MAX_OPCODES); + JERRY_ASSERT (label_set_p == label_p); + + /* Iterating jumps list, rewriting them */ + while (label_p->breaks_number--) + { + JERRY_ASSERT (label_p->breaks_list_oc != MAX_OPCODES); + + label_p->breaks_list_oc = rewrite_simple_or_nested_jump_and_get_next (label_p->breaks_list_oc, + break_tgt_oc); + } + while (label_p->continues_number--) + { + JERRY_ASSERT (label_p->continue_tgt_oc != MAX_OPCODES); + JERRY_ASSERT (label_p->continues_list_oc != MAX_OPCODES); + + label_p->continues_list_oc = rewrite_simple_or_nested_jump_and_get_next (label_p->continues_list_oc, + label_p->continue_tgt_oc); + } + + label_set_p = label_set_p->next_label_p; +} /* jsp_label_rewrite_jumps_and_pop */ + +/** + * Find label with specified identifier + * + * @return if found, pointer to label descriptor, + * otherwise - NULL. + */ +jsp_label_t* +jsp_label_find (jsp_label_type_flag_t type_mask, /**< types to search for */ + token id, /**< identifier of the label (TOK_NAME) + * if mask equals to JSP_LABEL_TYPE_NAMED, + * or empty token - otherwise + * (if so, mask should not include JSP_LABEL_TYPE_NAMED) */ + bool *out_is_simply_jumpable_p) /**< out: is the label currently + * accessible with a simple jump */ +{ + bool is_search_named = (type_mask == JSP_LABEL_TYPE_NAMED); + + if (is_search_named) + { + JERRY_ASSERT (id.type == TOK_NAME); + } + else + { + JERRY_ASSERT (!(type_mask & JSP_LABEL_TYPE_NAMED)); + JERRY_ASSERT (id.type == TOK_EMPTY); + } + + bool is_simply_jumpable = true; + jsp_label_t *ret_label_p = NULL; + + for (jsp_label_t *label_iter_p = label_set_p; + label_iter_p != NULL; + label_iter_p = label_iter_p->next_label_p) + { + if (label_iter_p->is_nested_jumpable_border) + { + is_simply_jumpable = false; + } + + bool is_named_label = (label_iter_p->type_mask & JSP_LABEL_TYPE_NAMED); + if ((is_search_named + && is_named_label + && lexer_are_tokens_with_same_identifier (label_iter_p->id, id)) + || (!is_search_named + && (type_mask & label_iter_p->type_mask))) + { + ret_label_p = label_iter_p; + + break; + } + } + + if (out_is_simply_jumpable_p != NULL) + { + *out_is_simply_jumpable_p = is_simply_jumpable; + } + + return ret_label_p; +} /* jsp_label_find */ + +/** + * Dump jump and register it in the specified label to be rewritten later (see also: jsp_label_rewrite_jumps_and_pop) + * + * Warning: + * The dumped instruction should not be modified before it is rewritten, as its idx fields are used + * to link jump instructions related to the label into singly linked list. + */ +void +jsp_label_add_jump (jsp_label_t *label_p, /**< label to register jump for */ + bool is_simply_jumpable, /**< is the label currently + * accessible with a simple jump */ + bool is_break) /**< type of jump - 'break' (true) or 'continue' (false) */ +{ + JERRY_ASSERT (label_p != NULL); + + if (is_break) + { + label_p->breaks_list_oc = dump_simple_or_nested_jump_for_rewrite (is_simply_jumpable, + label_p->breaks_list_oc); + label_p->breaks_number++; + } + else + { + label_p->continues_list_oc = dump_simple_or_nested_jump_for_rewrite (is_simply_jumpable, + label_p->continues_list_oc); + label_p->continues_number++; + } +} /* jsp_label_add_jump */ + +/** + * Setup target for 'continue' jumps, + * associated with the labels, from innermost + * to the specified label. + */ +void +jsp_label_setup_continue_target (jsp_label_t *outermost_label_p, /**< the outermost label to setup target for */ + opcode_counter_t tgt_oc) /**< target */ +{ + /* There are no labels that could not be targeted with 'break' jumps */ + JERRY_ASSERT (tgt_oc != MAX_OPCODES); + JERRY_ASSERT (outermost_label_p != NULL); + + for (jsp_label_t *label_iter_p = label_set_p; + label_iter_p != outermost_label_p->next_label_p; + label_iter_p = label_iter_p->next_label_p) + { + JERRY_ASSERT (label_iter_p != NULL); + JERRY_ASSERT (label_iter_p->continue_tgt_oc == MAX_OPCODES); + + label_iter_p->continue_tgt_oc = tgt_oc; + } +} /* jsp_label_setup_continue_target */ + +/** + * Mark current label, if any, as nested jumpable border + */ +void +jsp_label_raise_nested_jumpable_border (void) +{ + if (label_set_p != NULL) + { + JERRY_ASSERT (!label_set_p->is_nested_jumpable_border); + label_set_p->is_nested_jumpable_border = true; + } +} /* jsp_label_raise_nested_jumpable_border */ + +/** + * Unmark current label, if any, as nested jumpable border + */ +void +jsp_label_remove_nested_jumpable_border (void) +{ + if (label_set_p != NULL) + { + JERRY_ASSERT (label_set_p->is_nested_jumpable_border); + label_set_p->is_nested_jumpable_border = false; + } +} /* jsp_label_remove_nested_jumpable_border */ + +/** + * Mask current label set to restore it later, and start new label set + * + * @return pointer to masked label set's list of labels + */ +jsp_label_t* +jsp_label_mask_set (void) +{ + jsp_label_t *ret_p = label_set_p; + + label_set_p = NULL; + + return ret_p; +} /* jsp_label_mask_set */ + +/** + * Restore previously masked label set + * + * Note: + * current label set should be empty + */ +void +jsp_label_restore_set (jsp_label_t *masked_label_set_list_p) /**< list of labels of + * a masked label set */ +{ + JERRY_ASSERT (label_set_p == NULL); + + label_set_p = masked_label_set_list_p; +} /* jsp_label_restore_set */ + +/** + * @} + * @} + */ diff --git a/jerry-core/parser/js/jsp-label.h b/jerry-core/parser/js/jsp-label.h new file mode 100644 index 0000000000..dd05432187 --- /dev/null +++ b/jerry-core/parser/js/jsp-label.h @@ -0,0 +1,89 @@ +/* Copyright 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef JSP_LABEL_H +#define JSP_LABEL_H + +#include "lexer.h" +#include "opcodes.h" + +/** \addtogroup jsparser ECMAScript parser + * @{ + * + * \addtogroup labels Jump labels + * @{ + */ + +/** + * Label types + */ +typedef enum +{ + JSP_LABEL_TYPE_NAMED = (1 << 0), /**< label for breaks and continues with identifiers */ + JSP_LABEL_TYPE_UNNAMED_BREAKS = (1 << 1), /**< label for breaks without identifiers */ + JSP_LABEL_TYPE_UNNAMED_CONTINUES = (1 << 2) /**< label for continues without identifiers */ +} jsp_label_type_flag_t; + +/** + * Descriptor of a jump label (See also: ECMA-262 v5, 12.12, Labelled statements) + * + * Note: + * Jump instructions with target identified by some specific label, + * are linked into singly-linked list. + * + * Pointer to a next element of the list is represented with opcode counter. + * stored in instructions linked into the list. + * + */ +typedef struct jsp_label_t +{ + jsp_label_type_flag_t type_mask; /**< label type mask */ + token id; /**< label name (TOK_NAME), if type is LABEL_NAMED */ + opcode_counter_t continue_tgt_oc; /**< target opcode counter for continues on the label */ + opcode_counter_t breaks_list_oc; /**< opcode counter of first 'break' instruction in the list + * of instructions with the target identified by the label */ + opcode_counter_t breaks_number; /**< number of 'break' instructions in the list */ + opcode_counter_t continues_list_oc; /**< opcode counter of first 'continue' instruction in the list + * of instructions with the target identified by the label */ + opcode_counter_t continues_number; /**< number of 'continue' instructions in the list */ + jsp_label_t *next_label_p; /**< next label in current label set stack */ + bool is_nested_jumpable_border : 1; /**< flag, indicating that this and outer labels + * are not currently accessible with simple jumps, + * and so should be targetted with nested jumps only */ +} jsp_label_t; + +extern void jsp_label_init (void); +extern void jsp_label_finalize (void); + +extern void jsp_label_push (jsp_label_t *out_label_p, jsp_label_type_flag_t type_mask, token id); +extern void jsp_label_rewrite_jumps_and_pop (jsp_label_t *label_p, opcode_counter_t break_tgt_oc); + +extern jsp_label_t *jsp_label_find (jsp_label_type_flag_t type_mask, token id, bool *out_is_simply_jumpable_p); + +extern void jsp_label_add_jump (jsp_label_t *label_p, bool is_simply_jumpable, bool is_break); +extern void jsp_label_setup_continue_target (jsp_label_t *outermost_label_p, opcode_counter_t tgt_oc); + +extern void jsp_label_raise_nested_jumpable_border (void); +extern void jsp_label_remove_nested_jumpable_border (void); + +extern jsp_label_t *jsp_label_mask_set (void); +extern void jsp_label_restore_set (jsp_label_t *masked_label_set_list_p); + +/** + * @} + * @} + */ + +#endif /* !JSP_LABEL_H */ diff --git a/jerry-core/parser/js/lexer.cpp b/jerry-core/parser/js/lexer.cpp index 615acd0165..ad10b9c0ac 100644 --- a/jerry-core/parser/js/lexer.cpp +++ b/jerry-core/parser/js/lexer.cpp @@ -1596,6 +1596,26 @@ lexer_set_strict_mode (bool is_strict) strict_mode = is_strict; } +/** + * Check whether the identifier tokens represent the same identifiers + * + * Note: + * As all literals represent unique strings, + * it is sufficient to just check if literal indices + * in the tokens are equal. + * + * @return true / false + */ +bool +lexer_are_tokens_with_same_identifier (token id1, /**< identifier token (TOK_NAME) */ + token id2) /**< identifier token (TOK_NAME) */ +{ + JERRY_ASSERT (id1.type == TOK_NAME); + JERRY_ASSERT (id2.type == TOK_NAME); + + return (id1.uid == id2.uid); +} /* lexer_are_tokens_with_same_identifier */ + void lexer_init (const char *source, size_t source_size, bool show_opcodes) { diff --git a/jerry-core/parser/js/lexer.h b/jerry-core/parser/js/lexer.h index d987aef6fb..edc7b9762d 100644 --- a/jerry-core/parser/js/lexer.h +++ b/jerry-core/parser/js/lexer.h @@ -163,6 +163,11 @@ typedef struct literal_index_t uid; } token; +/** + * Initializer for empty token + */ +#define TOKEN_EMPTY_INITIALIZER {0, TOK_EMPTY, 0} + void lexer_init (const char *, size_t, bool); void lexer_free (void); @@ -185,4 +190,6 @@ const char *lexer_token_type_to_string (token_type); void lexer_set_strict_mode (bool); +extern bool lexer_are_tokens_with_same_identifier (token id1, token id2); + #endif diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index 18604aca0f..fb9a5edc11 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -76,30 +76,6 @@ enum }; STATIC_STACK (prop_getters, op_meta) -enum -{ - breaks_global_size -}; -STATIC_STACK (breaks, opcode_counter_t) - -enum -{ - break_targets_global_size -}; -STATIC_STACK (break_targets, opcode_counter_t) - -enum -{ - continues_global_size -}; -STATIC_STACK (continues, opcode_counter_t) - -enum -{ - continue_targets_global_size -}; -STATIC_STACK (continue_targets, opcode_counter_t) - enum { next_iterations_global_size @@ -2079,30 +2055,6 @@ dump_prop_setter_or_bitwise_or_res (operand res, operand op) return dump_prop_setter_or_triple_address_res (dump_bitwise_or, res, op); } -void -start_collecting_breaks (void) -{ - STACK_PUSH (U8, (uint8_t) STACK_SIZE (breaks)); -} - -void -start_collecting_continues (void) -{ - STACK_PUSH (U8, (uint8_t) STACK_SIZE (continues)); -} - -void -dumper_set_break_target (void) -{ - STACK_PUSH (break_targets, serializer_get_current_opcode_counter ()); -} - -void -dumper_set_continue_target (void) -{ - STACK_PUSH (continue_targets, serializer_get_current_opcode_counter ()); -} - void dumper_set_next_interation_target (void) { @@ -2143,126 +2095,83 @@ dump_continue_iterations_check (operand op) } /** - * Dump template of 'jmp_break_continue' instruction. + * Dump template of 'jmp_break_continue' or 'jmp_down' instruction (depending on is_simple_jump argument). * * Note: - * the instruction's flags field is written later (see also: rewrite_breaks, rewrite_continues). + * the instruction's flags field is written later (see also: rewrite_simple_or_nested_jump_get_next). * * @return position of dumped instruction */ -void -dump_break_continue_for_rewrite (bool is_break, /**< flag indicating whether 'break' should be dumped (true) - * or 'continue' (false) */ - bool is_simple_jump) /**< flag indicating, whether simple jump - * or 'jmp_break_continue' template should be dumped */ +opcode_counter_t +dump_simple_or_nested_jump_for_rewrite (bool is_simple_jump, /**< flag indicating, whether simple jump + * or 'jmp_break_continue' template should be dumped */ + opcode_counter_t next_jump_for_tgt_oc) /**< opcode counter of next + * template targetted to + * the same target - if any, + * or MAX_OPCODES - otherwise */ { - if (is_break) - { - STACK_PUSH (breaks, serializer_get_current_opcode_counter ()); - } - else - { - STACK_PUSH (continues, serializer_get_current_opcode_counter ()); - } + idx_t id1, id2; + split_opcode_counter (next_jump_for_tgt_oc, &id1, &id2); opcode_t opcode; if (is_simple_jump) { - opcode = getop_jmp_down (INVALID_VALUE, INVALID_VALUE); + opcode = getop_jmp_down (id1, id2); } else { - opcode = getop_jmp_break_continue (INVALID_VALUE, INVALID_VALUE); + opcode = getop_jmp_break_continue (id1, id2); } + opcode_counter_t ret = serializer_get_current_opcode_counter (); + serializer_dump_op_meta (create_op_meta_000 (opcode)); -} /* dump_break_continue_for_rewrite */ + + return ret; +} /* dump_simple_or_nested_jump_for_rewrite */ /** - * Write jump target positions into previously dumped templates of instructions - * for currently processed set of 'break' statements. + * Write jump target position into previously dumped template of jump (simple or nested) instruction * - * See also: - * dump_break_continue_for_rewrite - * start_collecting_breaks + * @return opcode counter value that was encoded in the jump before rewrite */ -void -rewrite_breaks (void) +opcode_counter_t +rewrite_simple_or_nested_jump_and_get_next (opcode_counter_t jump_oc, /**< position of jump to rewrite */ + opcode_counter_t target_oc) /**< the jump's target */ { - const opcode_counter_t break_target = STACK_TOP (break_targets); + op_meta jump_op_meta = serializer_get_op_meta (jump_oc); - STACK_ITERATE (breaks, break_oc, STACK_TOP (U8)) - { - idx_t id1, id2; - split_opcode_counter ((opcode_counter_t) (break_target - break_oc), &id1, &id2); - op_meta break_op_meta = serializer_get_op_meta (break_oc); + bool is_simple_jump = (jump_op_meta.op.op_idx == OPCODE (jmp_down)); - bool is_simple_jump = (break_op_meta.op.op_idx == OPCODE (jmp_down)); + JERRY_ASSERT (is_simple_jump + || (jump_op_meta.op.op_idx == OPCODE (jmp_break_continue))); - if (is_simple_jump) - { - break_op_meta.op.data.jmp_down.opcode_1 = id1; - break_op_meta.op.data.jmp_down.opcode_2 = id2; - } - else - { - JERRY_ASSERT (break_op_meta.op.op_idx == OPCODE (jmp_break_continue)); + idx_t id1, id2, id1_prev, id2_prev; + split_opcode_counter ((opcode_counter_t) (target_oc - jump_oc), &id1, &id2); - break_op_meta.op.data.jmp_break_continue.opcode_1 = id1; - break_op_meta.op.data.jmp_break_continue.opcode_2 = id2; - } + if (is_simple_jump) + { + id1_prev = jump_op_meta.op.data.jmp_down.opcode_1; + id2_prev = jump_op_meta.op.data.jmp_down.opcode_2; - serializer_rewrite_op_meta (break_oc, break_op_meta); + jump_op_meta.op.data.jmp_down.opcode_1 = id1; + jump_op_meta.op.data.jmp_down.opcode_2 = id2; } - STACK_ITERATE_END (); - - STACK_DROP (break_targets, 1); - STACK_DROP (breaks, STACK_SIZE (breaks) - STACK_TOP (U8)); - STACK_DROP (U8, 1); -} /* rewrite_breaks */ - -/** - * Write jump target positions into previously dumped templates of instructions - * for currently processed set of 'continue' statements. - * - * See also: - * dump_break_continue_for_rewrite - * start_collecting_continues - */ -void -rewrite_continues (void) -{ - const opcode_counter_t continue_target = STACK_TOP (continue_targets); - - STACK_ITERATE (continues, continue_oc, STACK_TOP (U8)) + else { - idx_t id1, id2; - split_opcode_counter ((opcode_counter_t) (continue_target - continue_oc), &id1, &id2); - op_meta continue_op_meta = serializer_get_op_meta (continue_oc); - - bool is_simple_jump = (continue_op_meta.op.op_idx == OPCODE (jmp_down)); + JERRY_ASSERT (jump_op_meta.op.op_idx == OPCODE (jmp_break_continue)); - if (is_simple_jump) - { - continue_op_meta.op.data.jmp_down.opcode_1 = id1; - continue_op_meta.op.data.jmp_down.opcode_2 = id2; - } - else - { - JERRY_ASSERT (continue_op_meta.op.op_idx == OPCODE (jmp_break_continue)); + id1_prev = jump_op_meta.op.data.jmp_break_continue.opcode_1; + id2_prev = jump_op_meta.op.data.jmp_break_continue.opcode_2; - continue_op_meta.op.data.jmp_break_continue.opcode_1 = id1; - continue_op_meta.op.data.jmp_break_continue.opcode_2 = id2; - } - - serializer_rewrite_op_meta (continue_oc, continue_op_meta); + jump_op_meta.op.data.jmp_break_continue.opcode_1 = id1; + jump_op_meta.op.data.jmp_break_continue.opcode_2 = id2; } - STACK_ITERATE_END (); - STACK_DROP (continue_targets, 1); - STACK_DROP (continues, STACK_SIZE (continues) - STACK_TOP (U8)); - STACK_DROP (U8, 1); -} /* rewrite_continues */ + serializer_rewrite_op_meta (jump_oc, jump_op_meta); + + return calc_opcode_counter_from_idx_idx (id1_prev, id2_prev); +} /* rewrite_simple_or_nested_jump_get_next */ void start_dumping_case_clauses (void) @@ -2576,10 +2485,6 @@ dumper_init (void) STACK_INIT (conditional_checks); STACK_INIT (jumps_to_end); STACK_INIT (prop_getters); - STACK_INIT (breaks); - STACK_INIT (continues); - STACK_INIT (break_targets); - STACK_INIT (continue_targets); STACK_INIT (next_iterations); STACK_INIT (case_clauses); STACK_INIT (catches); @@ -2600,10 +2505,6 @@ dumper_free (void) STACK_FREE (conditional_checks); STACK_FREE (jumps_to_end); STACK_FREE (prop_getters); - STACK_FREE (breaks); - STACK_FREE (continues); - STACK_FREE (break_targets); - STACK_FREE (continue_targets); STACK_FREE (next_iterations); STACK_FREE (case_clauses); STACK_FREE (catches); diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index e8981c3e65..0f96b91b85 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -178,14 +178,15 @@ operand dump_prop_setter_or_bitwise_and_res (operand, operand); operand dump_prop_setter_or_bitwise_xor_res (operand, operand); operand dump_prop_setter_or_bitwise_or_res (operand, operand); -void start_collecting_breaks (void); -void start_collecting_continues (void); void dumper_set_break_target (void); void dumper_set_continue_target (void); void dumper_set_next_interation_target (void); -void dump_break_continue_for_rewrite (bool is_break, bool is_simple_jump); -void rewrite_continues (void); -void rewrite_breaks (void); +opcode_counter_t +dump_simple_or_nested_jump_for_rewrite (bool is_simple_jump, + opcode_counter_t next_jump_for_tg_oc); +opcode_counter_t +rewrite_simple_or_nested_jump_and_get_next (opcode_counter_t jump_oc, + opcode_counter_t target_oc); void dump_continue_iterations_check (operand); void start_dumping_case_clauses (void); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 2728de90a0..77742ff7cd 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -16,6 +16,7 @@ #include #include "jrt-libc-includes.h" +#include "jsp-label.h" #include "parser.h" #include "opcodes.h" #include "serializer.h" @@ -29,34 +30,8 @@ #include "opcodes-dumper.h" #include "serializer.h" -/** - * Nesting types - * - * Note: - * Nesting is an element, describing classes of syntax blocks, handled by parser. - * - * Nestings are pushed to the nestings stack upon entering them, and popped upon leaving. - * - * The top-most nesting, if any, describes the inner-most syntax block of specified type, - * currently reached by parser. - */ -typedef enum -{ - NESTING_ITERATIONAL, /**< an iterational (for, for-in, while, do-while) statement */ - NESTING_SWITCH, /**< switch-case block */ - NESTING_FUNCTION, /**< function */ - NESTING_TRY, /**< try-catch-finally block */ - NESTING_WITH /**< with block */ -} nesting_t; - static token tok; -enum -{ - nestings_global_size -}; -STATIC_STACK (nestings, nesting_t) - enum { scopes_global_size @@ -67,22 +42,10 @@ STATIC_STACK (scopes, scopes_tree) #define EMIT_SORRY(MESSAGE) PARSE_SORRY(MESSAGE, tok.loc) #define EMIT_ERROR_VARG(MESSAGE, ...) PARSE_ERROR_VARG(MESSAGE, tok.loc, __VA_ARGS__) -#define NESTING_TO_STRING(I) (I == NESTING_FUNCTION \ - ? "function" \ - : I == NESTING_ITERATIONAL \ - ? "iterational" \ - : I == NESTING_SWITCH \ - ? "switch" \ - : I == NESTING_TRY \ - ? "try" \ - : I == NESTING_WITH \ - ? "with" \ - : "unknown") - #define OPCODE_IS(OP, ID) (OP.op_idx == __op__idx_##ID) static operand parse_expression (bool); -static void parse_statement (void); +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 *); @@ -90,64 +53,6 @@ static void process_keyword_names (void); static void skip_braces (void); static void skip_parens (void); -/** - * Push a nesting to the nesting stack, so setting new current nesting - */ -static void -push_nesting (nesting_t nesting_type) /**< type of new nesting */ -{ - STACK_PUSH (nestings, nesting_type); -} /* push_nesting */ - -/** - * Restore nesting from nestings stack - */ -static void -pop_nesting (nesting_t nesting_type) /**< type of current nesting */ -{ - JERRY_ASSERT (STACK_HEAD (nestings, 1) == nesting_type); - STACK_DROP (nestings, 1); -} /* pop_nesting */ - -/** - * Check that current nesting chain contains one of the specified 'inside' nesting types, - * and there is no 'not_in' nesting between current nesting and the 'inside' nesting type - * in the chain. - */ -static void -must_be_inside_but_not_in (uint8_t not_in, /**< 'not_in' nesting type */ - uint8_t insides_count, /**< 'inside' nestings number */ - ...) /**< 'inside' nestings list */ -{ - JERRY_ASSERT (STACK_SIZE (nestings) != 0); - - va_list insides_list; - va_start (insides_list, insides_count); - uint8_t *insides = (uint8_t*) mem_heap_alloc_block (insides_count, MEM_HEAP_ALLOC_SHORT_TERM); - for (uint8_t i = 0; i < insides_count; i++) - { - insides[i] = (uint8_t) va_arg (insides_list, int); - } - va_end (insides_list); - - for (uint8_t i = (uint8_t) STACK_SIZE (nestings); i != 0; i--) - { - for (uint8_t j = 0; j < insides_count; j++) - { - if (insides[j] == STACK_ELEMENT (nestings, i - 1)) - { - mem_heap_free_block (insides); - return; - } - } - if (STACK_ELEMENT (nestings, i - 1) == not_in) - { - EMIT_ERROR_VARG ("Shall not be inside a '%s' nesting", NESTING_TO_STRING (not_in)); - } - } - EMIT_ERROR ("Shall be inside a nesting"); -} /* must_be_inside_but_not_in */ - static bool token_is (token_type tt) { @@ -298,8 +203,6 @@ parse_property_name_and_value (void) static void parse_property_assignment (void) { - STACK_DECLARE_USAGE (nestings); - if (token_is (TOK_NAME)) { bool is_setter; @@ -316,7 +219,7 @@ parse_property_assignment (void) { parse_property_name_and_value (); - goto cleanup; + return; } const token temp = tok; @@ -328,7 +231,7 @@ parse_property_assignment (void) parse_property_name_and_value (); - goto cleanup; + return; } const operand name = parse_property_name (); @@ -344,9 +247,13 @@ parse_property_assignment (void) token_after_newlines_must_be (TOK_OPEN_BRACE); skip_newlines (); - push_nesting (NESTING_FUNCTION); + + jsp_label_t *masked_label_set_p = jsp_label_mask_set (); + parse_source_element_list (false); - pop_nesting (NESTING_FUNCTION); + + jsp_label_restore_set (masked_label_set_p); + token_after_newlines_must_be (TOK_CLOSE_BRACE); scopes_tree_set_strict_mode (STACK_TOP (scopes), is_strict); @@ -366,9 +273,6 @@ parse_property_assignment (void) { parse_property_name_and_value (); } - -cleanup: - STACK_CHECK_USAGE (nestings); } /** Parse list of identifiers, assigment expressions or properties, splitted by comma. @@ -541,10 +445,11 @@ static void parse_function_declaration (void) { STACK_DECLARE_USAGE (scopes); - STACK_DECLARE_USAGE (nestings); assert_keyword (KW_FUNCTION); + jsp_label_t *masked_label_set_p = jsp_label_mask_set (); + token_after_newlines_must_be (TOK_NAME); const operand name = literal_operand (token_data ()); @@ -560,9 +465,8 @@ parse_function_declaration (void) token_after_newlines_must_be (TOK_OPEN_BRACE); skip_newlines (); - push_nesting (NESTING_FUNCTION); + parse_source_element_list (false); - pop_nesting (NESTING_FUNCTION); next_token_must_be (TOK_CLOSE_BRACE); @@ -573,8 +477,9 @@ parse_function_declaration (void) serializer_set_scope (STACK_TOP (scopes)); lexer_set_strict_mode (scopes_tree_strict_mode (STACK_TOP (scopes))); + jsp_label_restore_set (masked_label_set_p); + STACK_CHECK_USAGE (scopes); - STACK_CHECK_USAGE (nestings); } /* function_expression @@ -583,8 +488,6 @@ parse_function_declaration (void) static operand parse_function_expression (void) { - STACK_DECLARE_USAGE (nestings) - assert_keyword (KW_FUNCTION); operand res; @@ -607,16 +510,19 @@ parse_function_expression (void) token_after_newlines_must_be (TOK_OPEN_BRACE); skip_newlines (); - push_nesting (NESTING_FUNCTION); + + jsp_label_t *masked_label_set_p = jsp_label_mask_set (); + parse_source_element_list (false); - pop_nesting (NESTING_FUNCTION); + + jsp_label_restore_set (masked_label_set_p); + + next_token_must_be (TOK_CLOSE_BRACE); dump_ret (); rewrite_function_end (VARG_FUNC_EXPR); - STACK_CHECK_USAGE (nestings); - return res; } @@ -1693,27 +1599,10 @@ parse_variable_declaration_list (bool *several_decls) } static void -parse_plain_for (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) */ { - /* Represent loop like - - for (i = 0; i < 10; i++) { - body; - } - - as - - assign i, 0 - jmp_down %cond -%body - body - post_incr i -%cond - less_than i, 10 - is_true_jmp_up %body - - */ - dump_jump_to_end_for_rewrite (); // Skip till body @@ -1731,19 +1620,16 @@ parse_plain_for (void) skip_newlines (); } - start_collecting_continues (); - start_collecting_breaks (); dumper_set_next_interation_target (); // Parse body skip_newlines (); - push_nesting (NESTING_ITERATIONAL); - parse_statement (); - pop_nesting (NESTING_ITERATIONAL); + parse_statement (NULL); const locus end_loc = tok.loc; - dumper_set_continue_target (); + jsp_label_setup_continue_target (outermost_stmt_label_p, + serializer_get_current_opcode_counter ()); lexer_seek (incr_loc); skip_token (); @@ -1766,10 +1652,6 @@ parse_plain_for (void) dump_continue_iterations_check (cond); } - dumper_set_break_target (); - rewrite_breaks (); - rewrite_continues (); - lexer_seek (end_loc); skip_token (); if (tok.type != TOK_CLOSE_BRACE) @@ -1779,8 +1661,12 @@ parse_plain_for (void) } static void -parse_for_in (void) +parse_for_in (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) */ { + (void) outermost_stmt_label_p; + EMIT_SORRY ("'for in' loops are not supported yet"); } @@ -1805,7 +1691,9 @@ parse_for_in (void) ;*/ static void -parse_for_or_for_in_statement (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) */ { assert_keyword (KW_FOR); token_after_newlines_must_be (TOK_OPEN_PAREN); @@ -1813,7 +1701,7 @@ parse_for_or_for_in_statement (void) skip_newlines (); if (token_is (TOK_SEMICOLON)) { - parse_plain_for (); + parse_plain_for (outermost_stmt_label_p); return; } /* Both for_statement_initialiser_part and for_in_statement_initialiser_part @@ -1826,7 +1714,7 @@ parse_for_or_for_in_statement (void) if (several_decls) { token_after_newlines_must_be (TOK_SEMICOLON); - parse_plain_for (); + parse_plain_for (outermost_stmt_label_p); return; } else @@ -1834,12 +1722,12 @@ parse_for_or_for_in_statement (void) skip_newlines (); if (token_is (TOK_SEMICOLON)) { - parse_plain_for (); + parse_plain_for (outermost_stmt_label_p); return; } else if (is_keyword (KW_IN)) { - parse_for_in (); + parse_for_in (outermost_stmt_label_p); return; } else @@ -1855,12 +1743,12 @@ parse_for_or_for_in_statement (void) skip_newlines (); if (token_is (TOK_SEMICOLON)) { - parse_plain_for (); + parse_plain_for (outermost_stmt_label_p); return; } else if (is_keyword (KW_IN)) { - parse_for_in (); + parse_for_in (outermost_stmt_label_p); return; } else @@ -1887,7 +1775,7 @@ parse_statement_list (void) { while (true) { - parse_statement (); + parse_statement (NULL); skip_newlines (); while (token_is (TOK_SEMICOLON)) @@ -1919,7 +1807,7 @@ parse_if_statement (void) dump_conditional_check_for_rewrite (cond); skip_newlines (); - parse_statement (); + parse_statement (NULL); skip_newlines (); if (is_keyword (KW_ELSE)) @@ -1928,7 +1816,7 @@ parse_if_statement (void) rewrite_conditional_check (); skip_newlines (); - parse_statement (); + parse_statement (NULL); rewrite_jump_to_end (); } @@ -1943,37 +1831,32 @@ parse_if_statement (void) : 'do' LT!* statement LT!* 'while' LT!* '(' expression ')' (LT | ';')! ; */ static void -parse_do_while_statement (void) +parse_do_while_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) */ { assert_keyword (KW_DO); - start_collecting_continues (); - start_collecting_breaks (); - dumper_set_next_interation_target (); skip_newlines (); - push_nesting (NESTING_ITERATIONAL); - parse_statement (); - pop_nesting (NESTING_ITERATIONAL); + parse_statement (NULL); - dumper_set_continue_target (); + jsp_label_setup_continue_target (outermost_stmt_label_p, + serializer_get_current_opcode_counter ()); token_after_newlines_must_be_keyword (KW_WHILE); const operand cond = parse_expression_inside_parens (); dump_continue_iterations_check (cond); - - dumper_set_break_target (); - - rewrite_breaks (); - rewrite_continues (); } /* while_statement : 'while' LT!* '(' LT!* expression LT!* ')' LT!* statement ; */ static void -parse_while_statement (void) +parse_while_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) */ { assert_keyword (KW_WHILE); @@ -1985,15 +1868,11 @@ parse_while_statement (void) dumper_set_next_interation_target (); - start_collecting_continues (); - start_collecting_breaks (); - skip_newlines (); - push_nesting (NESTING_ITERATIONAL); - parse_statement (); - pop_nesting (NESTING_ITERATIONAL); + parse_statement (NULL); - dumper_set_continue_target (); + jsp_label_setup_continue_target (outermost_stmt_label_p, + serializer_get_current_opcode_counter ()); rewrite_jump_to_end (); @@ -2002,11 +1881,6 @@ parse_while_statement (void) const operand cond = parse_expression_inside_parens (); dump_continue_iterations_check (cond); - dumper_set_break_target (); - - rewrite_breaks (); - rewrite_continues (); - lexer_seek (end_loc); skip_token (); } @@ -2024,27 +1898,29 @@ parse_with_statement (void) } const operand expr = parse_expression_inside_parens (); - push_nesting (NESTING_WITH); + jsp_label_raise_nested_jumpable_border (); opcode_counter_t with_begin_oc = dump_with_for_rewrite (expr); skip_newlines (); - parse_statement (); + parse_statement (NULL); rewrite_with (with_begin_oc); dump_with_end (); - pop_nesting (NESTING_WITH); + jsp_label_remove_nested_jumpable_border (); } static void skip_case_clause_body (void) { - while (!is_keyword (KW_CASE) && !is_keyword (KW_DEFAULT) && !token_is (TOK_CLOSE_BRACE)) + while (!is_keyword (KW_CASE) + && !is_keyword (KW_DEFAULT) + && !token_is (TOK_CLOSE_BRACE)) { - skip_newlines (); if (token_is (TOK_OPEN_BRACE)) { skip_braces (); } + skip_newlines (); } } @@ -2104,8 +1980,11 @@ parse_switch_statement (void) lexer_seek (start_loc); next_token_must_be (TOK_OPEN_BRACE); - start_collecting_breaks (); - push_nesting (NESTING_SWITCH); + jsp_label_t label; + jsp_label_push (&label, + JSP_LABEL_TYPE_UNNAMED_BREAKS, + TOKEN_EMPTY_INITIALIZER); + // Second, parse case clauses' bodies and rewrite jumps skip_newlines (); while (is_keyword (KW_CASE) || is_keyword (KW_DEFAULT)) @@ -2140,10 +2019,10 @@ parse_switch_statement (void) } current_token_must_be (TOK_CLOSE_BRACE); skip_token (); - pop_nesting (NESTING_SWITCH); - dumper_set_break_target (); - rewrite_breaks (); + jsp_label_rewrite_jumps_and_pop (&label, + serializer_get_current_opcode_counter ()); + finish_dumping_case_clauses (); } @@ -2197,7 +2076,7 @@ parse_try_statement (void) { assert_keyword (KW_TRY); - push_nesting (NESTING_TRY); + jsp_label_raise_nested_jumpable_border (); dump_try_for_rewrite (); @@ -2234,7 +2113,7 @@ parse_try_statement (void) dump_end_try_catch_finally (); - pop_nesting (NESTING_TRY); + jsp_label_remove_nested_jumpable_border (); } static void @@ -2258,6 +2137,45 @@ insert_semicolon (void) } } +/** + * iteration_statement + * : do_while_statement + * | while_statement + * | for_statement + * | for_in_statement + * ; + */ +static void +parse_iterational_statement (jsp_label_t *outermost_named_stmt_label_p) /**< outermost (first) named label, + * corresponding to the statement + * (or NULL, if there are no named + * labels associated with the statement) */ +{ + jsp_label_t label; + jsp_label_push (&label, + (jsp_label_type_flag_t) (JSP_LABEL_TYPE_UNNAMED_BREAKS | JSP_LABEL_TYPE_UNNAMED_CONTINUES), + TOKEN_EMPTY_INITIALIZER); + + jsp_label_t *outermost_stmt_label_p = (outermost_named_stmt_label_p != NULL ? outermost_named_stmt_label_p : &label); + + if (is_keyword (KW_DO)) + { + parse_do_while_statement (outermost_stmt_label_p); + } + else if (is_keyword (KW_WHILE)) + { + parse_while_statement (outermost_stmt_label_p); + } + else + { + JERRY_ASSERT (is_keyword (KW_FOR)); + parse_for_or_for_in_statement (outermost_stmt_label_p); + } + + jsp_label_rewrite_jumps_and_pop (&label, + serializer_get_current_opcode_counter ()); +} /* parse_iterational_statement */ + /* statement : statement_block | variable_statement @@ -2322,7 +2240,9 @@ insert_semicolon (void) : 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?) ;*/ static void -parse_statement (void) +parse_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) */ { dumper_new_statement (); @@ -2365,19 +2285,11 @@ parse_statement (void) parse_if_statement (); return; } - if (is_keyword (KW_DO)) - { - parse_do_while_statement (); - return; - } - if (is_keyword (KW_WHILE)) - { - parse_while_statement (); - return; - } - if (is_keyword (KW_FOR)) + if (is_keyword (KW_DO) + || is_keyword (KW_WHILE) + || is_keyword (KW_FOR)) { - parse_for_or_for_in_statement (); + parse_iterational_statement (outermost_stmt_label_p); return; } if (is_keyword (KW_CONTINUE) @@ -2385,45 +2297,49 @@ parse_statement (void) { bool is_break = is_keyword (KW_BREAK); - if (STACK_SIZE (nestings) == 0) - { - EMIT_ERROR ("Shall be inside a nesting"); - } - - nesting_t topmost_nesting = STACK_ELEMENT (nestings, STACK_SIZE (nestings) - 1); + skip_token (); - if (is_break) + jsp_label_t *label_p; + bool is_simply_jumpable = true; + if (token_is (TOK_NAME)) { - must_be_inside_but_not_in (NESTING_FUNCTION, - 4, - NESTING_ITERATIONAL, - NESTING_SWITCH, - NESTING_TRY, - NESTING_WITH); + /* break or continue on a label */ + label_p = jsp_label_find (JSP_LABEL_TYPE_NAMED, tok, &is_simply_jumpable); + + if (label_p == NULL) + { + EMIT_ERROR ("Label not found"); + } } - else + else if (is_break) { - must_be_inside_but_not_in (NESTING_FUNCTION, - 3, - NESTING_ITERATIONAL, - NESTING_TRY, - NESTING_WITH); - } + label_p = jsp_label_find (JSP_LABEL_TYPE_UNNAMED_BREAKS, + TOKEN_EMPTY_INITIALIZER, + &is_simply_jumpable); - if (topmost_nesting == NESTING_ITERATIONAL - || (topmost_nesting == NESTING_SWITCH && is_break)) - { - dump_break_continue_for_rewrite (is_break, true); + if (label_p == NULL) + { + EMIT_ERROR ("No corresponding statement for the break"); + } } else { - JERRY_ASSERT (topmost_nesting == NESTING_TRY - || topmost_nesting == NESTING_WITH - || (topmost_nesting == NESTING_SWITCH && !is_break)); + JERRY_ASSERT (!is_break); + + label_p = jsp_label_find (JSP_LABEL_TYPE_UNNAMED_CONTINUES, + TOKEN_EMPTY_INITIALIZER, + &is_simply_jumpable); - dump_break_continue_for_rewrite (is_break, false); + if (label_p == NULL) + { + EMIT_ERROR ("No corresponding statement for the continue"); + } } + JERRY_ASSERT (label_p != NULL); + + jsp_label_add_jump (label_p, is_simply_jumpable, is_break); + return; } if (is_keyword (KW_RETURN)) @@ -2471,8 +2387,21 @@ parse_statement (void) skip_newlines (); if (token_is (TOK_COLON)) { - // STMT_LABELLED; - EMIT_SORRY ("Labelled statements are not supported yet"); + skip_newlines (); + + jsp_label_t *label_p = jsp_label_find (JSP_LABEL_TYPE_NAMED, temp, NULL); + if (label_p != NULL) + { + EMIT_ERROR ("Label is duplicated"); + } + + jsp_label_t label; + jsp_label_push (&label, JSP_LABEL_TYPE_NAMED, temp); + + parse_statement (outermost_stmt_label_p != NULL ? outermost_stmt_label_p : &label); + + jsp_label_rewrite_jumps_and_pop (&label, + serializer_get_current_opcode_counter ()); } else { @@ -2512,7 +2441,7 @@ parse_source_element (void) } else { - parse_statement (); + parse_statement (NULL); } } @@ -2862,14 +2791,16 @@ parser_init (const char *source, size_t source_size, bool show_opcodes) dumper_init (); syntax_init (); - STACK_INIT (nestings); STACK_INIT (scopes); + + jsp_label_init (); } void parser_free (void) { - STACK_FREE (nestings); + jsp_label_finalize (); + STACK_FREE (scopes); syntax_free (); diff --git a/tests/jerry/fail/12/labelled_statements_break_across_function.js b/tests/jerry/fail/12/labelled_statements_break_across_function.js new file mode 100644 index 0000000000..3504c36ea9 --- /dev/null +++ b/tests/jerry/fail/12/labelled_statements_break_across_function.js @@ -0,0 +1,26 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// +// 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. + +a: for (var i = 0; i < 10; i++) +{ + function f () + { + for (var j = 0; n < 10; j++) + { + break a; + } + } + + f (); +} diff --git a/tests/jerry/fail/12/labelled_statements_duplicate_label.js b/tests/jerry/fail/12/labelled_statements_duplicate_label.js new file mode 100644 index 0000000000..3d61c19df4 --- /dev/null +++ b/tests/jerry/fail/12/labelled_statements_duplicate_label.js @@ -0,0 +1,18 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// +// 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. + +a: a: for (var i = 0; i < 10; i++) +{ + break a; +} diff --git a/tests/jerry/fail/12/labelled_statements_no_label.js b/tests/jerry/fail/12/labelled_statements_no_label.js new file mode 100644 index 0000000000..f580bcaa23 --- /dev/null +++ b/tests/jerry/fail/12/labelled_statements_no_label.js @@ -0,0 +1,18 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// +// 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. + +for (var i = 0; i < 10; i++) +{ + break a; +} diff --git a/tests/jerry/labelled_statements.js b/tests/jerry/labelled_statements.js new file mode 100644 index 0000000000..a0a707037e --- /dev/null +++ b/tests/jerry/labelled_statements.js @@ -0,0 +1,320 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// +// 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. + +/* 1 */ + +a: a = 1; + +str = ''; + +a: for (j = 0; j < 10; j++) +{ + str += 'A'; + + b: for (i = 0; i < 10; i++) + { + str += 'B'; + + break a; + + str += 'C'; + } + + str += 'D'; +} + +assert (str === 'AB'); + +/* 2 */ +str = ''; + +a: for (j = 0; j < 5; j++) +{ + str += 'A'; + + b: for (i = 0; i < 5; i++) + { + str += 'B'; + + switch (1) + { + case 1: + continue b; + default: + break b; + } + + str += 'C'; + } + + str += 'D'; +} + +assert (str === 'ABBBBBDABBBBBDABBBBBDABBBBBDABBBBBD'); + +/* 3 */ +str = ''; + +a: for (j = 0; j < 5; j++) +{ + str += 'A'; + + b: for (i = 0; i < 5; i++) + { + str += 'B'; + + switch (1) + { + case 1: + continue a; + } + + str += 'C'; + } + + str += 'D'; +} + +assert (str === 'ABABABABAB'); + +/* 4 */ +str = ''; + +a: for (j = 0; j < 5; j++) +{ + str += 'A'; + + b: for (i = 0; i < 5; i++) + { + str += 'B'; + + switch (1) + { + case 1: + break b; + } + + str += 'C'; + } + + str += 'D'; +} + +assert (str === 'ABDABDABDABDABD'); + +/* 5 */ +str = ''; + +a: for (j = 0; j < 5; j++) +{ + str += 'A'; + + b: for (i = 0; i < 5; i++) + { + str += 'B'; + + switch (1) + { + case 1: + break a; + } + + str += 'C'; + } + + str += 'D'; +} + +assert (str === 'AB'); + + +/* 6 */ +str = ''; + +a: for (j = 0; j < 5; j++) +{ + str += 'A'; + + b: for (i = 0; i < 5; i++) + { + str += 'B'; + + with ({}) + { + break b; + } + + str += 'C'; + } + + str += 'D'; +} + +assert (str === 'ABDABDABDABDABD'); + +/* 7 */ +str = ''; + +a: for (j = 0; j < 5; j++) +{ + c: + { + str += 'A'; + + b: for (i = 0; i < 5; i++) + { + str += 'B'; + + with ({}) + { + break c; + } + + str += 'C'; + } + + str += 'D'; + } +} + +assert (str === 'ABABABABAB'); + +/* 8 */ + +a: { + function f () + { + str = ''; + + a: for (i = 0; i < 5; i++) + { + str += 'A'; + + for (j = 0; j < 5; j++) + { + str += 'B'; + + continue a; + + str += 'C'; + } + str += 'D'; + } + + assert (str === 'ABABABABAB'); + } +} + +f (); + +/* 9 */ + +str = ''; + +for (i = 0; i < 5; i++) +{ + str += 'A'; + + switch (i) + { + case 0: + { + str += '0'; + break; + } + case 1: + { + str += '1'; + break; + } + case 2: + { + str += '2'; + break; + } + case 3: + { + str += '3'; + break; + } + case 4: + { + str += '4'; + break; + } + } + + str += 'B'; +} + +assert (str === 'A0BA1BA2BA3BA4B'); + +/* 10 */ + +str = ''; + +for (i = 0; i < 2; i++) +{ + str += '[A]'; + + a: + for (j = 0; j < 5; j++) + { + str += '[B]'; + + switch (j) + { + case 0: + { + str += '[0]'; + break; + } + case 1: + { + str += '[1]'; + if (i % 2 == 0) + { + str += '[1.1]'; + break a; + } + else + { + str += '[1.2]'; + } + } + case 2: + { + str += '[2]'; + continue a; + } + case 3: + { + str += '[3]'; + break; + } + case 4: + { + str += '[4]'; + continue a; + } + } + + str += '[C]'; + } + + str += '[D]'; +} + +assert (str === '[A][B][0][C][B][1][1.1][D]' + + '[A][B][0][C][B][1][1.2][2][B][2][B][3][C][B][4][D]');