diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index 4be1edada..f515cfe98 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -764,6 +764,27 @@ dump_intrinsic (operand obj, operand arg) return dump_undefined_assignment_res (); } +/** + * 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 ECMA_MAGIC_STRING_EVAL string, + * false - otherwise. + */ +bool +dumper_is_eval_literal (operand obj) /**< byte-code operand */ +{ + /* + * FIXME: Switch to corresponding magic string + */ + const ecma_char_t *eval_string_p = (const ecma_char_t *) "eval"; + bool is_eval_lit = (obj.type == OPERAND_LITERAL + && lit_literal_equal_type_zt (lit_get_literal_by_cp (obj.data.lit_id), + eval_string_p)); + + return is_eval_lit; +} /* dumper_is_eval_literal */ + void dump_boolean_assignment (operand op, bool is_true) { @@ -1077,14 +1098,30 @@ rewrite_varg_header_set_args_count (uint8_t args_count) } } +/** + * Dump 'meta' instruction of 'call additional information' type, + * containing call flags and, optionally, 'this' argument + */ void -dump_this_arg (operand this_arg) +dump_call_additional_info (opcode_call_flags_t flags, /**< call flags */ + operand this_arg) /**< 'this' argument - if flags include OPCODE_CALL_FLAGS_HAVE_THIS_ARG, + * or empty operand - otherwise */ { - JERRY_ASSERT (this_arg.type == OPERAND_TMP); - const opcode_t opcode = getop_meta (OPCODE_META_TYPE_THIS_ARG, this_arg.data.uid, INVALID_VALUE); + if (flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + JERRY_ASSERT (this_arg.type == OPERAND_TMP); + JERRY_ASSERT (!operand_is_empty (this_arg)); + } + else + { + JERRY_ASSERT (operand_is_empty (this_arg)); + } + + const opcode_t opcode = getop_meta (OPCODE_META_TYPE_CALL_SITE_INFO, + flags, + (flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) ? this_arg.data.uid : INVALID_VALUE); serializer_dump_op_meta (create_op_meta_000 (opcode)); - return; -} +} /* dump_call_additional_info */ void dump_varg (operand op) diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index f31dfc05f..94719a6e7 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -61,6 +61,8 @@ void dumper_finish_scope (void); bool dumper_is_intrinsic (operand); operand dump_intrinsic (operand, operand); +extern bool dumper_is_eval_literal (operand); + void dump_boolean_assignment (operand, bool); operand dump_boolean_assignment_res (bool); void dump_string_assignment (operand, lit_cpointer_t); @@ -78,7 +80,7 @@ operand dump_variable_assignment_res (operand); void dump_varg_header_for_rewrite (varg_list_type, operand); operand rewrite_varg_header_set_args_count (uint8_t); -void dump_this_arg (operand); +void dump_call_additional_info (opcode_call_flags_t, operand); void dump_varg (operand); void dump_prop_name_and_value (operand, operand); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 962315836..011ef6d3c 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -316,11 +316,13 @@ parse_property_assignment (void) For each ALT dumps appropriate bytecode. Uses OBJ during dump if neccesary. Result tmp. */ static operand -parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, operand *this_arg) +parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, operand *this_arg_p) { token_type close_tt = TOK_CLOSE_PAREN; uint8_t args_num = 0; + JERRY_ASSERT (!(vlt != VARG_CALL_EXPR && this_arg_p != NULL)); + switch (vlt) { case VARG_FUNC_DECL: @@ -342,11 +344,58 @@ parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, opera { break; } - if (this_arg != NULL && this_arg->type == OPERAND_LITERAL) + + opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; + + operand this_arg = empty_operand (); + if (this_arg_p != NULL + && !operand_is_empty (*this_arg_p)) { - *this_arg = dump_variable_assignment_res (*this_arg); + call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_HAVE_THIS_ARG); + + if (this_arg_p->type == OPERAND_LITERAL) + { + /* + * FIXME: + * Base of CallExpression should be evaluated only once during evaluation of CallExpression + * + * See also: + * Evaluation of MemberExpression (ECMA-262 v5, 11.2.1) + */ + this_arg = dump_variable_assignment_res (*this_arg_p); + } + else + { + this_arg = *this_arg_p; + } + + /* + * Presence of explicit 'this' argument implies that it is not Direct call to Eval + * + * See also: + * ECMA-262 v5, 15.2.2.1 + */ } + else if (dumper_is_eval_literal (obj)) + { + call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM); + } + dump_varg_header_for_rewrite (vlt, obj); + + if (call_flags != OPCODE_CALL_FLAGS__EMPTY) + { + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + JERRY_ASSERT (!operand_is_empty (this_arg)); + dump_call_additional_info (call_flags, this_arg); + } + else + { + dump_call_additional_info (call_flags, empty_operand ()); + } + } + break; } case VARG_ARRAY_DECL: @@ -365,11 +414,6 @@ parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, opera break; } } - if (vlt == VARG_CALL_EXPR && this_arg != NULL && !operand_is_empty (*this_arg)) - { - dump_this_arg (*this_arg); - args_num++; - } skip_newlines (); while (!token_is (close_tt)) diff --git a/jerry-core/parser/js/scopes-tree.cpp b/jerry-core/parser/js/scopes-tree.cpp index 91b6b53d8..5700c2b0b 100644 --- a/jerry-core/parser/js/scopes-tree.cpp +++ b/jerry-core/parser/js/scopes-tree.cpp @@ -338,7 +338,6 @@ generate_opcode (scopes_tree tree, opcode_counter_t opc_index, lit_id_hash_table change_uid (om, lit_ids, 0x011); break; } - case OPCODE_META_TYPE_THIS_ARG: case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { @@ -351,6 +350,7 @@ generate_opcode (scopes_tree tree, opcode_counter_t opc_index, lit_id_hash_table 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: case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: { change_uid (om, lit_ids, 0x000); @@ -477,7 +477,6 @@ count_new_literals_in_opcode (scopes_tree tree, opcode_counter_t opc_index) insert_uids_to_lit_id_map (om, 0x011); break; } - case OPCODE_META_TYPE_THIS_ARG: case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { @@ -490,6 +489,7 @@ count_new_literals_in_opcode (scopes_tree tree, opcode_counter_t opc_index) 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: case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: { insert_uids_to_lit_id_map (om, 0x000); diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index d916242a6..b0b2ddef9 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -653,44 +653,40 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ int_data->pos++; - bool this_arg_var_idx_set = false; idx_t this_arg_var_idx = INVALID_VALUE; - idx_t args_number; + + opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; 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_THIS_ARG) + && next_opcode.data.meta.type == OPCODE_META_TYPE_CALL_SITE_INFO) { - this_arg_var_idx = next_opcode.data.meta.data_1; - JERRY_ASSERT (is_reg_variable (int_data, this_arg_var_idx)); + call_flags = (opcode_call_flags_t) next_opcode.data.meta.data_1; - this_arg_var_idx_set = true; - - JERRY_ASSERT (args_number_idx > 0); - args_number = (idx_t) (args_number_idx - 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)); + } int_data->pos++; } - else - { - args_number = args_number_idx; - } - MEM_DEFINE_LOCAL_ARRAY (arg_values, args_number, ecma_value_t); + MEM_DEFINE_LOCAL_ARRAY (arg_values, args_number_idx, ecma_value_t); ecma_length_t args_read; ecma_completion_value_t get_arg_completion = fill_varg_list (int_data, - args_number, + args_number_idx, arg_values, &args_read); if (ecma_is_completion_value_empty (get_arg_completion)) { - JERRY_ASSERT (args_read == args_number); + JERRY_ASSERT (args_read == args_number_idx); ecma_completion_value_t get_this_completion_value; - if (this_arg_var_idx_set) + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) { get_this_completion_value = get_variable_value (int_data, this_arg_var_idx, false); } @@ -714,7 +710,7 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ ecma_op_function_call (func_obj_p, this_value, arg_values, - args_number), + args_number_idx), ret_value); ret_value = set_variable_value (int_data, lit_oc, @@ -1680,7 +1676,7 @@ opfunc_meta (opcode_t opdata, /**< operation data */ case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: case OPCODE_META_TYPE_UNDEFINED: - case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_FUNCTION_END: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 237f83405..26874e1a7 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -59,7 +59,8 @@ typedef enum typedef enum { OPCODE_META_TYPE_UNDEFINED, /**< undefined meta (should be rewritten) */ - OPCODE_META_TYPE_THIS_ARG, /**< value (var_idx) of this used during call */ + OPCODE_META_TYPE_CALL_SITE_INFO, /**< optional additional information about call site + * (includes opcode_call_flags_t and can include 'this' argument) */ OPCODE_META_TYPE_VARG, /**< element (var_idx) of arguments' list */ OPCODE_META_TYPE_VARG_PROP_DATA, /**< name (lit_idx) and value (var_idx) for a data property descriptor */ OPCODE_META_TYPE_VARG_PROP_GETTER, /**< name (lit_idx) and getter (var_idx) for an accessor property descriptor */ @@ -74,6 +75,17 @@ typedef enum * (See also: opcode_scope_code_flags_t) */ } opcode_meta_type; +typedef enum : idx_t +{ + OPCODE_CALL_FLAGS__EMPTY = (0u), /**< initializer for empty flag set */ + OPCODE_CALL_FLAGS_HAVE_THIS_ARG = (1u << 0), /**< flag, indicating that call is performed + * with 'this' argument specified */ + OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM = (1u << 1) /**< flag, indicating that call is performed + * in form 'eval (...)', i.e. through 'eval' string + * without object base (i.e. with lexical environment + * as base), so it can be a direct call to eval */ +} opcode_call_flags_t; + /** * Flags indicating various properties of a scope's code */ diff --git a/jerry-core/vm/pretty-printer.cpp b/jerry-core/vm/pretty-printer.cpp index 00b1723c6..e39c27e14 100644 --- a/jerry-core/vm/pretty-printer.cpp +++ b/jerry-core/vm/pretty-printer.cpp @@ -266,15 +266,9 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) } case NAME_TO_ID (call_n): { - if (opm.op.data.call_n.arg_list == 0) - { - pp_printf ("%s = %s ();", opm.op, opm.lit_id, oc, 1); - } - else - { - vargs_num = opm.op.data.call_n.arg_list; - seen_vargs = 0; - } + vargs_num = opm.op.data.call_n.arg_list; + seen_vargs = 0; + break; } case NAME_TO_ID (native_call): @@ -381,13 +375,17 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) printf ("unknown meta;"); break; } - case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_VARG_PROP_DATA: case OPCODE_META_TYPE_VARG_PROP_GETTER: case OPCODE_META_TYPE_VARG_PROP_SETTER: { - seen_vargs++; + if (opm.op.data.meta.type != OPCODE_META_TYPE_CALL_SITE_INFO) + { + seen_vargs++; + } + if (seen_vargs == vargs_num) { bool found = false; @@ -473,15 +471,26 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) for (opcode_counter_t counter = start; counter <= oc; counter++) { opcode_t meta_op = serializer_get_opcode (counter); + switch (meta_op.op_idx) { case NAME_TO_ID (meta): { switch (meta_op.data.meta.type) { - case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_CALL_SITE_INFO: { - pp_printf ("this_arg = %s", meta_op, NULL, counter, 2); + opcode_call_flags_t call_flags = (opcode_call_flags_t) meta_op.data.meta.data_1; + + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + pp_printf ("this_arg = %s", meta_op, NULL, counter, 3); + } + if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) + { + printf ("['direct call to eval' form]"); + } + break; } case OPCODE_META_TYPE_VARG: