/* Copyright 2014-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-early-error.h" #include "opcodes-dumper.h" #include "pretty-printer.h" #include "bytecode-data.h" /** * Register allocator's counter */ static vm_idx_t jsp_reg_next; /** * Maximum identifier of a register, allocated for intermediate value storage * * See also: * dumper_save_reg_alloc_ctx, dumper_restore_reg_alloc_ctx */ static vm_idx_t jsp_reg_max_for_temps; /** * Maximum identifier of a register, allocated for storage of a variable value. * * The value can be VM_IDX_EMPTY, indicating that no registers were allocated for variable values. * * Note: * Registers for variable values are always allocated after registers for temporary values, * so the value, if not equal to VM_IDX_EMPTY, is always greater than jsp_reg_max_for_temps. * * See also: * dumper_try_replace_identifier_name_with_reg */ static vm_idx_t jsp_reg_max_for_local_var; /** * Maximum identifier of a register, allocated for storage of an argument value. * * The value can be VM_IDX_EMPTY, indicating that no registers were allocated for argument values. * * Note: * Registers for argument values are always allocated after registers for variable values, * so the value, if not equal to VM_IDX_EMPTY, is always greater than jsp_reg_max_for_local_var. * * See also: * dumper_try_replace_identifier_name_with_reg */ static vm_idx_t jsp_reg_max_for_args; /** * Flag, indicating if instructions should be printed */ bool is_print_instrs = false; /** * Construct uninitialized operand * * @return constructed operand */ jsp_operand_t jsp_make_uninitialized_operand (void) { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_UNINITIALIZED; return ret; } /* jsp_make_uninitialized_operand */ /** * Construct empty operand * * @return constructed operand */ jsp_operand_t jsp_make_empty_operand (void) { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_EMPTY; return ret; } /* jsp_make_empty_operand */ /** * Construct ThisBinding operand * * @return constructed operand */ jsp_operand_t jsp_make_this_operand (void) { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_THIS_BINDING; return ret; } /* jsp_make_this_operand */ /** * Construct unknown operand * * @return constructed operand */ jsp_operand_t jsp_make_unknown_operand (void) { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_UNKNOWN; return ret; } /* jsp_make_unknown_operand */ /** * Construct idx-constant operand * * @return constructed operand */ jsp_operand_t jsp_make_idx_const_operand (vm_idx_t cnst) /**< integer in vm_idx_t range */ { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_IDX_CONST; ret.data.idx_const = cnst; return ret; } /* jsp_make_idx_const_operand */ /** * Construct small integer operand * * @return constructed operand */ jsp_operand_t jsp_make_smallint_operand (uint8_t integer_value) /**< small integer value */ { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_SMALLINT; ret.data.smallint_value = integer_value; return ret; } /* jsp_make_smallint_operand */ /** * Construct simple ecma value operand * * @return constructed operand */ jsp_operand_t jsp_make_simple_value_operand (ecma_simple_value_t simple_value) /**< simple ecma value */ { jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_SIMPLE_VALUE; ret.data.simple_value = simple_value; return ret; } /* jsp_make_simple_value_operand */ /** * Construct string literal operand * * @return constructed operand */ jsp_operand_t jsp_make_string_lit_operand (lit_cpointer_t lit_id) /**< literal identifier */ { JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); #ifndef JERRY_NDEBUG lit_literal_t lit = lit_get_literal_by_cp (lit_id); JERRY_ASSERT (RCS_RECORD_IS_CHARSET (lit) || RCS_RECORD_IS_MAGIC_STR (lit) || RCS_RECORD_IS_MAGIC_STR_EX (lit)); #endif /* !JERRY_NDEBUG */ jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_STRING_LITERAL; ret.data.lit_id = lit_id; return ret; } /* jsp_make_string_lit_operand */ /** * Construct RegExp literal operand * * @return constructed operand */ jsp_operand_t jsp_make_regexp_lit_operand (lit_cpointer_t lit_id) /**< literal identifier */ { JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); #ifndef JERRY_NDEBUG lit_literal_t lit = lit_get_literal_by_cp (lit_id); JERRY_ASSERT (RCS_RECORD_IS_CHARSET (lit) || RCS_RECORD_IS_MAGIC_STR (lit) || RCS_RECORD_IS_MAGIC_STR_EX (lit)); #endif /* !JERRY_NDEBUG */ jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_REGEXP_LITERAL; ret.data.lit_id = lit_id; return ret; } /* jsp_make_regexp_lit_operand */ /** * Construct number literal operand * * @return constructed operand */ jsp_operand_t jsp_make_number_lit_operand (lit_cpointer_t lit_id) /**< literal identifier */ { JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); #ifndef JERRY_NDEBUG lit_literal_t lit = lit_get_literal_by_cp (lit_id); JERRY_ASSERT (RCS_RECORD_IS_NUMBER (lit)); #endif /* !JERRY_NDEBUG */ jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_NUMBER_LITERAL; ret.data.lit_id = lit_id; return ret; } /* jsp_make_number_lit_operand */ /** * Construct identifier reference operand * * @return constructed operand */ jsp_operand_t jsp_make_identifier_operand (lit_cpointer_t lit_id) /**< literal identifier */ { JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_IDENTIFIER; ret.data.identifier = lit_id; return ret; } /* jsp_make_identifier_operand */ /** * Construct register operand * * @return constructed operand */ jsp_operand_t jsp_make_reg_operand (vm_idx_t reg_index) /**< register index */ { /* * The following check currently leads to 'comparison is always true * due to limited range of data type' warning, so it is turned off. * * If VM_IDX_GENERAL_VALUE_FIRST is changed to value greater than 0, * the check should be restored. */ // JERRY_ASSERT (reg_index >= VM_IDX_GENERAL_VALUE_FIRST); JERRY_STATIC_ASSERT (VM_IDX_GENERAL_VALUE_FIRST == 0); JERRY_ASSERT (reg_index <= VM_IDX_GENERAL_VALUE_LAST); jsp_operand_t ret; ret.type = JSP_OPERAND_TYPE_TMP; ret.data.uid = reg_index; return ret; } /* jsp_make_reg_operand */ /** * Is it empty operand? * * @return true / false */ bool jsp_is_empty_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_EMPTY); } /* jsp_is_empty_operand */ /** * Is it ThisBinding operand? * * @return true / false */ bool jsp_is_this_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_THIS_BINDING); } /* jsp_is_this_operand */ /** * Is it unknown operand? * * @return true / false */ bool jsp_is_unknown_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_UNKNOWN); } /* jsp_is_unknown_operand */ /** * Is it idx-constant operand? * * @return true / false */ bool jsp_is_idx_const_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_IDX_CONST); } /* jsp_is_idx_const_operand */ /** * Is it byte-code register operand? * * @return true / false */ bool jsp_is_register_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_TMP); } /* jsp_is_register_operand */ /** * Is it simple ecma value operand? * * @return true / false */ bool jsp_is_simple_value_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_SIMPLE_VALUE); } /* jsp_is_simple_value_operand */ /** * Is it small integer operand? * * @return true / false */ bool jsp_is_smallint_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_SMALLINT); } /* jsp_is_smallint_operand */ /** * Is it number literal operand? * * @return true / false */ bool jsp_is_number_lit_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_NUMBER_LITERAL); } /* jsp_is_number_lit_operand */ /** * Is it string literal operand? * * @return true / false */ bool jsp_is_string_lit_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_STRING_LITERAL); } /* jsp_is_string_lit_operand */ /** * Is it RegExp literal operand? * * @return true / false */ bool jsp_is_regexp_lit_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_REGEXP_LITERAL); } /* jsp_is_regexp_lit_operand */ /** * Is it identifier reference operand? * * @return true / false */ bool jsp_is_identifier_operand (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); return (operand.type == JSP_OPERAND_TYPE_IDENTIFIER); } /* jsp_is_identifier_operand */ /** * Get string literal - name of Identifier reference * * @return literal identifier */ lit_cpointer_t jsp_operand_get_identifier_name (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (jsp_is_identifier_operand (operand)); return (operand.data.identifier); } /* jsp_operand_get_identifier_name */ /** * Get idx for operand * * @return VM_IDX_REWRITE_LITERAL_UID (for LITERAL), * or register index (for TMP). */ vm_idx_t jsp_operand_get_idx (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); if (operand.type == JSP_OPERAND_TYPE_TMP) { return operand.data.uid; } if (operand.type == JSP_OPERAND_TYPE_STRING_LITERAL || operand.type == JSP_OPERAND_TYPE_NUMBER_LITERAL) { return VM_IDX_REWRITE_LITERAL_UID; } if (operand.type == JSP_OPERAND_TYPE_THIS_BINDING) { return VM_REG_SPECIAL_THIS_BINDING; } JERRY_ASSERT (operand.type == JSP_OPERAND_TYPE_EMPTY); return VM_IDX_EMPTY; } /* jsp_operand_get_idx */ /** * Get literal from operand * * @return literal identifier (for LITERAL), * or NOT_A_LITERAL (for TMP). */ lit_cpointer_t jsp_operand_get_literal (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); if (operand.type == JSP_OPERAND_TYPE_TMP) { return NOT_A_LITERAL; } if (operand.type == JSP_OPERAND_TYPE_STRING_LITERAL || operand.type == JSP_OPERAND_TYPE_NUMBER_LITERAL || operand.type == JSP_OPERAND_TYPE_REGEXP_LITERAL) { return operand.data.lit_id; } JERRY_ASSERT (operand.type == JSP_OPERAND_TYPE_EMPTY); return NOT_A_LITERAL; } /* jsp_operand_get_literal */ /** * Get constant from idx-constant operand * * @return an integer */ vm_idx_t jsp_operand_get_idx_const (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (jsp_is_idx_const_operand (operand)); return operand.data.idx_const; } /* jsp_operand_get_idx_const */ /** * Get small integer constant from operand * * @return an integer */ uint8_t jsp_operand_get_smallint_value (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (jsp_is_smallint_operand (operand)); return operand.data.smallint_value; } /* jsp_operand_get_smallint_value */ /** * Get simple value from operand * * @return a simple ecma value */ ecma_simple_value_t jsp_operand_get_simple_value (jsp_operand_t operand) /**< operand */ { JERRY_ASSERT (jsp_is_simple_value_operand (operand)); return (ecma_simple_value_t) operand.data.simple_value; } /* jsp_operand_get_simple_value */ /** * Determine if operand with specified index could be encoded as a literal * * @return true, if operand could be literal * false, otherwise */ static bool is_possible_literal (uint16_t mask, /**< mask encoding which operands could be literals */ uint8_t index) /**< index of operand */ { int res; switch (index) { case 0: { res = mask >> 8; break; } case 1: { res = (mask & 0xF0) >> 4; break; } default: { JERRY_ASSERT (index = 2); res = mask & 0x0F; } } JERRY_ASSERT (res == 0 || res == 1); return res == 1; } /* is_possible_literal */ /** * Get specified operand value from instruction * * @return operand value */ static vm_idx_t get_uid (op_meta *op, /**< intermediate instruction description, from which operand should be extracted */ size_t i) /**< index of the operand */ { JERRY_ASSERT (i < 3); return op->op.data.raw_args[i]; } /* get_uid */ /** * Insert literals from instruction to array of seen literals * This is needed for prediction of bytecode's hash table size */ static void insert_uids_to_lit_id_map (jsp_ctx_t *ctx_p, /**< parser context */ op_meta *om, /**< intermediate instruction description */ uint16_t mask) /**< mask of possibe literal operands */ { for (uint8_t i = 0; i < 3; i++) { if (is_possible_literal (mask, i)) { if (get_uid (om, i) == VM_IDX_REWRITE_LITERAL_UID) { JERRY_ASSERT (om->lit_id[i].packed_value != MEM_CP_NULL); jsp_account_next_bytecode_to_literal_reference (ctx_p, om->lit_id[i]); } else { JERRY_ASSERT (om->lit_id[i].packed_value == MEM_CP_NULL); } } else { JERRY_ASSERT (om->lit_id[i].packed_value == MEM_CP_NULL); } } } /** * Count number of literals in instruction which were not seen previously */ static void count_new_literals_in_instr (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t instr_pos, /**< position of instruction */ op_meta *om_p) /**< instruction */ { if (instr_pos % BLOCK_SIZE == 0) { jsp_empty_tmp_literal_set (ctx_p); } switch (om_p->op.op_idx) { case VM_OP_PROP_GETTER: case VM_OP_PROP_SETTER: case VM_OP_DELETE_PROP: case VM_OP_B_SHIFT_LEFT: case VM_OP_B_SHIFT_RIGHT: case VM_OP_B_SHIFT_URIGHT: case VM_OP_B_AND: case VM_OP_B_OR: case VM_OP_B_XOR: case VM_OP_EQUAL_VALUE: case VM_OP_NOT_EQUAL_VALUE: case VM_OP_EQUAL_VALUE_TYPE: case VM_OP_NOT_EQUAL_VALUE_TYPE: case VM_OP_LESS_THAN: case VM_OP_GREATER_THAN: case VM_OP_LESS_OR_EQUAL_THAN: case VM_OP_GREATER_OR_EQUAL_THAN: case VM_OP_INSTANCEOF: case VM_OP_IN: case VM_OP_ADDITION: case VM_OP_SUBSTRACTION: case VM_OP_DIVISION: case VM_OP_MULTIPLICATION: case VM_OP_REMAINDER: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x111); break; } case VM_OP_CALL_N: case VM_OP_CONSTRUCT_N: case VM_OP_FUNC_EXPR_N: case VM_OP_DELETE_VAR: case VM_OP_TYPEOF: case VM_OP_B_NOT: case VM_OP_LOGICAL_NOT: case VM_OP_POST_INCR: case VM_OP_POST_DECR: case VM_OP_PRE_INCR: case VM_OP_PRE_DECR: case VM_OP_UNARY_PLUS: case VM_OP_UNARY_MINUS: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x110); break; } case VM_OP_ASSIGNMENT: { switch (om_p->op.data.assignment.type_value_right) { case OPCODE_ARG_TYPE_SIMPLE: case OPCODE_ARG_TYPE_SMALLINT: case OPCODE_ARG_TYPE_SMALLINT_NEGATE: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x100); break; } case OPCODE_ARG_TYPE_NUMBER: case OPCODE_ARG_TYPE_NUMBER_NEGATE: case OPCODE_ARG_TYPE_REGEXP: case OPCODE_ARG_TYPE_STRING: case OPCODE_ARG_TYPE_VARIABLE: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x101); break; } } break; } case VM_OP_FUNC_DECL_N: case VM_OP_FUNC_EXPR_REF: case VM_OP_FOR_IN: case VM_OP_ARRAY_DECL: case VM_OP_OBJ_DECL: case VM_OP_WITH: case VM_OP_THROW_VALUE: case VM_OP_IS_TRUE_JMP_UP: case VM_OP_IS_TRUE_JMP_DOWN: case VM_OP_IS_FALSE_JMP_UP: case VM_OP_IS_FALSE_JMP_DOWN: case VM_OP_VAR_DECL: case VM_OP_RETVAL: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x100); break; } case VM_OP_RET: case VM_OP_TRY_BLOCK: case VM_OP_JMP_UP: case VM_OP_JMP_DOWN: case VM_OP_JMP_BREAK_CONTINUE: case VM_OP_REG_VAR_DECL: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x000); break; } case VM_OP_META: { switch (om_p->op.data.meta.type) { case OPCODE_META_TYPE_VARG_PROP_DATA: case OPCODE_META_TYPE_VARG_PROP_GETTER: case OPCODE_META_TYPE_VARG_PROP_SETTER: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x011); break; } case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x010); break; } case OPCODE_META_TYPE_UNDEFINED: case OPCODE_META_TYPE_END_WITH: case OPCODE_META_TYPE_FUNCTION_END: case OPCODE_META_TYPE_CATCH: case OPCODE_META_TYPE_FINALLY: case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: case OPCODE_META_TYPE_CALL_SITE_INFO: { insert_uids_to_lit_id_map (ctx_p, om_p, 0x000); break; } } break; } default: { JERRY_UNREACHABLE (); } } } /* count_new_literals_in_instr */ /** * Allocate next register for intermediate value * * @return identifier of the allocated register */ static vm_idx_t jsp_alloc_reg_for_temp (void) { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); vm_idx_t next_reg = jsp_reg_next++; if (next_reg > VM_REG_GENERAL_LAST) { /* * FIXME: * Implement mechanism, allowing reusage of register variables */ PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Not enough register variables", LIT_ITERATOR_POS_ZERO); } if (jsp_reg_max_for_temps < next_reg) { jsp_reg_max_for_temps = next_reg; } return next_reg; } /* jsp_alloc_reg_for_temp */ /** * Get current instruction counter * * @return number of currently generated instructions */ vm_instr_counter_t dumper_get_current_instr_counter (jsp_ctx_t *ctx_p) /**< parser context */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); return bc_header_p->instrs_count; } else { return scopes_tree_instrs_num (jsp_get_current_scopes_tree_node (ctx_p)); } } /* dumper_get_current_instr_counter */ /** * Convert instruction at the specified position to intermediate instruction description * * @return intermediate instruction description */ op_meta dumper_get_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos) /**< instruction position */ { op_meta opm; if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); JERRY_ASSERT (pos < bc_header_p->instrs_count); vm_instr_t *instr_p = bc_header_p->instrs_p + pos; opm.op = *instr_p; uint16_t mask; switch (opm.op.op_idx) { case VM_OP_PROP_GETTER: case VM_OP_PROP_SETTER: case VM_OP_DELETE_PROP: case VM_OP_B_SHIFT_LEFT: case VM_OP_B_SHIFT_RIGHT: case VM_OP_B_SHIFT_URIGHT: case VM_OP_B_AND: case VM_OP_B_OR: case VM_OP_B_XOR: case VM_OP_EQUAL_VALUE: case VM_OP_NOT_EQUAL_VALUE: case VM_OP_EQUAL_VALUE_TYPE: case VM_OP_NOT_EQUAL_VALUE_TYPE: case VM_OP_LESS_THAN: case VM_OP_GREATER_THAN: case VM_OP_LESS_OR_EQUAL_THAN: case VM_OP_GREATER_OR_EQUAL_THAN: case VM_OP_INSTANCEOF: case VM_OP_IN: case VM_OP_ADDITION: case VM_OP_SUBSTRACTION: case VM_OP_DIVISION: case VM_OP_MULTIPLICATION: case VM_OP_REMAINDER: { mask = 0x111; break; } case VM_OP_CALL_N: case VM_OP_CONSTRUCT_N: case VM_OP_FUNC_EXPR_N: case VM_OP_DELETE_VAR: case VM_OP_TYPEOF: case VM_OP_B_NOT: case VM_OP_LOGICAL_NOT: case VM_OP_POST_INCR: case VM_OP_POST_DECR: case VM_OP_PRE_INCR: case VM_OP_PRE_DECR: case VM_OP_UNARY_PLUS: case VM_OP_UNARY_MINUS: { mask = 0x110; break; } case VM_OP_ASSIGNMENT: { switch (opm.op.data.assignment.type_value_right) { case OPCODE_ARG_TYPE_SIMPLE: case OPCODE_ARG_TYPE_SMALLINT: case OPCODE_ARG_TYPE_SMALLINT_NEGATE: { mask = 0x100; break; } case OPCODE_ARG_TYPE_NUMBER: case OPCODE_ARG_TYPE_NUMBER_NEGATE: case OPCODE_ARG_TYPE_REGEXP: case OPCODE_ARG_TYPE_STRING: case OPCODE_ARG_TYPE_VARIABLE: { mask = 0x101; break; } default: { JERRY_UNREACHABLE (); } } break; } case VM_OP_FUNC_DECL_N: case VM_OP_FUNC_EXPR_REF: case VM_OP_FOR_IN: case VM_OP_ARRAY_DECL: case VM_OP_OBJ_DECL: case VM_OP_WITH: case VM_OP_THROW_VALUE: case VM_OP_IS_TRUE_JMP_UP: case VM_OP_IS_TRUE_JMP_DOWN: case VM_OP_IS_FALSE_JMP_UP: case VM_OP_IS_FALSE_JMP_DOWN: case VM_OP_VAR_DECL: case VM_OP_RETVAL: { mask = 0x100; break; } case VM_OP_RET: case VM_OP_TRY_BLOCK: case VM_OP_JMP_UP: case VM_OP_JMP_DOWN: case VM_OP_JMP_BREAK_CONTINUE: case VM_OP_REG_VAR_DECL: { mask = 0x000; break; } case VM_OP_META: { switch (opm.op.data.meta.type) { case OPCODE_META_TYPE_VARG_PROP_DATA: case OPCODE_META_TYPE_VARG_PROP_GETTER: case OPCODE_META_TYPE_VARG_PROP_SETTER: { mask = 0x011; break; } case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { mask = 0x010; break; } case OPCODE_META_TYPE_UNDEFINED: case OPCODE_META_TYPE_END_WITH: case OPCODE_META_TYPE_FUNCTION_END: case OPCODE_META_TYPE_CATCH: case OPCODE_META_TYPE_FINALLY: case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: case OPCODE_META_TYPE_END_FOR_IN: case OPCODE_META_TYPE_CALL_SITE_INFO: { mask = 0x000; break; } default: { JERRY_UNREACHABLE (); } } break; } default: { JERRY_UNREACHABLE (); } } for (uint8_t i = 0; i < 3; i++) { JERRY_STATIC_ASSERT (VM_IDX_LITERAL_FIRST == 0); if (is_possible_literal (mask, i) && opm.op.data.raw_args[i] <= VM_IDX_LITERAL_LAST) { opm.lit_id[i] = bc_get_literal_cp_by_uid (opm.op.data.raw_args[i], bc_header_p, pos); } else { opm.lit_id[i] = NOT_A_LITERAL; } } } return opm; } /* dumper_get_op_meta */ /** * Dump instruction */ static void dumper_dump_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ op_meta opm) /**< intermediate instruction description */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); JERRY_ASSERT (bc_header_p->instrs_count < MAX_OPCODES); #ifdef JERRY_ENABLE_PRETTY_PRINTER if (is_print_instrs) { pp_op_meta (bc_header_p, bc_header_p->instrs_count, opm, false); } #endif /* JERRY_ENABLE_PRETTY_PRINTER */ vm_instr_t *new_instr_place_p = bc_header_p->instrs_p + bc_header_p->instrs_count; bc_header_p->instrs_count++; *new_instr_place_p = opm.op; } else { scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); count_new_literals_in_instr (ctx_p, scope_p->instrs_count, &opm); scope_p->instrs_count++; } } /* dumper_dump_op_meta */ /** * Rewrite instruction at specified offset */ void dumper_rewrite_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ const vm_instr_counter_t loc, /**< instruction location */ op_meta opm) /**< intermediate instruction description */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); JERRY_ASSERT (loc < bc_header_p->instrs_count); vm_instr_t *instr_p = bc_header_p->instrs_p + loc; *instr_p = opm.op; #ifdef JERRY_ENABLE_PRETTY_PRINTER if (is_print_instrs) { pp_op_meta (bc_header_p, loc, opm, true); } #endif /* JERRY_ENABLE_PRETTY_PRINTER */ } } /* dumper_rewrite_op_meta */ #ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER /** * Start move of variable values to registers optimization pass */ void dumper_start_move_of_vars_to_regs (jsp_ctx_t *ctx_p __attr_unused___) { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); jsp_reg_max_for_local_var = jsp_reg_max_for_temps; } /* dumper_start_move_of_vars_to_regs */ /** * Start move of argument values to registers optimization pass * * @return true - if optimization can be performed successfully (i.e. there are enough free registers), * false - otherwise. */ bool dumper_start_move_of_args_to_regs (jsp_ctx_t *ctx_p __attr_unused___, uint32_t args_num) /**< number of arguments */ { JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); if (jsp_reg_max_for_local_var == VM_IDX_EMPTY) { if (args_num + jsp_reg_max_for_temps >= VM_REG_GENERAL_LAST) { return false; } jsp_reg_max_for_args = jsp_reg_max_for_temps; } else { if (args_num + jsp_reg_max_for_local_var >= VM_REG_GENERAL_LAST) { return false; } jsp_reg_max_for_args = jsp_reg_max_for_local_var; } return true; } /* dumper_start_move_of_args_to_regs */ /** * Try to move local variable to a register * * Note: * First instruction of the scope should be either func_decl_n or func_expr_n, as the scope is function scope, * and the optimization is not applied to 'new Function ()'-like constructed functions. * * See also: * parse_source_element_list * parser_parse_program * * @return true, if optimization performed successfully, i.e.: * - there is a free register to use; * - the variable name is not equal to any of the function's argument names; * false - otherwise. */ bool dumper_try_replace_identifier_name_with_reg (jsp_ctx_t *ctx_p, /**< parser context */ bytecode_data_header_t *bc_header_p, /**< a byte-code data header, * created for function declaration * or function expresssion */ op_meta *om_p) /**< operation meta of corresponding * variable declaration */ { lit_cpointer_t lit_cp; bool is_arg; if (om_p->op.op_idx == VM_OP_VAR_DECL) { JERRY_ASSERT (om_p->lit_id[0].packed_value != NOT_A_LITERAL.packed_value); JERRY_ASSERT (om_p->lit_id[1].packed_value == NOT_A_LITERAL.packed_value); JERRY_ASSERT (om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); lit_cp = om_p->lit_id[0]; is_arg = false; } else { JERRY_ASSERT (om_p->op.op_idx == VM_OP_META); JERRY_ASSERT (om_p->op.data.meta.type == OPCODE_META_TYPE_VARG); JERRY_ASSERT (om_p->lit_id[0].packed_value == NOT_A_LITERAL.packed_value); JERRY_ASSERT (om_p->lit_id[1].packed_value != NOT_A_LITERAL.packed_value); JERRY_ASSERT (om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); lit_cp = om_p->lit_id[1]; is_arg = true; } vm_idx_t reg; if (is_arg) { JERRY_ASSERT (jsp_reg_max_for_args != VM_IDX_EMPTY); JERRY_ASSERT (jsp_reg_max_for_args < VM_REG_GENERAL_LAST); reg = ++jsp_reg_max_for_args; } else { JERRY_ASSERT (jsp_reg_max_for_local_var != VM_IDX_EMPTY); if (jsp_reg_max_for_local_var == VM_REG_GENERAL_LAST) { /* not enough registers */ return false; } JERRY_ASSERT (jsp_reg_max_for_local_var < VM_REG_GENERAL_LAST); reg = ++jsp_reg_max_for_local_var; } for (vm_instr_counter_t instr_pos = 0; instr_pos < bc_header_p->instrs_count; instr_pos++) { op_meta om = dumper_get_op_meta (ctx_p, instr_pos); vm_op_t opcode = (vm_op_t) om.op.op_idx; int args_num = 0; #define VM_OP_0(opcode_name, opcode_name_uppercase) \ if (opcode == VM_OP_ ## opcode_name_uppercase) \ { \ args_num = 0; \ } #define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ if (opcode == VM_OP_ ## opcode_name_uppercase) \ { \ JERRY_STATIC_ASSERT (((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ args_num = 1; \ } #define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ if (opcode == VM_OP_ ## opcode_name_uppercase) \ { \ JERRY_STATIC_ASSERT (((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ JERRY_STATIC_ASSERT (((arg2_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ args_num = 2; \ } #define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ if (opcode == VM_OP_ ## opcode_name_uppercase) \ { \ /* * See also: * The loop below */ \ \ JERRY_ASSERT ((opcode == VM_OP_ASSIGNMENT && (arg2_type) == VM_OP_ARG_TYPE_TYPE_OF_NEXT) \ || (opcode != VM_OP_ASSIGNMENT && ((arg2_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0)); \ JERRY_ASSERT ((opcode == VM_OP_META && ((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) != 0) \ || (opcode != VM_OP_META && ((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0)); \ JERRY_STATIC_ASSERT (((arg3_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ args_num = 3; \ } #include "vm-opcodes.inc.h" for (int arg_index = 0; arg_index < args_num; arg_index++) { /* * 'assignment' and 'meta' are the only opcodes with statically unspecified argument type * (checked by assertions above) */ if (opcode == VM_OP_ASSIGNMENT && arg_index == 1 && om.op.data.assignment.type_value_right != VM_OP_ARG_TYPE_VARIABLE) { break; } if (opcode == VM_OP_META && (((om.op.data.meta.type == OPCODE_META_TYPE_VARG_PROP_DATA || om.op.data.meta.type == OPCODE_META_TYPE_VARG_PROP_GETTER || om.op.data.meta.type == OPCODE_META_TYPE_VARG_PROP_SETTER) && arg_index == 1) || om.op.data.meta.type == OPCODE_META_TYPE_END_WITH || om.op.data.meta.type == OPCODE_META_TYPE_CATCH || om.op.data.meta.type == OPCODE_META_TYPE_FINALLY || om.op.data.meta.type == OPCODE_META_TYPE_END_TRY_CATCH_FINALLY || om.op.data.meta.type == OPCODE_META_TYPE_END_FOR_IN)) { continue; } if (om.lit_id[arg_index].packed_value == lit_cp.packed_value) { om.lit_id[arg_index] = NOT_A_LITERAL; om.op.data.raw_args[arg_index] = reg; } } dumper_rewrite_op_meta (ctx_p, instr_pos, om); } return true; } /* dumper_try_replace_identifier_name_with_reg */ #endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ /** * Just allocate register for argument that is never used due to duplicated argument names */ void dumper_alloc_reg_for_unused_arg (jsp_ctx_t *ctx_p __attr_unused___) { JERRY_ASSERT (jsp_reg_max_for_args != VM_IDX_EMPTY); JERRY_ASSERT (jsp_reg_max_for_args < VM_REG_GENERAL_LAST); ++jsp_reg_max_for_args; } /* dumper_alloc_reg_for_unused_arg */ /** * Generate instruction with specified opcode and operands * * @return VM instruction */ static vm_instr_t jsp_dmp_gen_instr (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< operation code */ jsp_operand_t ops[], /**< operands */ size_t ops_num) /**< operands number */ { vm_instr_t instr; instr.op_idx = opcode; for (size_t i = 0; i < ops_num; i++) { if (jsp_is_empty_operand (ops[i])) { instr.data.raw_args[i] = VM_IDX_EMPTY; } else if (jsp_is_unknown_operand (ops[i])) { instr.data.raw_args[i] = VM_IDX_REWRITE_GENERAL_CASE; } else if (jsp_is_idx_const_operand (ops[i])) { instr.data.raw_args[i] = jsp_operand_get_idx_const (ops[i]); } else if (jsp_is_smallint_operand (ops[i])) { instr.data.raw_args[i] = jsp_operand_get_smallint_value (ops[i]); } else if (jsp_is_simple_value_operand (ops[i])) { instr.data.raw_args[i] = jsp_operand_get_simple_value (ops[i]); } else if (jsp_is_register_operand (ops[i]) || jsp_is_this_operand (ops[i])) { instr.data.raw_args[i] = jsp_operand_get_idx (ops[i]); } else { lit_cpointer_t lit_cp; if (jsp_is_identifier_operand (ops[i])) { lit_cp = jsp_operand_get_identifier_name (ops[i]); } else { JERRY_ASSERT (jsp_is_number_lit_operand (ops[i]) || jsp_is_string_lit_operand (ops[i]) || jsp_is_regexp_lit_operand (ops[i])); lit_cp = jsp_operand_get_literal (ops[i]); } vm_idx_t idx = VM_IDX_REWRITE_LITERAL_UID; if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); lit_id_hash_table *lit_id_hash_p = MEM_CP_GET_NON_NULL_POINTER (lit_id_hash_table, bc_header_p->lit_id_hash_cp); idx = lit_id_hash_table_insert (lit_id_hash_p, bc_header_p->instrs_count, lit_cp); } instr.data.raw_args[i] = idx; } } for (size_t i = ops_num; i < 3; i++) { instr.data.raw_args[i] = VM_IDX_EMPTY; } return instr; } /* jsp_dmp_gen_instr */ /** * Create intermediate instruction description, containing pointers to literals, * associated with the instruction's arguments, if there are any. * * @return intermediate operation description */ static op_meta jsp_dmp_create_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t ops[], /**< operands */ size_t ops_num) /**< operands number */ { op_meta ret; ret.op = jsp_dmp_gen_instr (ctx_p, opcode, ops, ops_num); for (size_t i = 0; i < ops_num; i++) { if (jsp_is_number_lit_operand (ops[i]) || jsp_is_string_lit_operand (ops[i]) || jsp_is_regexp_lit_operand (ops[i])) { ret.lit_id[i] = jsp_operand_get_literal (ops[i]); } else if (jsp_is_identifier_operand (ops[i])) { ret.lit_id[i] = jsp_operand_get_identifier_name (ops[i]); } else { ret.lit_id[i] = NOT_A_LITERAL; } } for (size_t i = ops_num; i < 3; i++) { ret.lit_id[i] = NOT_A_LITERAL; } return ret; } /* jsp_dmp_create_op_meta */ /** * Create intermediate instruction description (for instructions without arguments) * * See also: * jsp_dmp_create_op_meta * * @return intermediate instruction description */ static op_meta jsp_dmp_create_op_meta_0 (jsp_ctx_t *ctx_p, vm_op_t opcode) /**< opcode */ { return jsp_dmp_create_op_meta (ctx_p, opcode, NULL, 0); } /* jsp_dmp_create_op_meta_0 */ /** * Create intermediate instruction description (for instructions with 1 argument) * * See also: * jsp_dmp_create_op_meta * * @return intermediate instruction description */ static op_meta jsp_dmp_create_op_meta_1 (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t operand1) /**< first operand */ { return jsp_dmp_create_op_meta (ctx_p, opcode, &operand1, 1); } /* jsp_dmp_create_op_meta_1 */ /** * Create intermediate instruction description (for instructions with 2 arguments) * * See also: * jsp_dmp_create_op_meta * * @return intermediate instruction description */ static op_meta jsp_dmp_create_op_meta_2 (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t operand1, /**< first operand */ jsp_operand_t operand2) /**< second operand */ { jsp_operand_t ops[] = { operand1, operand2 }; return jsp_dmp_create_op_meta (ctx_p, opcode, ops, 2); } /* jsp_dmp_create_op_meta_2 */ /** * Create intermediate instruction description (for instructions with 3 arguments) * * See also: * jsp_dmp_create_op_meta * * @return intermediate instruction description */ static op_meta jsp_dmp_create_op_meta_3 (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t operand1, /**< first operand */ jsp_operand_t operand2, /**< second operand */ jsp_operand_t operand3) /**< third operand */ { jsp_operand_t ops[] = { operand1, operand2, operand3 }; return jsp_dmp_create_op_meta (ctx_p, opcode, ops, 3); } /* jsp_dmp_create_op_meta_3 */ /** * Create temporary operand (alloc available temporary register) */ jsp_operand_t tmp_operand (void) { return jsp_make_reg_operand (jsp_alloc_reg_for_temp ()); } /* tmp_operand */ /** * Store instruction counter into two operands */ static void split_instr_counter (vm_instr_counter_t oc, /**< instruction counter */ vm_idx_t *id1, /**< pointer to the first operand */ vm_idx_t *id2) /**< pointer to the second operand */ { JERRY_ASSERT (id1 != NULL); JERRY_ASSERT (id2 != NULL); *id1 = (vm_idx_t) (oc >> JERRY_BITSINBYTE); *id2 = (vm_idx_t) (oc & ((1 << JERRY_BITSINBYTE) - 1)); JERRY_ASSERT (oc == vm_calc_instr_counter_from_idx_idx (*id1, *id2)); } /* split_instr_counter */ /** * Dump single address instruction */ static void dump_single_address (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t op) /**< first operand */ { dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_1 (ctx_p, opcode, op)); } /* dump_single_address */ /* * Dump double address instruction */ static void dump_double_address (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t res, /**< result operand */ jsp_operand_t obj) /**< argument operand */ { dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_2 (ctx_p, opcode, res, obj)); } /* dump_double_address */ /* * Dump triple address instruction */ static void dump_triple_address (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t res, /**< result operand */ jsp_operand_t lhs, /**< first operand */ jsp_operand_t rhs) /**< second operand */ { dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_3 (ctx_p, opcode, res, lhs, rhs)); } /* dump_triple_address */ /** * Get offset from specified instruction (to calculate distance for jump) * * @return distance between specified and current instruction */ static vm_instr_counter_t get_diff_from (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t oc) /**< position of instruction */ { return (vm_instr_counter_t) (dumper_get_current_instr_counter (ctx_p) - oc); } /* get_diff_from */ /** * Create empty operand */ jsp_operand_t empty_operand (void) { return jsp_make_empty_operand (); } /* empty_operand */ /** * Check if operand is empty */ bool operand_is_empty (jsp_operand_t op) /**< operand */ { return jsp_is_empty_operand (op); } /* operand_is_empty */ /** * Start dump of a new statement (mark all temporary registers as unused) */ void dumper_new_statement (jsp_ctx_t *ctx_p __attr_unused___) { jsp_reg_next = VM_REG_GENERAL_FIRST; } /* dumper_new_statement */ /** * Save temporary registers context */ void dumper_save_reg_alloc_ctx (jsp_ctx_t *ctx_p __attr_unused___, vm_idx_t *out_saved_reg_next_p, /**< pointer to store index of the next free temporary * register */ vm_idx_t *out_saved_reg_max_for_temps_p) /**< pointer to store maximum identifier of a * register, allocated for intermediate * value storage */ { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); *out_saved_reg_next_p = jsp_reg_next; *out_saved_reg_max_for_temps_p = jsp_reg_max_for_temps; jsp_reg_next = VM_REG_GENERAL_FIRST; jsp_reg_max_for_temps = jsp_reg_next; } /* dumper_save_reg_alloc_ctx */ /** * Restore temporary registers context */ void dumper_restore_reg_alloc_ctx (jsp_ctx_t *ctx_p __attr_unused___, vm_idx_t saved_reg_next, /**< identifier of next free register */ vm_idx_t saved_reg_max_for_temps, /**< maximum identifier of register, allocated for * intermediate value storage */ bool is_overwrite_max) /**< flag, indicating if maximum identifier of register, * allocated for intermediate value storage, should be * overwritten, or maximum of current and new value * should be chosen */ { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); if (is_overwrite_max) { jsp_reg_max_for_temps = saved_reg_max_for_temps; } else { jsp_reg_max_for_temps = JERRY_MAX (jsp_reg_max_for_temps, saved_reg_max_for_temps); } jsp_reg_next = saved_reg_next; } /* dumper_restore_reg_alloc_ctx */ /** * Save identifier of the next free register * * @return identifier of the next free register */ vm_idx_t dumper_save_reg_alloc_counter (jsp_ctx_t *ctx_p __attr_unused___) { return jsp_reg_next; } /* dumper_save_reg_alloc_counter */ /** * Restore value of tthe next free register */ void dumper_restore_reg_alloc_counter (jsp_ctx_t *ctx_p __attr_unused___, vm_idx_t reg_alloc_counter) /**< value, returned by corresponding * dumper_save_reg_alloc_counter */ { jsp_reg_next = reg_alloc_counter; } /* dumper_restore_reg_alloc_counter */ /** * Check that byte-code operand refers to 'eval' string * * @return true - if specified byte-code operand's type is literal, and value of corresponding * literal is equal to LIT_MAGIC_STRING_EVAL string, * false - otherwise. */ bool dumper_is_eval_literal (jsp_operand_t obj) /**< byte-code operand */ { /* * FIXME: Switch to corresponding magic string */ if (jsp_is_identifier_operand (obj)) { lit_literal_t lit = lit_get_literal_by_cp (jsp_operand_get_identifier_name (obj)); return lit_literal_equal_type_cstr (lit, "eval"); } return false; } /* dumper_is_eval_literal */ /** * Dump variable assignment */ void dump_variable_assignment (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t res, /**< result operand */ jsp_operand_t var) /**< operand, holding the value to assign */ { jsp_operand_t type_operand; if (jsp_is_string_lit_operand (var)) { type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_STRING); } else if (jsp_is_number_lit_operand (var)) { type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_NUMBER); } else if (jsp_is_regexp_lit_operand (var)) { type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_REGEXP); } else if (jsp_is_smallint_operand (var)) { type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_SMALLINT); } else if (jsp_is_simple_value_operand (var)) { type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_SIMPLE); } else { JERRY_ASSERT (jsp_is_identifier_operand (var) || jsp_is_register_operand (var) || jsp_is_this_operand (var)); type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_VARIABLE); } dump_triple_address (ctx_p, VM_OP_ASSIGNMENT, res, type_operand, var); } /* dump_variable_assignment */ /** * Dump instruction, which implies variable number of arguments after it */ vm_instr_counter_t dump_varg_header_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ varg_list_type vlt, /**< type of instruction */ jsp_operand_t res, /**< result operand */ jsp_operand_t obj) /**< argument operand */ { vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); switch (vlt) { case VARG_FUNC_EXPR: { dump_triple_address (ctx_p, VM_OP_FUNC_EXPR_N, res, obj, jsp_make_unknown_operand ()); break; } case VARG_CONSTRUCT_EXPR: { dump_triple_address (ctx_p, VM_OP_CONSTRUCT_N, res, obj, jsp_make_unknown_operand ()); break; } case VARG_CALL_EXPR: { dump_triple_address (ctx_p, VM_OP_CALL_N, res, obj, jsp_make_unknown_operand ()); break; } case VARG_FUNC_DECL: { dump_double_address (ctx_p, VM_OP_FUNC_DECL_N, obj, jsp_make_unknown_operand ()); break; } case VARG_ARRAY_DECL: { dump_double_address (ctx_p, VM_OP_ARRAY_DECL, res, jsp_make_unknown_operand ()); break; } case VARG_OBJ_DECL: { dump_double_address (ctx_p, VM_OP_OBJ_DECL, res, jsp_make_unknown_operand ()); break; } } return pos; } /* dump_varg_header_for_rewrite */ /** * Enumeration of possible rewrite types */ typedef enum { REWRITE_VARG_HEADER, REWRITE_FUNCTION_END, REWRITE_CONDITIONAL_CHECK, REWRITE_JUMP_TO_END, REWRITE_SIMPLE_OR_NESTED_JUMP, REWRITE_CASE_CLAUSE, REWRITE_DEFAULT_CLAUSE, REWRITE_WITH, REWRITE_FOR_IN, REWRITE_TRY, REWRITE_CATCH, REWRITE_FINALLY, REWRITE_SCOPE_CODE_FLAGS, REWRITE_REG_VAR_DECL, } rewrite_type_t; /** * Assert operands in rewrite operation */ static void dumper_assert_op_fields (jsp_ctx_t *ctx_p, /**< parser context */ rewrite_type_t rewrite_type, /**< type of rewrite */ op_meta meta) /**< intermediate instruction description */ { if (!jsp_is_dump_mode (ctx_p)) { return; } if (rewrite_type == REWRITE_FUNCTION_END) { JERRY_ASSERT (meta.op.op_idx == VM_OP_META); JERRY_ASSERT (meta.op.data.meta.type == OPCODE_META_TYPE_FUNCTION_END); JERRY_ASSERT (meta.op.data.meta.data_1 == VM_IDX_REWRITE_GENERAL_CASE); JERRY_ASSERT (meta.op.data.meta.data_2 == VM_IDX_REWRITE_GENERAL_CASE); } else if (rewrite_type == REWRITE_CONDITIONAL_CHECK) { JERRY_ASSERT (meta.op.op_idx == VM_OP_IS_FALSE_JMP_DOWN); } else if (rewrite_type == REWRITE_JUMP_TO_END) { JERRY_ASSERT (meta.op.op_idx == VM_OP_JMP_DOWN); } else if (rewrite_type == REWRITE_CASE_CLAUSE) { JERRY_ASSERT (meta.op.op_idx == VM_OP_IS_TRUE_JMP_DOWN); } else if (rewrite_type == REWRITE_DEFAULT_CLAUSE) { JERRY_ASSERT (meta.op.op_idx == VM_OP_JMP_DOWN); } else if (rewrite_type == REWRITE_TRY) { JERRY_ASSERT (meta.op.op_idx == VM_OP_TRY_BLOCK); } else if (rewrite_type == REWRITE_CATCH) { JERRY_ASSERT (meta.op.op_idx == VM_OP_META && meta.op.data.meta.type == OPCODE_META_TYPE_CATCH); } else if (rewrite_type == REWRITE_FINALLY) { JERRY_ASSERT (meta.op.op_idx == VM_OP_META && meta.op.data.meta.type == OPCODE_META_TYPE_FINALLY); } else if (rewrite_type == REWRITE_SCOPE_CODE_FLAGS) { JERRY_ASSERT (meta.op.op_idx == VM_OP_META); JERRY_ASSERT (meta.op.data.meta.data_1 == VM_IDX_REWRITE_GENERAL_CASE); JERRY_ASSERT (meta.op.data.meta.data_2 == VM_IDX_EMPTY); } else if (rewrite_type == REWRITE_REG_VAR_DECL) { JERRY_ASSERT (meta.op.op_idx == VM_OP_REG_VAR_DECL); } else { JERRY_UNREACHABLE (); } } /* dumper_assert_op_fields */ /** * Rewrite args_count field of an instruction */ void rewrite_varg_header_set_args_count (jsp_ctx_t *ctx_p, /**< parser context */ size_t args_count, /**< number of arguments */ vm_instr_counter_t pos) /**< position of instruction to rewrite */ { /* * FIXME: * Remove formal parameters / arguments number from instruction, * after ecma-values collection would become extendable (issue #310). * In the case, each 'varg' instruction would just append corresponding * argument / formal parameter name to values collection. */ if (!jsp_is_dump_mode (ctx_p)) { return; } op_meta om = dumper_get_op_meta (ctx_p, pos); switch (om.op.op_idx) { case VM_OP_FUNC_EXPR_N: case VM_OP_CONSTRUCT_N: case VM_OP_CALL_N: { if (args_count > 255) { PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "No more than 255 formal parameters / arguments are currently supported", LIT_ITERATOR_POS_ZERO); } om.op.data.func_expr_n.arg_list = (vm_idx_t) args_count; dumper_rewrite_op_meta (ctx_p, pos, om); break; } case VM_OP_FUNC_DECL_N: { if (args_count > 255) { PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "No more than 255 formal parameters are currently supported", LIT_ITERATOR_POS_ZERO); } om.op.data.func_decl_n.arg_list = (vm_idx_t) args_count; dumper_rewrite_op_meta (ctx_p, pos, om); break; } case VM_OP_ARRAY_DECL: case VM_OP_OBJ_DECL: { if (args_count > 65535) { PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "No more than 65535 formal parameters are currently supported", LIT_ITERATOR_POS_ZERO); } om.op.data.obj_decl.list_1 = (vm_idx_t) (args_count >> 8); om.op.data.obj_decl.list_2 = (vm_idx_t) (args_count & 0xffu); dumper_rewrite_op_meta (ctx_p, pos, om); break; } default: { JERRY_UNREACHABLE (); } } } /* rewrite_varg_header_set_args_count */ /** * Dump 'meta' instruction of 'call additional information' type, * containing call flags and, optionally, 'this' argument */ void dump_call_additional_info (jsp_ctx_t *ctx_p, /**< parser context */ opcode_call_flags_t flags, /**< call flags */ jsp_operand_t this_arg) /**< 'this' argument - if flags * include OPCODE_CALL_FLAGS_HAVE_THIS_ARG, * or empty operand - otherwise */ { if (flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) { JERRY_ASSERT (jsp_is_register_operand (this_arg) || jsp_is_this_operand (this_arg)); JERRY_ASSERT (!operand_is_empty (this_arg)); } else { JERRY_ASSERT (operand_is_empty (this_arg)); } dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_CALL_SITE_INFO), jsp_make_idx_const_operand (flags), this_arg); } /* dump_call_additional_info */ /** * Dump meta instruction, specifying the value of the argument */ void dump_varg (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) /**< operand, holding the value of the argument */ { dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG), op, jsp_make_empty_operand ()); } /* dump_varg */ void dump_prop_name_and_value (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t name, /**< property name */ jsp_operand_t value) /**< property value */ { JERRY_ASSERT (jsp_is_string_lit_operand (name)); dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG_PROP_DATA), name, value); } /* dump_prop_name_and_value */ void dump_prop_getter_decl (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t name, /**< property name */ jsp_operand_t func) /**< property getter */ { JERRY_ASSERT (jsp_is_string_lit_operand (name)); JERRY_ASSERT (jsp_is_register_operand (func)); dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG_PROP_GETTER), name, func); } /* dump_prop_getter_decl */ void dump_prop_setter_decl (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t name, /**< property name */ jsp_operand_t func) /**< property setter */ { JERRY_ASSERT (jsp_is_string_lit_operand (name)); JERRY_ASSERT (jsp_is_register_operand (func)); dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG_PROP_SETTER), name, func); } /* dump_prop_setter_decl */ /** * Dump property getter */ void dump_prop_getter (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t obj, /**< result operand */ jsp_operand_t base, /**< object, which property is being acquired */ jsp_operand_t prop_name) /**< operand, holding property name */ { dump_triple_address (ctx_p, VM_OP_PROP_GETTER, obj, base, prop_name); } /* dump_prop_getter */ /** * Dump property setter */ void dump_prop_setter (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t base, /**< object, which property is being set */ jsp_operand_t prop_name, /**< operand, holding property name */ jsp_operand_t obj) /**< operand, holding value to store */ { dump_triple_address (ctx_p, VM_OP_PROP_SETTER, base, prop_name, obj); } /* dump_prop_setter */ /** * Dump instruction, which deletes propery of an object */ void dump_delete_prop (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t res, /**< operand to store result of delete oeprator */ jsp_operand_t base, /**< operand, holding object, from which property is deleted */ jsp_operand_t prop_name) /**< operand, holding property name */ { dump_triple_address (ctx_p, VM_OP_DELETE_PROP, res, base, prop_name); } /* dump_delete_prop */ /** * Dump unary operation */ void dump_unary_op (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, jsp_operand_t res, jsp_operand_t op) { dump_double_address (ctx_p, opcode, res, op); } /* dump_unary_op */ /** * Dump binary operation */ void dump_binary_op (jsp_ctx_t *ctx_p, /**< parser context */ vm_op_t opcode, /**< opcode */ jsp_operand_t res, /**< result operand */ jsp_operand_t op1, /**< first operand */ jsp_operand_t op2) /**< second operand */ { dump_triple_address (ctx_p, opcode, res, op1, op2); } /* dump_binary_op */ /** * Dump conditional check, jump offset in which would be updated later * * @return position of dumped instruction */ vm_instr_counter_t dump_conditional_check_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) /**< operand, holding the value to check */ { vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); dump_triple_address (ctx_p, VM_OP_IS_FALSE_JMP_DOWN, op, jsp_make_unknown_operand (), jsp_make_unknown_operand ()); return pos; } /* dump_conditional_check_for_rewrite */ /** * Rewrite jump offset in conditional check */ void rewrite_conditional_check (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos) /**< position to jump to */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); op_meta jmp_op_meta = dumper_get_op_meta (ctx_p, pos); dumper_assert_op_fields (ctx_p, REWRITE_CONDITIONAL_CHECK, jmp_op_meta); jmp_op_meta.op.data.is_false_jmp_down.oc_idx_1 = id1; jmp_op_meta.op.data.is_false_jmp_down.oc_idx_2 = id2; dumper_rewrite_op_meta (ctx_p, pos, jmp_op_meta); } /* rewrite_conditional_check */ /** * Dump jump to end of the loop, jump offset would be updated by rewrite */ vm_instr_counter_t dump_jump_to_end_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ { vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); dump_double_address (ctx_p, VM_OP_JMP_DOWN, jsp_make_unknown_operand (), jsp_make_unknown_operand ()); return pos; } /* dump_jump_to_end_for_rewrite */ /** * Rewrite jump offset in jump instruction */ void rewrite_jump_to_end (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos)/**< position to jump to */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); op_meta jmp_op_meta = dumper_get_op_meta (ctx_p, pos); dumper_assert_op_fields (ctx_p, REWRITE_JUMP_TO_END, jmp_op_meta); jmp_op_meta.op.data.jmp_down.oc_idx_1 = id1; jmp_op_meta.op.data.jmp_down.oc_idx_2 = id2; dumper_rewrite_op_meta (ctx_p, pos, jmp_op_meta); } /* rewrite_jump_to_end */ /** * Get current instruction counter to use as jump target for loop iteration */ vm_instr_counter_t dumper_set_next_iteration_target (jsp_ctx_t *ctx_p) /**< parser context */ { return dumper_get_current_instr_counter (ctx_p); } /* dumper_set_next_iteration_target */ /** * Dump conditional/unconditional jump to next iteration of the loop */ void dump_continue_iterations_check (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos, /**< position to jump to */ jsp_operand_t op)/**< operand to check in condition * if operand is empty, unconditional jump is * dumped */ { const vm_instr_counter_t diff = (vm_instr_counter_t) (dumper_get_current_instr_counter (ctx_p) - pos); vm_idx_t id1, id2; split_instr_counter (diff, &id1, &id2); if (operand_is_empty (op)) { dump_double_address (ctx_p, VM_OP_JMP_UP, jsp_make_idx_const_operand (id1), jsp_make_idx_const_operand (id2)); } else { dump_triple_address (ctx_p, VM_OP_IS_TRUE_JMP_UP, op, jsp_make_idx_const_operand (id1), jsp_make_idx_const_operand (id2)); } } /** * Dump template of a jump instruction. * * Note: * the instruction's flags field is written later (see also: rewrite_simple_or_nested_jump_get_next). * * @return position of dumped instruction */ vm_instr_counter_t dump_simple_or_nested_jump_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ bool is_nested, /**< flag, indicating whether nested (true) * or simple (false) jump should be dumped */ bool is_conditional, /**< flag, indicating whether conditional (true) * or unconditional jump should be dumped */ bool is_inverted_condition, /**< if is_conditional is set, this flag * indicates whether to invert the condition */ jsp_operand_t cond, /**< condition (for conditional jumps), * empty operand - for non-conditional */ vm_instr_counter_t next_jump_for_tgt_oc) /**< instr counter of next * template targetted to * the same target - if any, * or MAX_OPCODES - otherwise */ { vm_idx_t id1, id2; split_instr_counter (next_jump_for_tgt_oc, &id1, &id2); vm_instr_counter_t ret = dumper_get_current_instr_counter (ctx_p); vm_op_t jmp_opcode; if (is_nested) { jmp_opcode = VM_OP_JMP_BREAK_CONTINUE; } else if (is_conditional) { if (is_inverted_condition) { jmp_opcode = VM_OP_IS_FALSE_JMP_DOWN; } else { jmp_opcode = VM_OP_IS_TRUE_JMP_DOWN; } } else { jmp_opcode = VM_OP_JMP_DOWN; } if (jmp_opcode == VM_OP_JMP_DOWN || jmp_opcode == VM_OP_JMP_BREAK_CONTINUE) { JERRY_ASSERT (jsp_is_empty_operand (cond)); dump_double_address (ctx_p, jmp_opcode, jsp_make_idx_const_operand (id1), jsp_make_idx_const_operand (id2)); } else { JERRY_ASSERT (!jsp_is_empty_operand (cond)); JERRY_ASSERT (jmp_opcode == VM_OP_IS_FALSE_JMP_DOWN || jmp_opcode == VM_OP_IS_TRUE_JMP_DOWN); dump_triple_address (ctx_p, jmp_opcode, cond, jsp_make_idx_const_operand (id1), jsp_make_idx_const_operand (id2)); } return ret; } /* dump_simple_or_nested_jump_for_rewrite */ /** * Write jump target position into previously dumped template of jump (simple or nested) instruction * * @return instr counter value that was encoded in the jump before rewrite */ vm_instr_counter_t rewrite_simple_or_nested_jump_and_get_next (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t jump_oc, /**< position of jump to rewrite */ vm_instr_counter_t target_oc) /**< the jump's target */ { if (!jsp_is_dump_mode (ctx_p)) { return MAX_OPCODES; } op_meta jump_op_meta = dumper_get_op_meta (ctx_p, jump_oc); vm_op_t jmp_opcode = (vm_op_t) jump_op_meta.op.op_idx; vm_idx_t id1, id2, id1_prev, id2_prev; if (target_oc < jump_oc) { split_instr_counter ((vm_instr_counter_t) (jump_oc - target_oc), &id1, &id2); } else { split_instr_counter ((vm_instr_counter_t) (target_oc - jump_oc), &id1, &id2); } if (jmp_opcode == VM_OP_JMP_DOWN) { if (target_oc < jump_oc) { jump_op_meta.op.op_idx = VM_OP_JMP_UP; id1_prev = jump_op_meta.op.data.jmp_up.oc_idx_1; id2_prev = jump_op_meta.op.data.jmp_up.oc_idx_2; jump_op_meta.op.data.jmp_up.oc_idx_1 = id1; jump_op_meta.op.data.jmp_up.oc_idx_2 = id2; } else { id1_prev = jump_op_meta.op.data.jmp_down.oc_idx_1; id2_prev = jump_op_meta.op.data.jmp_down.oc_idx_2; jump_op_meta.op.data.jmp_down.oc_idx_1 = id1; jump_op_meta.op.data.jmp_down.oc_idx_2 = id2; } } else if (jmp_opcode == VM_OP_IS_TRUE_JMP_DOWN) { if (target_oc < jump_oc) { jump_op_meta.op.op_idx = VM_OP_IS_TRUE_JMP_UP; id1_prev = jump_op_meta.op.data.is_true_jmp_up.oc_idx_1; id2_prev = jump_op_meta.op.data.is_true_jmp_up.oc_idx_2; jump_op_meta.op.data.is_true_jmp_up.oc_idx_1 = id1; jump_op_meta.op.data.is_true_jmp_up.oc_idx_2 = id2; } else { id1_prev = jump_op_meta.op.data.is_true_jmp_down.oc_idx_1; id2_prev = jump_op_meta.op.data.is_true_jmp_down.oc_idx_2; jump_op_meta.op.data.is_true_jmp_down.oc_idx_1 = id1; jump_op_meta.op.data.is_true_jmp_down.oc_idx_2 = id2; } } else if (jmp_opcode == VM_OP_IS_FALSE_JMP_DOWN) { if (target_oc < jump_oc) { jump_op_meta.op.op_idx = VM_OP_IS_FALSE_JMP_UP; id1_prev = jump_op_meta.op.data.is_false_jmp_up.oc_idx_1; id2_prev = jump_op_meta.op.data.is_false_jmp_up.oc_idx_2; jump_op_meta.op.data.is_false_jmp_up.oc_idx_1 = id1; jump_op_meta.op.data.is_false_jmp_up.oc_idx_2 = id2; } else { id1_prev = jump_op_meta.op.data.is_false_jmp_down.oc_idx_1; id2_prev = jump_op_meta.op.data.is_false_jmp_down.oc_idx_2; jump_op_meta.op.data.is_false_jmp_down.oc_idx_1 = id1; jump_op_meta.op.data.is_false_jmp_down.oc_idx_2 = id2; } } else { JERRY_ASSERT (!jsp_is_dump_mode (ctx_p) || (jmp_opcode == VM_OP_JMP_BREAK_CONTINUE)); JERRY_ASSERT (target_oc > jump_oc); id1_prev = jump_op_meta.op.data.jmp_break_continue.oc_idx_1; id2_prev = jump_op_meta.op.data.jmp_break_continue.oc_idx_2; jump_op_meta.op.data.jmp_break_continue.oc_idx_1 = id1; jump_op_meta.op.data.jmp_break_continue.oc_idx_2 = id2; } dumper_rewrite_op_meta (ctx_p, jump_oc, jump_op_meta); return vm_calc_instr_counter_from_idx_idx (id1_prev, id2_prev); } /* rewrite_simple_or_nested_jump_get_next */ /** * Dump template of 'with' instruction. * * Note: * the instruction's flags field is written later (see also: rewrite_with). * * @return position of dumped instruction */ vm_instr_counter_t dump_with_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) /**< jsp_operand_t - result of evaluating Expression * in WithStatement */ { vm_instr_counter_t oc = dumper_get_current_instr_counter (ctx_p); dump_triple_address (ctx_p, VM_OP_WITH, op, jsp_make_unknown_operand (), jsp_make_unknown_operand ()); 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 rewrite_with (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t oc) /**< instr counter of the instruction template */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, oc), &id1, &id2); op_meta with_op_meta = dumper_get_op_meta (ctx_p, oc); with_op_meta.op.data.with.oc_idx_1 = id1; with_op_meta.op.data.with.oc_idx_2 = id2; dumper_rewrite_op_meta (ctx_p, oc, with_op_meta); } /* rewrite_with */ /** * Dump 'meta' instruction of 'end with' type */ void dump_with_end (jsp_ctx_t *ctx_p) /**< parser context */ { dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_END_WITH), jsp_make_empty_operand (), jsp_make_empty_operand ()); } /* dump_with_end */ /** * Dump template of 'for_in' instruction. * * Note: * the instruction's flags field is written later (see also: rewrite_for_in). * * @return position of dumped instruction */ vm_instr_counter_t dump_for_in_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) /**< jsp_operand_t - result of evaluating Expression * in for-in statement */ { vm_instr_counter_t oc = dumper_get_current_instr_counter (ctx_p); dump_triple_address (ctx_p, VM_OP_FOR_IN, op, jsp_make_unknown_operand (), jsp_make_unknown_operand ()); return oc; } /* dump_for_in_for_rewrite */ /** * Write position of 'for_in' block's end to specified 'for_in' instruction template, * dumped earlier (see also: dump_for_in_for_rewrite). */ void rewrite_for_in (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t oc) /**< instr counter of the instruction template */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, oc), &id1, &id2); op_meta for_in_op_meta = dumper_get_op_meta (ctx_p, oc); for_in_op_meta.op.data.for_in.oc_idx_1 = id1; for_in_op_meta.op.data.for_in.oc_idx_2 = id2; dumper_rewrite_op_meta (ctx_p, oc, for_in_op_meta); } /* rewrite_for_in */ /** * Dump 'meta' instruction of 'end for_in' type */ void dump_for_in_end (jsp_ctx_t *ctx_p) /**< parser context */ { dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_END_FOR_IN), jsp_make_empty_operand (), jsp_make_empty_operand ()); } /* dump_for_in_end */ /** * Dump instruction, designating start of the try block * * @return position of dumped instruction */ vm_instr_counter_t dump_try_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ { vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); dump_double_address (ctx_p, VM_OP_TRY_BLOCK, jsp_make_unknown_operand (), jsp_make_unknown_operand ()); return pos; } /* dump_try_for_rewrite */ /** * Rewrite jump offset in instruction, designating start of the try block */ void rewrite_try (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos) /**< position of try block end */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); op_meta try_op_meta = dumper_get_op_meta (ctx_p, pos); dumper_assert_op_fields (ctx_p, REWRITE_TRY, try_op_meta); try_op_meta.op.data.try_block.oc_idx_1 = id1; try_op_meta.op.data.try_block.oc_idx_2 = id2; dumper_rewrite_op_meta (ctx_p, pos, try_op_meta); } /* rewrite_try */ /** * Dump instruction, designating start of the catch block * * @return position of the dumped instruction */ vm_instr_counter_t dump_catch_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) { vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); JERRY_ASSERT (jsp_is_string_lit_operand (op)); dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_CATCH), jsp_make_unknown_operand (), jsp_make_unknown_operand ()); dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER), op, jsp_make_empty_operand ()); return pos; } /* dump_catch_for_rewrite */ /** * Rewrite jump offset in instruction, designating start of the catch block */ void rewrite_catch (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos) /**< position of the catch block end */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); op_meta catch_op_meta = dumper_get_op_meta (ctx_p, pos); dumper_assert_op_fields (ctx_p, REWRITE_CATCH, catch_op_meta); catch_op_meta.op.data.meta.data_1 = id1; catch_op_meta.op.data.meta.data_2 = id2; dumper_rewrite_op_meta (ctx_p, pos, catch_op_meta); } /* rewrite_catch */ /** * Dump instruction, designating start of the finally block * * @return position of the dumped instruction */ vm_instr_counter_t dump_finally_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ { vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_FINALLY), jsp_make_unknown_operand (), jsp_make_unknown_operand ()); return pos; } /* dump_finally_for_rewrite */ /** * Rewrite jump offset in instruction, designating start of the finally block */ void rewrite_finally (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t pos) /**< position of the finally block end */ { vm_idx_t id1, id2; split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); op_meta finally_op_meta = dumper_get_op_meta (ctx_p, pos); dumper_assert_op_fields (ctx_p, REWRITE_FINALLY, finally_op_meta); finally_op_meta.op.data.meta.data_1 = id1; finally_op_meta.op.data.meta.data_2 = id2; dumper_rewrite_op_meta (ctx_p, pos, finally_op_meta); } /* rewrite_finally */ /** * Dump end of try-catch-finally block */ void dump_end_try_catch_finally (jsp_ctx_t *ctx_p) /**< parser context */ { dump_triple_address (ctx_p, VM_OP_META, jsp_make_idx_const_operand (OPCODE_META_TYPE_END_TRY_CATCH_FINALLY), jsp_make_empty_operand (), jsp_make_empty_operand ()); } /* dump_end_try_catch_finally */ /** * Dump throw instruction */ void dump_throw (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) { dump_single_address (ctx_p, VM_OP_THROW_VALUE, op); } /** * Dump instruction designating variable declaration */ void dump_variable_declaration (jsp_ctx_t *ctx_p, /**< parser context */ lit_cpointer_t lit_id) /**< literal which holds variable's name */ { jsp_operand_t op_var_name = jsp_make_string_lit_operand (lit_id); op_meta op = jsp_dmp_create_op_meta (ctx_p, VM_OP_VAR_DECL, &op_var_name, 1); JERRY_ASSERT (!jsp_is_dump_mode (ctx_p)); scopes_tree_add_var_decl (jsp_get_current_scopes_tree_node (ctx_p), op); } /* dump_variable_declaration */ /** * Dump return instruction */ void dump_ret (jsp_ctx_t *ctx_p) /**< parser context */ { dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_0 (ctx_p, VM_OP_RET)); } /* dump_ret */ /** * Dump 'reg_var_decl' instruction template * * @return position of the dumped instruction */ vm_instr_counter_t dump_reg_var_decl_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ { vm_instr_counter_t oc = dumper_get_current_instr_counter (ctx_p); dump_triple_address (ctx_p, VM_OP_REG_VAR_DECL, jsp_make_unknown_operand (), jsp_make_unknown_operand (), jsp_make_unknown_operand ()); return oc; } /* dump_reg_var_decl_for_rewrite */ /** * Rewrite 'reg_var_decl' instruction's template with current scope's register counts */ void rewrite_reg_var_decl (jsp_ctx_t *ctx_p, /**< parser context */ vm_instr_counter_t reg_var_decl_oc) /**< position of dumped 'reg_var_decl' template */ { op_meta opm = dumper_get_op_meta (ctx_p, reg_var_decl_oc); dumper_assert_op_fields (ctx_p, REWRITE_REG_VAR_DECL, opm); opm.op.data.reg_var_decl.tmp_regs_num = (vm_idx_t) (jsp_reg_max_for_temps - VM_REG_GENERAL_FIRST + 1); if (jsp_reg_max_for_local_var != VM_IDX_EMPTY) { JERRY_ASSERT (jsp_reg_max_for_local_var >= jsp_reg_max_for_temps); opm.op.data.reg_var_decl.local_var_regs_num = (vm_idx_t) (jsp_reg_max_for_local_var - jsp_reg_max_for_temps); jsp_reg_max_for_local_var = VM_IDX_EMPTY; } else { opm.op.data.reg_var_decl.local_var_regs_num = 0; } if (jsp_reg_max_for_args != VM_IDX_EMPTY) { if (jsp_reg_max_for_local_var != VM_IDX_EMPTY) { JERRY_ASSERT (jsp_reg_max_for_args >= jsp_reg_max_for_local_var); opm.op.data.reg_var_decl.arg_regs_num = (vm_idx_t) (jsp_reg_max_for_args - jsp_reg_max_for_local_var); } else { JERRY_ASSERT (jsp_reg_max_for_args >= jsp_reg_max_for_temps); opm.op.data.reg_var_decl.arg_regs_num = (vm_idx_t) (jsp_reg_max_for_args - jsp_reg_max_for_temps); } jsp_reg_max_for_args = VM_IDX_EMPTY; } else { opm.op.data.reg_var_decl.arg_regs_num = 0; } dumper_rewrite_op_meta (ctx_p, reg_var_decl_oc, opm); } /* rewrite_reg_var_decl */ /** * Dump return instruction */ void dump_retval (jsp_ctx_t *ctx_p, /**< parser context */ jsp_operand_t op) /**< operand, holding a value to return */ { dump_single_address (ctx_p, VM_OP_RETVAL, op); } /* dump_retval */ /** * Dumper initialization function */ void dumper_init (jsp_ctx_t *ctx_p __attr_unused___, bool show_instrs) { is_print_instrs = show_instrs; jsp_reg_next = VM_REG_GENERAL_FIRST; jsp_reg_max_for_temps = VM_REG_GENERAL_FIRST; jsp_reg_max_for_local_var = VM_IDX_EMPTY; jsp_reg_max_for_args = VM_IDX_EMPTY; } /* dumper_init */