diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index 0ef1a03cfc..c9e1b95bb9 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -755,6 +755,41 @@ dumper_finish_scope (void) STACK_DROP (temp_names, 1); } +/** + * Handle start of argument preparation instruction sequence generation + * + * Note: + * Values of registers, allocated for the code sequence, are not used outside of the sequence, + * so they can be reused, reducing register pressure. + * + * To reuse the registers, counter of register allocator is saved, and restored then, + * after finishing generation of the code sequence, using dumper_finish_varg_code_sequence. + * + * FIXME: + * Implement general register allocation mechanism + * + * See also: + * dumper_finish_varg_code_sequence + */ +void +dumper_start_varg_code_sequence (void) +{ + STACK_PUSH (temp_names, temp_name); +} /* dumper_start_varg_code_sequence */ + +/** + * Handle finish of argument preparation instruction sequence generation + * + * See also: + * dumper_start_varg_code_sequence + */ +void +dumper_finish_varg_code_sequence (void) +{ + temp_name = STACK_TOP (temp_names); + STACK_DROP (temp_names, 1); +} /* dumper_finish_varg_code_sequence */ + /** * Check that byte-code operand refers to 'eval' string * diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index 548d5ea148..12058fc80f 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -58,6 +58,8 @@ void dumper_free (void); void dumper_new_statement (void); void dumper_new_scope (void); void dumper_finish_scope (void); +void dumper_start_varg_code_sequence (void); +void dumper_finish_varg_code_sequence (void); extern bool dumper_is_eval_literal (operand); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 6b1307745b..f09b4b5edd 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -521,62 +521,61 @@ parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, opera skip_newlines (); while (!token_is (close_tt)) { + dumper_start_varg_code_sequence (); + operand op; - switch (vlt) + + if (vlt == VARG_FUNC_DECL + || vlt == VARG_FUNC_EXPR) { - case VARG_FUNC_DECL: - case VARG_FUNC_EXPR: - { - current_token_must_be (TOK_NAME); - op = literal_operand (token_data_as_lit_cp ()); - syntax_add_varg (op); - syntax_check_for_eval_and_arguments_in_strict_mode (op, is_strict_mode (), tok.loc); - break; - } - case VARG_ARRAY_DECL: - { - if (token_is (TOK_COMMA)) - { - op = dump_undefined_assignment_res (); - dump_varg (op); - args_num++; - skip_newlines (); - continue; - } - /* FALLTHRU */ - } - case VARG_CONSTRUCT_EXPR: + current_token_must_be (TOK_NAME); + op = literal_operand (token_data_as_lit_cp ()); + syntax_add_varg (op); + syntax_check_for_eval_and_arguments_in_strict_mode (op, is_strict_mode (), tok.loc); + dump_varg (op); + skip_newlines (); + } + else if (vlt == VARG_CONSTRUCT_EXPR + || vlt == VARG_CALL_EXPR) + { + op = parse_assignment_expression (true); + dump_varg (op); + skip_newlines (); + } + else if (vlt == VARG_ARRAY_DECL) + { + if (token_is (TOK_COMMA)) { - op = parse_assignment_expression (true); - break; + op = dump_undefined_assignment_res (); + dump_varg (op); } - case VARG_CALL_EXPR: + else { op = parse_assignment_expression (true); - break; - } - case VARG_OBJ_DECL: - { - parse_property_assignment (); - break; + dump_varg (op); + skip_newlines (); } } - - /* In case of obj_decl prop is already dumped. */ - if (vlt != VARG_OBJ_DECL) + else { - dump_varg (op); + JERRY_ASSERT (vlt == VARG_OBJ_DECL); + + parse_property_assignment (); + skip_newlines (); } - args_num++; - skip_newlines (); - if (!token_is (TOK_COMMA)) + if (token_is (TOK_COMMA)) + { + skip_newlines (); + } + else { current_token_must_be (close_tt); - break; } - skip_newlines (); + args_num++; + + dumper_finish_varg_code_sequence (); } if (args_count != NULL) diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index d076c3a673..3014d94a39 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -39,6 +39,10 @@ * The operator should return from the opcode handler with it's 'return value'. * * 5. No other operations with opcode handler's 'return value' variable should be performed. + * + * Note: + * get_variable_value, taking an idx should be called with instruction counter value, + * corresponding to the instruction, containing the idx argument. */ #define OP_UNIMPLEMENTED_LIST(op) \ @@ -690,6 +694,72 @@ opfunc_func_expr_n (opcode_t opdata, /**< operation data */ return ret_value; } /* opfunc_func_expr_n */ +/** + * Get 'this' argument value and call flags mask for function call + * + * See also: + * ECMA-262 v5, 11.2.3, steps 6-7 + + * @return ecma-value + * Returned value must be freed with ecma_free_value + */ +static ecma_value_t +vm_helper_call_get_call_flags_and_this_arg (int_data_t *int_data_p, /**< interpreter context */ + opcode_call_flags_t *out_flags_p) /**< out: call flags */ +{ + JERRY_ASSERT (out_flags_p != NULL); + + bool is_increase_instruction_pointer; + + opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; + idx_t this_arg_var_idx = INVALID_VALUE; + + opcode_t next_opcode = vm_get_opcode (int_data_p->opcodes_p, int_data_p->pos); + if (next_opcode.op_idx == __op__idx_meta + && next_opcode.data.meta.type == OPCODE_META_TYPE_CALL_SITE_INFO) + { + call_flags = (opcode_call_flags_t) next_opcode.data.meta.data_1; + + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + this_arg_var_idx = next_opcode.data.meta.data_2; + JERRY_ASSERT (is_reg_variable (int_data_p, this_arg_var_idx)); + + JERRY_ASSERT ((call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) == 0); + } + + is_increase_instruction_pointer = true; + } + else + { + is_increase_instruction_pointer = false; + } + + ecma_completion_value_t get_this_completion_value; + ecma_value_t this_value; + + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + get_this_completion_value = get_variable_value (int_data_p, this_arg_var_idx, false); + } + else + { + get_this_completion_value = ecma_op_implicit_this_value (int_data_p->lex_env_p); + } + JERRY_ASSERT (ecma_is_completion_value_normal (get_this_completion_value)); + + this_value = ecma_get_completion_value_value (get_this_completion_value); + + if (is_increase_instruction_pointer) + { + int_data_p->pos++; + } + + *out_flags_p = call_flags; + + return this_value; +} /* vm_helper_call_get_call_flags_and_this_arg */ + /** * 'Function call' opcode handler. * @@ -713,28 +783,10 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ int_data->pos++; - idx_t this_arg_var_idx = INVALID_VALUE; - - opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; - JERRY_ASSERT (!int_data->is_call_in_direct_eval_form); - opcode_t next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); - if (next_opcode.op_idx == __op__idx_meta - && next_opcode.data.meta.type == OPCODE_META_TYPE_CALL_SITE_INFO) - { - call_flags = (opcode_call_flags_t) next_opcode.data.meta.data_1; - - if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - this_arg_var_idx = next_opcode.data.meta.data_2; - JERRY_ASSERT (is_reg_variable (int_data, this_arg_var_idx)); - - JERRY_ASSERT ((call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) == 0); - } - - int_data->pos++; - } + opcode_call_flags_t call_flags; + ecma_value_t this_value = vm_helper_call_get_call_flags_and_this_arg (int_data, &call_flags); MEM_DEFINE_LOCAL_ARRAY (arg_values, args_number_idx, ecma_value_t); @@ -748,20 +800,6 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ { JERRY_ASSERT (args_read == args_number_idx); - ecma_completion_value_t get_this_completion_value; - - if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - get_this_completion_value = get_variable_value (int_data, this_arg_var_idx, false); - } - else - { - get_this_completion_value = ecma_op_implicit_this_value (int_data->lex_env_p); - } - JERRY_ASSERT (ecma_is_completion_value_normal (get_this_completion_value)); - - ecma_value_t this_value = ecma_get_completion_value_value (get_this_completion_value); - if (!ecma_op_is_callable (func_value)) { ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); @@ -798,8 +836,6 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ JERRY_ASSERT (!int_data->is_call_in_direct_eval_form); } } - - ecma_free_completion_value (get_this_completion_value); } else { @@ -817,6 +853,8 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ MEM_FINALIZE_LOCAL_ARRAY (arg_values); + ecma_free_value (this_value, true); + ECMA_FINALIZE (func_value); return ret_value; @@ -982,11 +1020,10 @@ opfunc_obj_decl (opcode_t opdata, /**< operation data */ ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ecma_completion_value_t completion = ecma_make_empty_completion_value (); ecma_object_t *obj_p = ecma_op_create_object_object_noarg (); for (uint32_t prop_index = 0; - prop_index < args_number; + prop_index < args_number && ecma_is_completion_value_empty (ret_value); prop_index++) { ecma_completion_value_t evaluate_prop_completion = vm_loop (int_data, NULL); @@ -1002,106 +1039,98 @@ opfunc_obj_decl (opcode_t opdata, /**< operation data */ || type == OPCODE_META_TYPE_VARG_PROP_SETTER); const idx_t prop_name_var_idx = next_opcode.data.meta.data_1; + JERRY_ASSERT (is_reg_variable (int_data, prop_name_var_idx)); + const idx_t value_for_prop_desc_var_idx = next_opcode.data.meta.data_2; - ecma_completion_value_t value_for_prop_desc = get_variable_value (int_data, - value_for_prop_desc_var_idx, - false); + ECMA_TRY_CATCH (value_for_prop_desc, + get_variable_value (int_data, + value_for_prop_desc_var_idx, + false), + ret_value); + ECMA_TRY_CATCH (prop_name_value, + get_variable_value (int_data, + prop_name_var_idx, + false), + ret_value); + ECMA_TRY_CATCH (prop_name_str_value, + ecma_op_to_string (prop_name_value), + ret_value); - if (ecma_is_completion_value_normal (value_for_prop_desc)) + bool is_throw_syntax_error = false; + + ecma_string_t *prop_name_string_p = ecma_get_string_from_value (prop_name_str_value); + ecma_property_t *previous_p = ecma_op_object_get_own_property (obj_p, prop_name_string_p); + + const bool is_previous_undefined = (previous_p == NULL); + const bool is_previous_data_desc = (!is_previous_undefined + && previous_p->type == ECMA_PROPERTY_NAMEDDATA); + const bool is_previous_accessor_desc = (!is_previous_undefined + && previous_p->type == ECMA_PROPERTY_NAMEDACCESSOR); + JERRY_ASSERT (is_previous_undefined || is_previous_data_desc || is_previous_accessor_desc); + + ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); { - JERRY_ASSERT (is_reg_variable (int_data, prop_name_var_idx)); - - ECMA_TRY_CATCH (prop_name_value, - get_variable_value (int_data, - prop_name_var_idx, - false), - ret_value); - ECMA_TRY_CATCH (prop_name_str_value, - ecma_op_to_string (prop_name_value), - ret_value); - - bool is_throw_syntax_error = false; - - ecma_string_t *prop_name_string_p = ecma_get_string_from_value (prop_name_str_value); - ecma_property_t *previous_p = ecma_op_object_get_own_property (obj_p, prop_name_string_p); - - const bool is_previous_undefined = (previous_p == NULL); - const bool is_previous_data_desc = (!is_previous_undefined - && previous_p->type == ECMA_PROPERTY_NAMEDDATA); - const bool is_previous_accessor_desc = (!is_previous_undefined - && previous_p->type == ECMA_PROPERTY_NAMEDACCESSOR); - JERRY_ASSERT (is_previous_undefined || is_previous_data_desc || is_previous_accessor_desc); - - ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); - { - prop_desc.is_enumerable_defined = true; - prop_desc.is_enumerable = true; + prop_desc.is_enumerable_defined = true; + prop_desc.is_enumerable = true; - prop_desc.is_configurable_defined = true; - prop_desc.is_configurable = true; - } + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = true; + } - if (type == OPCODE_META_TYPE_VARG_PROP_DATA) + if (type == OPCODE_META_TYPE_VARG_PROP_DATA) + { + prop_desc.is_value_defined = true; + prop_desc.value = value_for_prop_desc; + + prop_desc.is_writable_defined = true; + prop_desc.is_writable = true; + + if (!is_previous_undefined + && ((is_previous_data_desc + && int_data->is_strict) + || is_previous_accessor_desc)) { - prop_desc.is_value_defined = true; - prop_desc.value = ecma_get_completion_value_value (value_for_prop_desc); - - prop_desc.is_writable_defined = true; - prop_desc.is_writable = true; - - if (!is_previous_undefined - && ((is_previous_data_desc - && int_data->is_strict) - || is_previous_accessor_desc)) - { - is_throw_syntax_error = true; - } + is_throw_syntax_error = true; } - else if (type == OPCODE_META_TYPE_VARG_PROP_GETTER) + } + else if (type == OPCODE_META_TYPE_VARG_PROP_GETTER) + { + prop_desc.is_get_defined = true; + prop_desc.get_p = ecma_get_object_from_value (value_for_prop_desc); + + if (!is_previous_undefined + && is_previous_data_desc) { - prop_desc.is_get_defined = true; - prop_desc.get_p = ecma_get_object_from_completion_value (value_for_prop_desc); - - if (!is_previous_undefined - && is_previous_data_desc) - { - is_throw_syntax_error = true; - } + is_throw_syntax_error = true; } - else + } + else + { + prop_desc.is_set_defined = true; + prop_desc.set_p = ecma_get_object_from_value (value_for_prop_desc); + + if (!is_previous_undefined + && is_previous_data_desc) { - prop_desc.is_set_defined = true; - prop_desc.set_p = ecma_get_object_from_completion_value (value_for_prop_desc); - - if (!is_previous_undefined - && is_previous_data_desc) - { - is_throw_syntax_error = true; - } + is_throw_syntax_error = true; } + } - /* The SyntaxError should be treated as an early error */ - JERRY_ASSERT (!is_throw_syntax_error); - - ecma_completion_value_t define_prop_completion = ecma_op_object_define_own_property (obj_p, - prop_name_string_p, - &prop_desc, - false); - JERRY_ASSERT (ecma_is_completion_value_normal_true (define_prop_completion) - || ecma_is_completion_value_normal_false (define_prop_completion)); + /* The SyntaxError should be treated as an early error */ + JERRY_ASSERT (!is_throw_syntax_error); - ecma_free_completion_value (value_for_prop_desc); + ecma_completion_value_t define_prop_completion = ecma_op_object_define_own_property (obj_p, + prop_name_string_p, + &prop_desc, + false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (define_prop_completion) + || ecma_is_completion_value_normal_false (define_prop_completion)); - ECMA_FINALIZE (prop_name_str_value); - ECMA_FINALIZE (prop_name_value); - } - else - { - completion = value_for_prop_desc; + ECMA_FINALIZE (prop_name_str_value); + ECMA_FINALIZE (prop_name_value); - break; - } + ECMA_FINALIZE (value_for_prop_desc); int_data->pos++; } @@ -1109,21 +1138,17 @@ opfunc_obj_decl (opcode_t opdata, /**< operation data */ { JERRY_ASSERT (ecma_is_completion_value_throw (evaluate_prop_completion)); - completion = evaluate_prop_completion; - - break; + ret_value = evaluate_prop_completion; } } - if (ecma_is_completion_value_empty (completion)) + if (ecma_is_completion_value_empty (ret_value)) { ret_value = set_variable_value (int_data, obj_lit_oc, lhs_var_idx, ecma_make_object_value (obj_p)); } else { - JERRY_ASSERT (ecma_is_completion_value_throw (completion)); - - ret_value = completion; + JERRY_ASSERT (ecma_is_completion_value_throw (ret_value)); } ecma_deref_object (obj_p);