From 712f8b0c9a58a0b7749a3c1640eb2dbcd7ed96ee Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Mon, 1 Jun 2015 13:12:07 +0300 Subject: [PATCH 1/3] Introducing 'try' and 'with' nestings in parser. JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- jerry-core/parser/js/parser.cpp | 67 ++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 13493a4d24..52fbf9c73b 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -29,9 +29,25 @@ #include "opcodes-dumper.h" #include "serializer.h" -#define NESTING_ITERATIONAL 1 -#define NESTING_SWITCH 2 -#define NESTING_FUNCTION 3 +/** + * 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; @@ -39,7 +55,7 @@ enum { nestings_global_size }; -STATIC_STACK (nestings, uint8_t) +STATIC_STACK (nestings, nesting_t) enum { @@ -56,7 +72,12 @@ STATIC_STACK (scopes, scopes_tree) : I == NESTING_ITERATIONAL \ ? "iterational" \ : I == NESTING_SWITCH \ - ? "switch" : "unknown") + ? "switch" \ + : I == NESTING_TRY \ + ? "try" \ + : I == NESTING_WITH \ + ? "with" \ + : "unknown") #define OPCODE_IS(OP, ID) (OP.op_idx == __op__idx_##ID) @@ -69,18 +90,24 @@ 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 (uint8_t nesting_type) +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 (uint8_t nesting_type) +pop_nesting (nesting_t nesting_type) /**< type of current nesting */ { JERRY_ASSERT (STACK_HEAD (nestings, 1) == nesting_type); STACK_DROP (nestings, 1); -} +} /* pop_nesting */ static void must_be_inside_but_not_in (uint8_t not_in, uint8_t insides_count, ...) @@ -1992,10 +2019,15 @@ parse_with_statement (void) EMIT_ERROR ("'with' expression is not allowed in strict mode."); } const operand expr = parse_expression_inside_parens (); + + push_nesting (NESTING_WITH); + dump_with (expr); skip_newlines (); parse_statement (); dump_with_end (); + + pop_nesting (NESTING_WITH); } static void @@ -2160,6 +2192,8 @@ parse_try_statement (void) { assert_keyword (KW_TRY); + push_nesting (NESTING_TRY); + dump_try_for_rewrite (); token_after_newlines_must_be (TOK_OPEN_BRACE); @@ -2194,6 +2228,8 @@ parse_try_statement (void) } dump_end_try_catch_finally (); + + pop_nesting (NESTING_TRY); } static void @@ -2341,13 +2377,24 @@ parse_statement (void) } if (is_keyword (KW_CONTINUE)) { + must_be_inside_but_not_in (NESTING_FUNCTION, + 4, + NESTING_ITERATIONAL, + NESTING_TRY, + NESTING_WITH); + must_be_inside_but_not_in (NESTING_FUNCTION, 1, NESTING_ITERATIONAL); dump_continue_for_rewrite (); return; } if (is_keyword (KW_BREAK)) { - must_be_inside_but_not_in (NESTING_FUNCTION, 2, NESTING_ITERATIONAL, NESTING_SWITCH); + must_be_inside_but_not_in (NESTING_FUNCTION, + 4, + NESTING_ITERATIONAL, + NESTING_SWITCH, + NESTING_TRY, + NESTING_WITH); dump_break_for_rewrite (); return; } From 8433df309734eef433624505969ec88380b845c4 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Tue, 2 Jun 2015 17:04:18 +0300 Subject: [PATCH 2/3] Introducing interpreter run scopes. JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- jerry-core/parser/js/opcodes-dumper.cpp | 51 +++++++++++++++++-- jerry-core/parser/js/opcodes-dumper.h | 3 +- jerry-core/parser/js/parser.cpp | 3 +- .../vm/opcodes-ecma-try-catch-finally.cpp | 11 ++-- jerry-core/vm/opcodes-varg.cpp | 2 +- jerry-core/vm/opcodes.cpp | 28 ++++++---- jerry-core/vm/opcodes.h | 18 ++++++- jerry-core/vm/vm.cpp | 18 ++++--- jerry-core/vm/vm.h | 2 +- 9 files changed, 109 insertions(+), 27 deletions(-) diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index 6d626583c8..e42c8474cd 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -2262,18 +2262,61 @@ finish_dumping_case_clauses (void) STACK_DROP (U8, 1); } +/** + * Dump template of 'with' instruction. + * + * Note: + * the instruction's flags field is written later (see also: rewrite_with). + * + * @return position of dumped instruction + */ +opcode_counter_t +dump_with_for_rewrite (operand op) /**< operand - result of evaluating Expression + * in WithStatement */ +{ + opcode_counter_t oc = serializer_get_current_opcode_counter (); + + if (op.type == OPERAND_LITERAL) + { + const opcode_t opcode = getop_with (LITERAL_TO_REWRITE, INVALID_VALUE, INVALID_VALUE); + serializer_dump_op_meta (create_op_meta_100 (opcode, op.data.lit_id)); + } + else + { + JERRY_ASSERT (op.type == OPERAND_TMP); + + const opcode_t opcode = getop_with (op.data.uid, INVALID_VALUE, INVALID_VALUE); + serializer_dump_op_meta (create_op_meta_000 (opcode)); + } + + return oc; +} /* dump_with_for_rewrite */ + +/** + * Write position of 'with' block's end to specified 'with' instruction template, + * dumped earlier (see also: dump_with_for_rewrite). + */ void -dump_with (operand op) +rewrite_with (opcode_counter_t oc) /**< opcode counter of the instruction template */ { - dump_single_address (getop_with, op); -} + op_meta with_op_meta = serializer_get_op_meta (oc); + + idx_t id1, id2; + split_opcode_counter (get_diff_from (oc), &id1, &id2); + with_op_meta.op.data.with.oc_idx_1 = id1; + with_op_meta.op.data.with.oc_idx_2 = id2; + serializer_rewrite_op_meta (oc, with_op_meta); +} /* rewrite_with */ +/** + * Dump 'meta' instruction of 'end with' type + */ void dump_with_end (void) { const opcode_t opcode = getop_meta (OPCODE_META_TYPE_END_WITH, INVALID_VALUE, INVALID_VALUE); serializer_dump_op_meta (create_op_meta_000 (opcode)); -} +} /* dump_with_end */ void dump_try_for_rewrite (void) diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index 0da07c27e5..8a4111dec8 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -202,7 +202,8 @@ operand dump_delete_res (operand, bool, locus); void dump_typeof (operand, operand); operand dump_typeof_res (operand); -void dump_with (operand); +opcode_counter_t dump_with_for_rewrite (operand); +void rewrite_with (opcode_counter_t); void dump_with_end (void); void dump_try_for_rewrite (void); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 52fbf9c73b..91e598c780 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -2022,9 +2022,10 @@ parse_with_statement (void) push_nesting (NESTING_WITH); - dump_with (expr); + opcode_counter_t with_begin_oc = dump_with_for_rewrite (expr); skip_newlines (); parse_statement (); + rewrite_with (with_begin_oc); dump_with_end (); pop_nesting (NESTING_WITH); diff --git a/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp b/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp index 92ac626056..75caf7186c 100644 --- a/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp +++ b/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp @@ -37,7 +37,8 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ int_data->pos++; - ecma_completion_value_t try_completion = vm_loop (int_data); + vm_run_scope_t run_scope_try = { int_data->pos, try_end_oc }; + ecma_completion_value_t try_completion = vm_loop (int_data, &run_scope_try); JERRY_ASSERT ((!ecma_is_completion_value_empty (try_completion) && int_data->pos <= try_end_oc) || (ecma_is_completion_value_empty (try_completion) && int_data->pos == try_end_oc)); int_data->pos = try_end_oc; @@ -87,7 +88,9 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ int_data->lex_env_p = catch_env_p; ecma_free_completion_value (try_completion); - try_completion = vm_loop (int_data); + + vm_run_scope_t run_scope_catch = { int_data->pos, catch_end_oc }; + try_completion = vm_loop (int_data, &run_scope_catch); int_data->lex_env_p = old_env_p; @@ -114,7 +117,9 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ read_meta_opcode_counter (OPCODE_META_TYPE_FINALLY, int_data) + int_data->pos); int_data->pos++; - ecma_completion_value_t finally_completion = vm_loop (int_data); + vm_run_scope_t run_scope_finally = { int_data->pos, finally_end_oc }; + ecma_completion_value_t finally_completion = vm_loop (int_data, &run_scope_finally); + JERRY_ASSERT ((!ecma_is_completion_value_empty (finally_completion) && int_data->pos <= finally_end_oc) || (ecma_is_completion_value_empty (finally_completion) && int_data->pos == finally_end_oc)); int_data->pos = finally_end_oc; diff --git a/jerry-core/vm/opcodes-varg.cpp b/jerry-core/vm/opcodes-varg.cpp index a070224777..85c209f645 100644 --- a/jerry-core/vm/opcodes-varg.cpp +++ b/jerry-core/vm/opcodes-varg.cpp @@ -40,7 +40,7 @@ fill_varg_list (int_data_t *int_data, /**< interpreter context */ arg_index < args_number; arg_index++) { - ecma_completion_value_t evaluate_arg_completion = vm_loop (int_data); + ecma_completion_value_t evaluate_arg_completion = vm_loop (int_data, NULL); if (ecma_is_completion_value_normal (evaluate_arg_completion)) { diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index 3b8446fd56..d123f6ed54 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -912,7 +912,7 @@ opfunc_obj_decl (opcode_t opdata, /**< operation data */ prop_index < args_number; prop_index++) { - ecma_completion_value_t evaluate_prop_completion = vm_loop (int_data); + ecma_completion_value_t evaluate_prop_completion = vm_loop (int_data, NULL); if (ecma_is_completion_value_normal (evaluate_prop_completion)) { @@ -1299,6 +1299,10 @@ opfunc_with (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t expr_var_idx = opdata.data.with.expr; + const idx_t block_end_oc_idx_1 = opdata.data.with.oc_idx_1; + const idx_t block_end_oc_idx_2 = opdata.data.with.oc_idx_2; + const opcode_counter_t with_end_oc = (opcode_counter_t) ( + calc_opcode_counter_from_idx_idx (block_end_oc_idx_1, block_end_oc_idx_2) + int_data->pos); ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); @@ -1321,15 +1325,19 @@ opfunc_with (opcode_t opdata, /**< operation data */ true); int_data->lex_env_p = new_env_p; - ecma_completion_value_t evaluation_completion = vm_loop (int_data); +#ifndef JERRY_NDEBUG + opcode_t meta_opcode = vm_get_opcode (with_end_oc); + JERRY_ASSERT (meta_opcode.op_idx == __op__idx_meta); + JERRY_ASSERT (meta_opcode.data.meta.type == OPCODE_META_TYPE_END_WITH); +#endif /* !JERRY_NDEBUG */ - if (ecma_is_completion_value_normal (evaluation_completion)) - { - JERRY_ASSERT (ecma_is_completion_value_empty (evaluation_completion)); + vm_run_scope_t run_scope_with = { int_data->pos, with_end_oc }; + ecma_completion_value_t with_completion = vm_loop (int_data, &run_scope_with); - opcode_t meta_opcode = vm_get_opcode (int_data->pos); - JERRY_ASSERT (meta_opcode.op_idx == __op__idx_meta); - JERRY_ASSERT (meta_opcode.data.meta.type == OPCODE_META_TYPE_END_WITH); + if (ecma_is_completion_value_normal (with_completion)) + { + JERRY_ASSERT (ecma_is_completion_value_empty (with_completion)); + JERRY_ASSERT (int_data->pos == with_end_oc); int_data->pos++; @@ -1337,7 +1345,9 @@ opfunc_with (opcode_t opdata, /**< operation data */ } else { - ret_value = evaluation_completion; + JERRY_ASSERT (int_data->pos <= with_end_oc); + + ret_value = with_completion; } int_data->lex_env_p = old_env_p; diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 7996ebd218..2ab1866a61 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -113,6 +113,22 @@ typedef struct #endif /* MEM_STATS */ } int_data_t; +/** + * Description of a run scope + * + * Note: + * Run scope represents boundaries of byte-code block to run. + * + * Jumps within of the current run scope are performed by just changing opcode counter, + * and outside of the run scope - by returning corresponding ECMA_COMPLETION_TYPE_BREAK_CONTINUE + * completion value. + */ +typedef struct +{ + const opcode_counter_t start_oc; /**< opcode counter of the first instruction of the scope */ + const opcode_counter_t end_oc; /**< opcode counter of the last instruction of the scope */ +} vm_run_scope_t; + opcode_counter_t calc_opcode_counter_from_idx_idx (const idx_t oc_idx_1, const idx_t oc_idx_2); opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, int_data_t *int_data); @@ -135,7 +151,7 @@ opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, int_d p##_2 (a, delete_var, lhs, name) \ p##_3 (a, delete_prop, lhs, base, name) \ p##_2 (a, typeof, lhs, obj) \ - p##_1 (a, with, expr) \ + p##_3 (a, with, expr, oc_idx_1, oc_idx_2) \ p##_2 (a, try_block, oc_idx_1, oc_idx_2) \ p##_1 (a, throw_value, var) diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 834b178b79..11ec30f355 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -423,7 +423,9 @@ vm_run_global (void) * Otherwise - the completion value is discarded and normal empty completion value is returned. */ ecma_completion_value_t -vm_loop (int_data_t *int_data) /**< interpreter context */ +vm_loop (int_data_t *int_data_p, /**< interpreter context */ + vm_run_scope_t *run_scope_p) /**< current run scope, + * or NULL - if there is no active run scope */ { ecma_completion_value_t completion; @@ -439,24 +441,28 @@ vm_loop (int_data_t *int_data) /**< interpreter context */ { do { - const opcode_t *curr = &__program[int_data->pos]; + JERRY_ASSERT (run_scope_p == NULL + || (run_scope_p->start_oc <= int_data_p->pos + && int_data_p->pos <= run_scope_p->end_oc)); + + const opcode_t *curr = &__program[int_data_p->pos]; #ifdef MEM_STATS - const opcode_counter_t opcode_pos = int_data->pos; + const opcode_counter_t opcode_pos = int_data_p->pos; interp_mem_stats_opcode_enter (opcode_pos, &heap_stats_before, &pools_stats_before); #endif /* MEM_STATS */ - completion = __opfuncs[curr->op_idx] (*curr, int_data); + completion = __opfuncs[curr->op_idx] (*curr, int_data_p); #ifdef CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE ecma_gc_run (); #endif /* CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE */ #ifdef MEM_STATS - interp_mem_stats_opcode_exit (int_data, + interp_mem_stats_opcode_exit (int_data_p, opcode_pos, &heap_stats_before, &pools_stats_before); @@ -525,7 +531,7 @@ vm_run_from_pos (opcode_counter_t start_pos, /**< identifier of starting opcode interp_mem_stats_context_enter (&int_data, start_pos); #endif /* MEM_STATS */ - completion = vm_loop (&int_data); + completion = vm_loop (&int_data, NULL); JERRY_ASSERT (ecma_is_completion_value_normal (completion) || ecma_is_completion_value_throw (completion) diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index d584c7f2af..37b7e90b71 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -22,7 +22,7 @@ extern void vm_init (const opcode_t* program_p, bool dump_mem_stats); extern jerry_completion_code_t vm_run_global (void); -extern ecma_completion_value_t vm_loop (int_data_t *int_data); +extern ecma_completion_value_t vm_loop (int_data_t *int_data, vm_run_scope_t *run_scope_p); extern ecma_completion_value_t vm_run_from_pos (opcode_counter_t start_pos, ecma_value_t this_binding_value, ecma_object_t *lex_env_p, From ac87616f05cb0e90292067d703b4eb862c607385 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Tue, 2 Jun 2015 17:06:43 +0300 Subject: [PATCH 3/3] Fixing break / continue, nested into 'try', 'with' blocks. Related issue: #128 JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- jerry-core/ecma/base/ecma-globals.h | 39 +++-- jerry-core/ecma/base/ecma-helpers-value.cpp | 124 ++++++-------- jerry-core/ecma/base/ecma-helpers.h | 10 +- jerry-core/parser/js/opcodes-dumper.cpp | 100 ++++++++--- jerry-core/parser/js/opcodes-dumper.h | 3 +- jerry-core/parser/js/parser.cpp | 79 ++++++--- jerry-core/vm/opcodes-agnostic.cpp | 17 ++ jerry-core/vm/opcodes.cpp | 10 +- jerry-core/vm/opcodes.h | 3 +- jerry-core/vm/pretty-printer.cpp | 1 + jerry-core/vm/vm.cpp | 21 ++- ...reak_continue_nested_to_try_with_blocks.js | 158 ++++++++++++++++++ tests/jerry/regression-test-issue-128.js | 21 +++ 13 files changed, 440 insertions(+), 146 deletions(-) create mode 100644 tests/jerry/break_continue_nested_to_try_with_blocks.js create mode 100644 tests/jerry/regression-test-issue-128.js diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 0197f624cc..c45d743a34 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -99,11 +99,26 @@ typedef enum */ typedef enum { - ECMA_COMPLETION_TYPE_NORMAL, /**< default block completion */ - ECMA_COMPLETION_TYPE_RETURN, /**< block completed with return */ - ECMA_COMPLETION_TYPE_BREAK, /**< block completed with break */ - ECMA_COMPLETION_TYPE_CONTINUE, /**< block completed with continue */ - ECMA_COMPLETION_TYPE_THROW, /**< block completed with throw */ + ECMA_COMPLETION_TYPE_NORMAL, /**< default completion */ + ECMA_COMPLETION_TYPE_RETURN, /**< completion with return */ + ECMA_COMPLETION_TYPE_JUMP, /**< implementation-defined completion type + * for jump statements (break, continue) + * that require completion of one or several + * statements, before performing related jump. + * + * For example, 'break' in the following code + * requires to return from opfunc_with handler + * before performing jump to the loop end: + * + * for (var i = 0; i < 10; i++) + * { + * with (obj) + * { + * break; + * } + * } + */ + ECMA_COMPLETION_TYPE_THROW, /**< completion with throw */ ECMA_COMPLETION_TYPE_EXIT, /**< implementation-defined completion type for finishing script execution */ ECMA_COMPLETION_TYPE_META /**< implementation-defined completion type @@ -142,7 +157,7 @@ typedef uint32_t ecma_value_t; * * value (16) * Bit-field structure: type (8) | padding (8) < - * label_desc_cp (16) + * break / continue target */ typedef uint32_t ecma_completion_value_t; @@ -155,12 +170,10 @@ typedef uint32_t ecma_completion_value_t; #define ECMA_COMPLETION_VALUE_VALUE_WIDTH (ECMA_VALUE_SIZE) /** - * Label - * - * Used for break and continue completion types. + * Break / continue jump target */ -#define ECMA_COMPLETION_VALUE_LABEL_DESC_CP_POS (0) -#define ECMA_COMPLETION_VALUE_LABEL_DESC_CP_WIDTH (ECMA_POINTER_FIELD_WIDTH) +#define ECMA_COMPLETION_VALUE_TARGET_POS (0) +#define ECMA_COMPLETION_VALUE_TARGET_WIDTH ((uint32_t) sizeof (opcode_counter_t) * JERRY_BITSINBYTE) /** * Type (ecma_completion_type_t) @@ -168,8 +181,8 @@ typedef uint32_t ecma_completion_value_t; #define ECMA_COMPLETION_VALUE_TYPE_POS (JERRY_MAX (JERRY_ALIGNUP (ECMA_COMPLETION_VALUE_VALUE_POS + \ ECMA_COMPLETION_VALUE_VALUE_WIDTH, \ JERRY_BITSINBYTE), \ - JERRY_ALIGNUP (ECMA_COMPLETION_VALUE_LABEL_DESC_CP_POS + \ - ECMA_COMPLETION_VALUE_LABEL_DESC_CP_WIDTH, \ + JERRY_ALIGNUP (ECMA_COMPLETION_VALUE_TARGET_POS + \ + ECMA_COMPLETION_VALUE_TARGET_WIDTH, \ JERRY_BITSINBYTE))) #define ECMA_COMPLETION_VALUE_TYPE_WIDTH (8) diff --git a/jerry-core/ecma/base/ecma-helpers-value.cpp b/jerry-core/ecma/base/ecma-helpers-value.cpp index 85103f7d99..7e63e59886 100644 --- a/jerry-core/ecma/base/ecma-helpers-value.cpp +++ b/jerry-core/ecma/base/ecma-helpers-value.cpp @@ -462,18 +462,17 @@ ecma_get_completion_value_value_field (ecma_completion_value_t completion_value) } /* ecma_get_completion_value_value_field */ /** - * Get pointer to label descriptor from completion value + * Get target of break / continue completion value * - * @return pointer to label descriptor + * @return opcode counter */ -static ecma_label_descriptor_t* __attr_const___ -ecma_get_completion_value_label_descriptor (ecma_completion_value_t completion_value) /**< completion value */ +static opcode_counter_t +ecma_get_completion_value_target (ecma_completion_value_t completion_value) /**< completion value */ { - return ECMA_GET_NON_NULL_POINTER (ecma_label_descriptor_t, - (uintptr_t) jrt_extract_bit_field (completion_value, - ECMA_COMPLETION_VALUE_LABEL_DESC_CP_POS, - ECMA_COMPLETION_VALUE_LABEL_DESC_CP_WIDTH)); -} /* ecma_get_completion_value_label_descriptor */ + return (opcode_counter_t) jrt_extract_bit_field (completion_value, + ECMA_COMPLETION_VALUE_TARGET_POS, + ECMA_COMPLETION_VALUE_TARGET_WIDTH); +} /* ecma_get_completion_value_target */ /** * Set type field of completion value @@ -508,24 +507,20 @@ ecma_set_completion_value_value_field (ecma_completion_value_t completion_value, } /* ecma_set_completion_value_value_field */ /** - * Set label descriptor of completion value + * Set target of break / continue completion value * * @return completion value with updated field */ static ecma_completion_value_t __attr_const___ -ecma_set_completion_value_label_descriptor (ecma_completion_value_t completion_value, /**< completion value - * to set field in */ - ecma_label_descriptor_t* label_desc_p) /**< pointer to the - * label descriptor */ +ecma_set_completion_value_target (ecma_completion_value_t completion_value, /**< completion value + * to set field in */ + opcode_counter_t target) /**< break / continue target */ { - uintptr_t label_desc_cp; - ECMA_SET_NON_NULL_POINTER (label_desc_cp, label_desc_p); - return (ecma_completion_value_t) jrt_set_bit_field_value (completion_value, - label_desc_cp, - ECMA_COMPLETION_VALUE_LABEL_DESC_CP_POS, - ECMA_COMPLETION_VALUE_LABEL_DESC_CP_WIDTH); -} /* ecma_set_completion_value_label_descriptor */ + target, + ECMA_COMPLETION_VALUE_TARGET_POS, + ECMA_COMPLETION_VALUE_TARGET_WIDTH); +} /* ecma_set_completion_value_target */ /** * Normal, throw, return, exit and meta completion values constructor @@ -555,34 +550,6 @@ ecma_make_completion_value (ecma_completion_type_t type, /**< type */ return completion_value; } /* ecma_make_completion_value */ -/** - * Break and continue completion values constructor - * - * @return completion value - */ -ecma_completion_value_t __attr_const___ -ecma_make_label_completion_value (ecma_completion_type_t type, /**< type */ - uint8_t depth_level, /**< depth level (in try constructions, - with blocks, etc.) */ - uint16_t offset) /**< offset to label from end of last block */ -{ - JERRY_ASSERT (type == ECMA_COMPLETION_TYPE_BREAK - || type == ECMA_COMPLETION_TYPE_CONTINUE); - - ecma_label_descriptor_t *label_desc_p = ecma_alloc_label_descriptor (); - label_desc_p->offset = offset; - label_desc_p->depth = depth_level; - - ecma_completion_value_t completion_value = 0; - - completion_value = ecma_set_completion_value_type_field (completion_value, - type); - completion_value = ecma_set_completion_value_label_descriptor (completion_value, - label_desc_p); - - return completion_value; -} /* ecma_make_label_completion_value */ - /** * Simple normal completion value constructor * @@ -688,6 +655,24 @@ ecma_make_meta_completion_value (void) ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY)); } /* ecma_make_meta_completion_value */ +/** + * Break / continue completion values constructor + * + * @return completion value + */ +ecma_completion_value_t __attr_const___ +ecma_make_jump_completion_value (opcode_counter_t target) /**< target break / continue */ +{ + ecma_completion_value_t completion_value = 0; + + completion_value = ecma_set_completion_value_type_field (completion_value, + ECMA_COMPLETION_TYPE_JUMP); + completion_value = ecma_set_completion_value_target (completion_value, + target); + + return completion_value; +} /* ecma_make_jump_completion_value */ + /** * Get ecma-value from specified completion value * @@ -741,6 +726,20 @@ ecma_get_object_from_completion_value (ecma_completion_value_t completion_value) return ecma_get_object_from_value (ecma_get_completion_value_value (completion_value)); } /* ecma_get_object_from_completion_value */ +/** + * Get break / continue target from completion value + * + * @return opcode counter + */ +opcode_counter_t +ecma_get_jump_target_from_completion_value (ecma_completion_value_t completion_value) /**< completion + * value */ +{ + JERRY_ASSERT (ecma_get_completion_value_type_field (completion_value) == ECMA_COMPLETION_TYPE_JUMP); + + return ecma_get_completion_value_target (completion_value); +} /* ecma_get_jump_target_from_completion_value */ + /** * Copy ecma-completion value. * @@ -753,6 +752,7 @@ ecma_copy_completion_value (ecma_completion_value_t value) /**< completion value const bool is_type_ok = (type == ECMA_COMPLETION_TYPE_NORMAL || type == ECMA_COMPLETION_TYPE_THROW || type == ECMA_COMPLETION_TYPE_RETURN + || type == ECMA_COMPLETION_TYPE_JUMP || type == ECMA_COMPLETION_TYPE_EXIT); JERRY_ASSERT (is_type_ok); @@ -784,10 +784,8 @@ ecma_free_completion_value (ecma_completion_value_t completion_value) /**< compl JERRY_ASSERT (ecma_get_value_type_field (v) == ECMA_TYPE_SIMPLE); break; } - case ECMA_COMPLETION_TYPE_CONTINUE: - case ECMA_COMPLETION_TYPE_BREAK: + case ECMA_COMPLETION_TYPE_JUMP: { - ecma_dealloc_label_descriptor (ecma_get_completion_value_label_descriptor (completion_value)); break; } case ECMA_COMPLETION_TYPE_META: @@ -876,28 +874,16 @@ ecma_is_completion_value_meta (ecma_completion_value_t value) /**< completion va } /* ecma_is_completion_value_meta */ /** - * Check if the completion value is break value. - * - * @return true - if the completion type is break, - * false - otherwise. - */ -bool __attr_const___ __attr_always_inline___ -ecma_is_completion_value_break (ecma_completion_value_t value) /**< completion value */ -{ - return (ecma_get_completion_value_type_field (value) == ECMA_COMPLETION_TYPE_BREAK); -} /* ecma_is_completion_value_break */ - -/** - * Check if the completion value is continue value. + * Check if the completion value is break / continue value. * - * @return true - if the completion type is continue, + * @return true - if the completion type is break / continue, * false - otherwise. */ bool __attr_const___ __attr_always_inline___ -ecma_is_completion_value_continue (ecma_completion_value_t value) /**< completion value */ +ecma_is_completion_value_jump (ecma_completion_value_t value) /**< completion value */ { - return (ecma_get_completion_value_type_field (value) == ECMA_COMPLETION_TYPE_CONTINUE); -} /* ecma_is_completion_value_continue */ + return (ecma_get_completion_value_type_field (value) == ECMA_COMPLETION_TYPE_JUMP); +} /* ecma_is_completion_value_jump */ /** * Check if the completion value is specified normal simple value. diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index a039dff5e4..618bc517c4 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -25,6 +25,7 @@ #include "ecma-globals.h" #include "mem-allocator.h" +#include "opcodes.h" /** * Get value of pointer from specified non-null compressed pointer. @@ -74,9 +75,6 @@ extern void ecma_free_value (ecma_value_t value, bool do_deref_if_object); extern ecma_completion_value_t ecma_make_completion_value (ecma_completion_type_t type, ecma_value_t value); -extern ecma_completion_value_t ecma_make_label_completion_value (ecma_completion_type_t type, - uint8_t depth_level, - uint16_t offset); extern ecma_completion_value_t ecma_make_simple_completion_value (ecma_simple_value_t simple_value); extern ecma_completion_value_t ecma_make_normal_completion_value (ecma_value_t value); extern ecma_completion_value_t ecma_make_throw_completion_value (ecma_value_t value); @@ -85,6 +83,7 @@ extern ecma_completion_value_t ecma_make_empty_completion_value (void); extern ecma_completion_value_t ecma_make_return_completion_value (ecma_value_t value); extern ecma_completion_value_t ecma_make_exit_completion_value (bool is_successful); extern ecma_completion_value_t ecma_make_meta_completion_value (void); +extern ecma_completion_value_t ecma_make_jump_completion_value (opcode_counter_t target); extern ecma_value_t ecma_get_completion_value_value (ecma_completion_value_t completion_value); extern ecma_number_t* __attr_const___ ecma_get_number_from_completion_value (ecma_completion_value_t completion_value); @@ -92,6 +91,8 @@ extern ecma_string_t* __attr_const___ ecma_get_string_from_completion_value (ecma_completion_value_t completion_value); extern ecma_object_t* __attr_const___ ecma_get_object_from_completion_value (ecma_completion_value_t completion_value); +extern opcode_counter_t +ecma_get_jump_target_from_completion_value (ecma_completion_value_t completion_value); extern ecma_completion_value_t ecma_copy_completion_value (ecma_completion_value_t value); extern void ecma_free_completion_value (ecma_completion_value_t completion_value); @@ -100,8 +101,7 @@ extern bool ecma_is_completion_value_throw (ecma_completion_value_t value); extern bool ecma_is_completion_value_return (ecma_completion_value_t value); extern bool ecma_is_completion_value_exit (ecma_completion_value_t value); extern bool ecma_is_completion_value_meta (ecma_completion_value_t value); -extern bool ecma_is_completion_value_break (ecma_completion_value_t value); -extern bool ecma_is_completion_value_continue (ecma_completion_value_t value); +extern bool ecma_is_completion_value_jump (ecma_completion_value_t value); extern bool ecma_is_completion_value_normal_simple_value (ecma_completion_value_t value, ecma_simple_value_t simple_value); extern bool ecma_is_completion_value_normal_true (ecma_completion_value_t value); diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index e42c8474cd..18604aca0f 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -2142,22 +2142,50 @@ dump_continue_iterations_check (operand op) STACK_DROP (next_iterations, 1); } +/** + * Dump template of 'jmp_break_continue' instruction. + * + * Note: + * the instruction's flags field is written later (see also: rewrite_breaks, rewrite_continues). + * + * @return position of dumped instruction + */ void -dump_continue_for_rewrite (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 */ { - STACK_PUSH (continues, serializer_get_current_opcode_counter ()); - const opcode_t opcode = getop_jmp_down (INVALID_VALUE, INVALID_VALUE); - serializer_dump_op_meta (create_op_meta_000 (opcode)); -} + if (is_break) + { + STACK_PUSH (breaks, serializer_get_current_opcode_counter ()); + } + else + { + STACK_PUSH (continues, serializer_get_current_opcode_counter ()); + } + + opcode_t opcode; + if (is_simple_jump) + { + opcode = getop_jmp_down (INVALID_VALUE, INVALID_VALUE); + } + else + { + opcode = getop_jmp_break_continue (INVALID_VALUE, INVALID_VALUE); + } -void -dump_break_for_rewrite (void) -{ - STACK_PUSH (breaks, serializer_get_current_opcode_counter ()); - const opcode_t opcode = getop_jmp_down (INVALID_VALUE, INVALID_VALUE); serializer_dump_op_meta (create_op_meta_000 (opcode)); -} +} /* dump_break_continue_for_rewrite */ +/** + * Write jump target positions into previously dumped templates of instructions + * for currently processed set of 'break' statements. + * + * See also: + * dump_break_continue_for_rewrite + * start_collecting_breaks + */ void rewrite_breaks (void) { @@ -2168,9 +2196,22 @@ rewrite_breaks (void) 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); - JERRY_ASSERT (break_op_meta.op.op_idx == OPCODE (jmp_down)); - break_op_meta.op.data.jmp_down.opcode_1 = id1; - break_op_meta.op.data.jmp_down.opcode_2 = id2; + + bool is_simple_jump = (break_op_meta.op.op_idx == OPCODE (jmp_down)); + + 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)); + + break_op_meta.op.data.jmp_break_continue.opcode_1 = id1; + break_op_meta.op.data.jmp_break_continue.opcode_2 = id2; + } + serializer_rewrite_op_meta (break_oc, break_op_meta); } STACK_ITERATE_END (); @@ -2178,8 +2219,16 @@ rewrite_breaks (void) 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) { @@ -2190,9 +2239,22 @@ rewrite_continues (void) 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); - JERRY_ASSERT (continue_op_meta.op.op_idx == OPCODE (jmp_down)); - continue_op_meta.op.data.jmp_down.opcode_1 = id1; - continue_op_meta.op.data.jmp_down.opcode_2 = id2; + + bool is_simple_jump = (continue_op_meta.op.op_idx == OPCODE (jmp_down)); + + 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)); + + 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); } STACK_ITERATE_END (); @@ -2200,7 +2262,7 @@ rewrite_continues (void) STACK_DROP (continue_targets, 1); STACK_DROP (continues, STACK_SIZE (continues) - STACK_TOP (U8)); STACK_DROP (U8, 1); -} +} /* rewrite_continues */ void start_dumping_case_clauses (void) diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index 8a4111dec8..e8981c3e65 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -183,8 +183,7 @@ 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_continue_for_rewrite (void); -void dump_break_for_rewrite (void); +void dump_break_continue_for_rewrite (bool is_break, bool is_simple_jump); void rewrite_continues (void); void rewrite_breaks (void); void dump_continue_iterations_check (operand); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 91e598c780..2728de90a0 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -109,15 +109,19 @@ pop_nesting (nesting_t nesting_type) /**< type of current nesting */ 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, uint8_t insides_count, ...) +must_be_inside_but_not_in (uint8_t not_in, /**< 'not_in' nesting type */ + uint8_t insides_count, /**< 'inside' nestings number */ + ...) /**< 'inside' nestings list */ { - va_list insides_list; - if (STACK_SIZE (nestings) == 0) - { - EMIT_ERROR ("Shall be inside a nesting"); - } + 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++) @@ -142,7 +146,7 @@ must_be_inside_but_not_in (uint8_t not_in, uint8_t insides_count, ...) } } EMIT_ERROR ("Shall be inside a nesting"); -} +} /* must_be_inside_but_not_in */ static bool token_is (token_type tt) @@ -2376,27 +2380,50 @@ parse_statement (void) parse_for_or_for_in_statement (); return; } - if (is_keyword (KW_CONTINUE)) + if (is_keyword (KW_CONTINUE) + || is_keyword (KW_BREAK)) { - must_be_inside_but_not_in (NESTING_FUNCTION, - 4, - NESTING_ITERATIONAL, - NESTING_TRY, - NESTING_WITH); + 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); + + if (is_break) + { + must_be_inside_but_not_in (NESTING_FUNCTION, + 4, + NESTING_ITERATIONAL, + NESTING_SWITCH, + NESTING_TRY, + NESTING_WITH); + } + else + { + must_be_inside_but_not_in (NESTING_FUNCTION, + 3, + NESTING_ITERATIONAL, + NESTING_TRY, + NESTING_WITH); + } + + if (topmost_nesting == NESTING_ITERATIONAL + || (topmost_nesting == NESTING_SWITCH && is_break)) + { + dump_break_continue_for_rewrite (is_break, true); + } + else + { + JERRY_ASSERT (topmost_nesting == NESTING_TRY + || topmost_nesting == NESTING_WITH + || (topmost_nesting == NESTING_SWITCH && !is_break)); + + dump_break_continue_for_rewrite (is_break, false); + } - must_be_inside_but_not_in (NESTING_FUNCTION, 1, NESTING_ITERATIONAL); - dump_continue_for_rewrite (); - return; - } - if (is_keyword (KW_BREAK)) - { - must_be_inside_but_not_in (NESTING_FUNCTION, - 4, - NESTING_ITERATIONAL, - NESTING_SWITCH, - NESTING_TRY, - NESTING_WITH); - dump_break_for_rewrite (); return; } if (is_keyword (KW_RETURN)) diff --git a/jerry-core/vm/opcodes-agnostic.cpp b/jerry-core/vm/opcodes-agnostic.cpp index b144a79e2f..0ce86c0180 100644 --- a/jerry-core/vm/opcodes-agnostic.cpp +++ b/jerry-core/vm/opcodes-agnostic.cpp @@ -198,3 +198,20 @@ opfunc_jmp_up (opcode_t opdata, /**< operation data */ return ecma_make_empty_completion_value (); } + +/** + * 'Break or continue jump' opcode handler. + * + * Note: + * the opcode returns break-continue completion value with jump target + */ +ecma_completion_value_t +opfunc_jmp_break_continue (opcode_t opdata, /**< operation data */ + int_data_t *int_data) /**< interpreter context */ +{ + opcode_counter_t target = int_data->pos; + target = (opcode_counter_t) (target + calc_opcode_counter_from_idx_idx (opdata.data.jmp_down.opcode_1, + opdata.data.jmp_down.opcode_2)); + + return ecma_make_jump_completion_value (target); +} /* opfunc_jmp_break_continue */ diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index d123f6ed54..33b1e1f62b 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -1334,22 +1334,20 @@ opfunc_with (opcode_t opdata, /**< operation data */ vm_run_scope_t run_scope_with = { int_data->pos, with_end_oc }; ecma_completion_value_t with_completion = vm_loop (int_data, &run_scope_with); - if (ecma_is_completion_value_normal (with_completion)) + if (ecma_is_completion_value_empty (with_completion)) { - JERRY_ASSERT (ecma_is_completion_value_empty (with_completion)); JERRY_ASSERT (int_data->pos == with_end_oc); int_data->pos++; - - ret_value = ecma_make_empty_completion_value (); } else { + JERRY_ASSERT (!ecma_is_completion_value_normal (with_completion)); JERRY_ASSERT (int_data->pos <= with_end_oc); - - ret_value = with_completion; } + ret_value = with_completion; + int_data->lex_env_p = old_env_p; ecma_deref_object (new_env_p); diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 2ab1866a61..a936d60ee6 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -206,7 +206,8 @@ opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, int_d p##_3 (a, is_true_jmp_up, value, opcode_1, opcode_2) \ p##_3 (a, is_true_jmp_down, value, opcode_1, opcode_2) \ p##_3 (a, is_false_jmp_up, value, opcode_1, opcode_2) \ - p##_3 (a, is_false_jmp_down, value, opcode_1, opcode_2) + p##_3 (a, is_false_jmp_down, value, opcode_1, opcode_2) \ + p##_2 (a, jmp_break_continue, opcode_1, opcode_2) #define OP_LIST_FULL(p, a) \ OP_CALLS_AND_ARGS (p, a) \ diff --git a/jerry-core/vm/pretty-printer.cpp b/jerry-core/vm/pretty-printer.cpp index 2441759934..8bbe5a4336 100644 --- a/jerry-core/vm/pretty-printer.cpp +++ b/jerry-core/vm/pretty-printer.cpp @@ -288,6 +288,7 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) case NAME_TO_ID (is_false_jmp_down): printf ("if (%s == false) goto %d;", VAR (1), oc + OC (2, 3)); break; case NAME_TO_ID (jmp_up): printf ("goto %d;", oc - OC (1, 2)); break; case NAME_TO_ID (jmp_down): printf ("goto %d;", oc + OC (1, 2)); break; + case NAME_TO_ID (jmp_break_continue): printf ("goto_nested %d;", oc + OC (1, 2)); break; case NAME_TO_ID (try_block): printf ("try (end: %d);", oc + OC (1, 2)); break; case NAME_TO_ID (assignment): { diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 11ec30f355..b21f94264a 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -473,12 +473,23 @@ vm_loop (int_data_t *int_data_p, /**< interpreter context */ } while (ecma_is_completion_value_normal (completion)); - if (ecma_is_completion_value_break (completion) - || ecma_is_completion_value_continue (completion)) + if (ecma_is_completion_value_jump (completion)) { - JERRY_UNIMPLEMENTED ("break and continue on labels are not supported."); - - continue; + opcode_counter_t target = ecma_get_jump_target_from_completion_value (completion); + + /* + * TODO: + * Implement instantiation of run scopes for global scope, functions and eval scope. + * Currently, correctness of jumps without run scope set is guaranteed through byte-code semantics. + */ + if (run_scope_p == NULL /* if no run scope set */ + || (target >= run_scope_p->start_oc /* or target is within the current run scope */ + && target < run_scope_p->end_oc)) + { + int_data_p->pos = target; + + continue; + } } if (ecma_is_completion_value_meta (completion)) diff --git a/tests/jerry/break_continue_nested_to_try_with_blocks.js b/tests/jerry/break_continue_nested_to_try_with_blocks.js new file mode 100644 index 0000000000..1b19533717 --- /dev/null +++ b/tests/jerry/break_continue_nested_to_try_with_blocks.js @@ -0,0 +1,158 @@ +// 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. + +/* with */ + +for (var i = 0; i < 10; i++) +{ + with ({}) + { + break; + + assert (false); + } +} +assert (i === 0); + +for (var i = 0; i < 10; i++) +{ + with ({}) + { + continue; + + assert (false); + } +} +assert (i === 10); + +/* try */ +for (var i = 0; i < 10; i++) +{ + try + { + break; + + assert (false); + } + catch (e) + { + } +} +assert (i === 0); + +for (var i = 0; i < 10; i++) +{ + try + { + continue; + + assert (false); + } + catch (e) + { + } +} +assert (i === 10); + +/* catch */ +for (var i = 0; i < 10; i++) +{ + try + { + throw new TypeError (); + assert (false); + } + catch (e) + { + break; + assert (false); + } +} +assert (i === 0); + +for (var i = 0; i < 10; i++) +{ + try + { + throw new TypeError (); + assert (false); + } + catch (e) + { + continue; + assert (false); + } +} +assert (i === 10); + + +/* finally */ +for (var i = 0; i < 10; i++) +{ + try + { + throw new TypeError (); + assert (false); + } + catch (e) + { + } + finally + { + break; + assert (false); + } +} +assert (i === 0); + +for (var i = 0; i < 10; i++) +{ + try + { + throw new TypeError (); + assert (false); + } + catch (e) + { + } + finally + { + continue; + assert (false); + } +} +assert (i === 10); + + +/* with - switch */ + +str = ''; +for (var i = 0; i < 10; i++) +{ + with ({}) + { + switch (i) + { + case 0: + str += 'A'; + break; + default: + str += 'B'; + continue; + } + + str += 'C'; + } +} +assert (str === 'ACBBBBBBBBB'); diff --git a/tests/jerry/regression-test-issue-128.js b/tests/jerry/regression-test-issue-128.js new file mode 100644 index 0000000000..fa99a2a50e --- /dev/null +++ b/tests/jerry/regression-test-issue-128.js @@ -0,0 +1,21 @@ +// 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. + +do { + try { + } finally { + break; + } +} while (false);