/* Copyright 2014 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 "opcodes-ecma-support.h" #include "globals.h" #include "interpreter.h" #include "jerry-libc.h" #include "mem-heap.h" #include "opcodes.h" #include "actuators.h" #include "common-io.h" #include "sensors.h" #include "ecma-objects.h" /** * Note: * The note describes exception handling in opcode handlers that perform operations, * that can throw exceptions, and do not themself handle the exceptions. * * Generally, each opcode handler consists of sequence of operations. * Some of these operations (exceptionable operations) can throw exceptions and other - cannot. * * 1. At the beginning of the handler there should be declared opcode handler's 'return value' variable. * * 2. All exceptionable operations except the last should be enclosed in ECMA_TRY_CATCH macro. * All subsequent operations in the opcode handler should be placed into block between * the ECMA_TRY_CATCH and corresponding ECMA_FINALIZE. * * 3. The last exceptionable's operation result should be assigned directly to opcode handler's * 'return value' variable without using ECMA_TRY_CATCH macro. * * 4. After last ECMA_FINALIZE statement there should be only one operator. * 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. */ /** * String literal copy descriptor. */ typedef struct { ecma_char_t *str_p; /**< pointer to copied string literal */ ecma_char_t literal_copy[32]; /**< buffer with string literal, if it is stored locally (i.e. not in the heap) */ } string_literal_copy; /** * Initialize string literal copy. */ static void init_string_literal_copy (idx_t idx, /**< literal identifier */ string_literal_copy *str_lit_descr_p) /**< pointer to string literal copy descriptor */ { JERRY_ASSERT (str_lit_descr_p != NULL); ssize_t sz = try_get_string_by_idx (idx, str_lit_descr_p->literal_copy, sizeof (str_lit_descr_p->literal_copy)); if (sz > 0) { str_lit_descr_p->str_p = str_lit_descr_p->literal_copy; } else { JERRY_ASSERT (sz < 0); ssize_t req_sz = -sz; str_lit_descr_p->str_p = mem_heap_alloc_block ((size_t) req_sz, MEM_HEAP_ALLOC_SHORT_TERM); sz = try_get_string_by_idx (idx, str_lit_descr_p->str_p, req_sz); JERRY_ASSERT (sz > 0); } } /* init_string_literal */ /** * Free string literal copy. */ static void free_string_literal_copy (string_literal_copy *str_lit_descr_p) /**< string literal copy descriptor */ { JERRY_ASSERT (str_lit_descr_p != NULL); JERRY_ASSERT (str_lit_descr_p->str_p != NULL); if (str_lit_descr_p->str_p == str_lit_descr_p->literal_copy) { /* copy is inside descriptor */ } else { mem_heap_free_block (str_lit_descr_p->str_p); } str_lit_descr_p->str_p = NULL; return; } /* free_string_literal */ #define OP_UNIMPLEMENTED_LIST(op) \ op (native_call) \ op (func_expr_n) \ op (varg_list) \ op (array_decl) \ op (prop) \ op (prop_get_decl) \ op (prop_set_decl) \ op (obj_decl) \ op (delete) \ op (with) \ op (end_with) \ op (meta) \ static char __unused unimplemented_list_end #define DEFINE_UNIMPLEMENTED_OP(op) \ ecma_completion_value_t opfunc_ ## op (opcode_t opdata, int_data_t *int_data) \ { \ JERRY_UNIMPLEMENTED_REF_UNUSED_VARS (opdata, int_data); \ } OP_UNIMPLEMENTED_LIST (DEFINE_UNIMPLEMENTED_OP); #undef DEFINE_UNIMPLEMENTED_OP /** * 'Nop' opcode handler. */ ecma_completion_value_t opfunc_nop (opcode_t opdata __unused, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { int_data->pos++; return ecma_make_empty_completion_value (); } /* opfunc_nop */ ecma_completion_value_t opfunc_call_1 (opcode_t opdata __unused, int_data_t *int_data) { ecma_completion_value_t ret_value; ret_value = ecma_make_empty_completion_value (); int_data->pos++; ECMA_TRY_CATCH (arg_value, get_variable_value (int_data, opdata.data.call_1.arg1_lit_idx, false), ret_value); /**********************************************************/ FIXME (/* Native call, i.e. opfunc_native_call */); bool is_native_call = true; string_literal_copy function_name; init_string_literal_copy (opdata.data.call_1.name_lit_idx, &function_name); if (!__strcmp ((const char*) function_name.str_p, "LEDToggle")) { JERRY_ASSERT (arg_value.value.value_type == ECMA_TYPE_NUMBER); ecma_number_t * num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_value.value.value); uint32_t int_num = (uint32_t) * num_p; led_toggle (int_num); ret_value = ecma_make_empty_completion_value (); } else if (!__strcmp ((const char*) function_name.str_p, "LEDOn")) { JERRY_ASSERT (arg_value.value.value_type == ECMA_TYPE_NUMBER); ecma_number_t * num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_value.value.value); uint32_t int_num = (uint32_t) * num_p; led_on (int_num); ret_value = ecma_make_empty_completion_value (); } else if (!__strcmp ((const char*) function_name.str_p, "LEDOff")) { JERRY_ASSERT (arg_value.value.value_type == ECMA_TYPE_NUMBER); ecma_number_t * num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_value.value.value); uint32_t int_num = (uint32_t) * num_p; led_off (int_num); ret_value = ecma_make_empty_completion_value (); } else if (!__strcmp ((const char*) function_name.str_p, "LEDOnce")) { JERRY_ASSERT (arg_value.value.value_type == ECMA_TYPE_NUMBER); ecma_number_t * num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_value.value.value); uint32_t int_num = (uint32_t) * num_p; led_blink_once (int_num); ret_value = ecma_make_empty_completion_value (); } else if (!__strcmp ((const char*) function_name.str_p, "wait")) { JERRY_ASSERT (arg_value.value.value_type == ECMA_TYPE_NUMBER); ecma_number_t * num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_value.value.value); uint32_t int_num = (uint32_t) * num_p; wait_ms (int_num); ret_value = ecma_make_empty_completion_value (); } else if (!__strcmp ((const char *) function_name.str_p, "exit")) { JERRY_ASSERT (arg_value.value.value_type == ECMA_TYPE_NUMBER); ecma_number_t * num_p = (ecma_number_t*) ECMA_GET_POINTER (arg_value.value.value); uint32_t int_num = (uint32_t) * num_p; ecma_value_t exit_status = ecma_make_simple_value (int_num == 0 ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE); ret_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_EXIT, exit_status, ECMA_TARGET_ID_RESERVED); } else { is_native_call = false; } if (is_native_call) { #ifdef __TARGET_HOST_x64 __printf ("%d::op_call_1:idx:%d:%d\t", int_data->pos, opdata.data.call_1.name_lit_idx, opdata.data.call_1.arg1_lit_idx); __printf ("%s\n", function_name.str_p); #endif } free_string_literal_copy (&function_name); /**********************************************************/ if (!is_native_call) { const idx_t func_name_lit_idx = opdata.data.call_1.name_lit_idx; const idx_t lhs_var_idx = opdata.data.call_1.lhs; ECMA_TRY_CATCH (func_value, get_variable_value (int_data, func_name_lit_idx, false), ret_value); if (!ecma_op_is_callable (func_value.value)) { ret_value = ecma_make_throw_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); } else { ecma_object_t *func_obj_p = ECMA_GET_POINTER (func_value.value.value); ECMA_TRY_CATCH (this_value, ecma_op_implicit_this_value (int_data->lex_env_p), ret_value); ECMA_FUNCTION_CALL (call_completion, ecma_op_function_call (func_obj_p, this_value.value, &arg_value.value, 1), ret_value); ret_value = set_variable_value (int_data, lhs_var_idx, call_completion.value); ECMA_FINALIZE (call_completion); ECMA_FINALIZE (this_value); } ECMA_FINALIZE (func_value); } ECMA_FINALIZE (arg_value); return ret_value; } /** * 'Assignment' opcode handler. * * Note: * This handler implements case of assignment of a literal's or a variable's * value to a variable. Assignment to an object's property is not implemented * by this opcode. * * FIXME: Add cross link with 'property accessor assignment` opcode. * * See also: ECMA-262 v5, 11.13.1 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_assignment (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.assignment.var_left; const opcode_arg_type_operand type_value_right = opdata.data.assignment.type_value_right; const idx_t src_val_descr = opdata.data.assignment.value_right; int_data->pos++; ecma_completion_value_t get_value_completion; switch (type_value_right) { case OPCODE_ARG_TYPE_SIMPLE: { get_value_completion = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_simple_value (src_val_descr), ECMA_TARGET_ID_RESERVED); break; } case OPCODE_ARG_TYPE_STRING: { ecma_string_t *ecma_string_p = ecma_new_ecma_string_from_lit_index (src_val_descr); get_value_completion = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_string_value (ecma_string_p), ECMA_TARGET_ID_RESERVED); break; } case OPCODE_ARG_TYPE_VARIABLE: { get_value_completion = get_variable_value (int_data, src_val_descr, false); break; } case OPCODE_ARG_TYPE_NUMBER: { ecma_number_t *num_p = ecma_alloc_number (); *num_p = get_number_by_idx (src_val_descr); get_value_completion = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_number_value (num_p), ECMA_TARGET_ID_RESERVED); break; } case OPCODE_ARG_TYPE_SMALLINT: { ecma_number_t *num_p = ecma_alloc_number (); *num_p = src_val_descr; get_value_completion = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_number_value (num_p), ECMA_TARGET_ID_RESERVED); break; } } if (unlikely (ecma_is_completion_value_throw (get_value_completion))) { return get_value_completion; } else { JERRY_ASSERT (ecma_is_completion_value_normal (get_value_completion)); ecma_completion_value_t assignment_completion_value = set_variable_value (int_data, dst_var_idx, get_value_completion.value); ecma_free_completion_value (get_value_completion); return assignment_completion_value; } } /* opfunc_assignment */ /** * 'Pre increment' opcode handler. * * See also: ECMA-262 v5, 11.4.4 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_pre_incr (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.pre_incr.dst; const idx_t incr_var_idx = opdata.data.pre_incr.var_right; int_data->pos++; ecma_completion_value_t ret_value; // 1., 2., 3. ECMA_TRY_CATCH (old_value, get_variable_value (int_data, incr_var_idx, true), ret_value); ECMA_TRY_CATCH (old_num_value, ecma_op_to_number (old_value.value), ret_value); // 4. ecma_number_t* new_num_p = ecma_alloc_number (); ecma_number_t* old_num_p = (ecma_number_t*) ECMA_GET_POINTER (old_num_value.value.value); *new_num_p = ecma_op_number_add (*old_num_p, ECMA_NUMBER_ONE); ecma_value_t new_num_value = ecma_make_number_value (new_num_p); // 5. ret_value = set_variable_value (int_data, incr_var_idx, new_num_value); // assignment of operator result to register variable ecma_completion_value_t reg_assignment_res = set_variable_value (int_data, dst_var_idx, new_num_value); JERRY_ASSERT (ecma_is_empty_completion_value (reg_assignment_res)); ecma_dealloc_number (new_num_p); ECMA_FINALIZE (old_num_value); ECMA_FINALIZE (old_value); return ret_value; } /* opfunc_pre_incr */ /** * 'Pre decrement' opcode handler. * * See also: ECMA-262 v5, 11.4.4 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_pre_decr (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.pre_decr.dst; const idx_t decr_var_idx = opdata.data.pre_decr.var_right; int_data->pos++; ecma_completion_value_t ret_value; // 1., 2., 3. ECMA_TRY_CATCH (old_value, get_variable_value (int_data, decr_var_idx, true), ret_value); ECMA_TRY_CATCH (old_num_value, ecma_op_to_number (old_value.value), ret_value); // 4. ecma_number_t* new_num_p = ecma_alloc_number (); ecma_number_t* old_num_p = (ecma_number_t*) ECMA_GET_POINTER (old_num_value.value.value); *new_num_p = ecma_op_number_substract (*old_num_p, ECMA_NUMBER_ONE); ecma_value_t new_num_value = ecma_make_number_value (new_num_p); // 5. ret_value = set_variable_value (int_data, decr_var_idx, new_num_value); // assignment of operator result to register variable ecma_completion_value_t reg_assignment_res = set_variable_value (int_data, dst_var_idx, new_num_value); JERRY_ASSERT (ecma_is_empty_completion_value (reg_assignment_res)); ecma_dealloc_number (new_num_p); ECMA_FINALIZE (old_num_value); ECMA_FINALIZE (old_value); return ret_value; } /* opfunc_pre_decr */ /** * 'Post increment' opcode handler. * * See also: ECMA-262 v5, 11.3.1 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_post_incr (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.post_incr.dst; const idx_t incr_var_idx = opdata.data.post_incr.var_right; int_data->pos++; ecma_completion_value_t ret_value; // 1., 2., 3. ECMA_TRY_CATCH (old_value, get_variable_value (int_data, incr_var_idx, true), ret_value); ECMA_TRY_CATCH (old_num_value, ecma_op_to_number (old_value.value), ret_value); // 4. ecma_number_t* new_num_p = ecma_alloc_number (); ecma_number_t* old_num_p = (ecma_number_t*) ECMA_GET_POINTER (old_num_value.value.value); *new_num_p = ecma_op_number_add (*old_num_p, ECMA_NUMBER_ONE); // 5. ret_value = set_variable_value (int_data, incr_var_idx, ecma_make_number_value (new_num_p)); ecma_dealloc_number (new_num_p); // assignment of operator result to register variable ecma_completion_value_t reg_assignment_res = set_variable_value (int_data, dst_var_idx, old_value.value); JERRY_ASSERT (ecma_is_empty_completion_value (reg_assignment_res)); ECMA_FINALIZE (old_num_value); ECMA_FINALIZE (old_value); return ret_value; } /* opfunc_post_incr */ /** * 'Post decrement' opcode handler. * * See also: ECMA-262 v5, 11.3.2 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_post_decr (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.post_decr.dst; const idx_t decr_var_idx = opdata.data.post_decr.var_right; int_data->pos++; ecma_completion_value_t ret_value; // 1., 2., 3. ECMA_TRY_CATCH (old_value, get_variable_value (int_data, decr_var_idx, true), ret_value); ECMA_TRY_CATCH (old_num_value, ecma_op_to_number (old_value.value), ret_value); // 4. ecma_number_t* new_num_p = ecma_alloc_number (); ecma_number_t* old_num_p = (ecma_number_t*) ECMA_GET_POINTER (old_num_value.value.value); *new_num_p = ecma_op_number_substract (*old_num_p, ECMA_NUMBER_ONE); // 5. ret_value = set_variable_value (int_data, decr_var_idx, ecma_make_number_value (new_num_p)); ecma_dealloc_number (new_num_p); // assignment of operator result to register variable ecma_completion_value_t reg_assignment_res = set_variable_value (int_data, dst_var_idx, old_value.value); JERRY_ASSERT (ecma_is_empty_completion_value (reg_assignment_res)); ECMA_FINALIZE (old_num_value); ECMA_FINALIZE (old_value); return ret_value; } /* opfunc_post_decr */ /** * 'Register variable declaration' opcode handler. * * The opcode is meta-opcode that is not supposed to be executed. */ ecma_completion_value_t opfunc_reg_var_decl (opcode_t opdata __unused, /**< operation data */ int_data_t *int_data __unused) /**< interpreter context */ { JERRY_UNREACHABLE (); } /* opfunc_reg_var_decl */ /** * 'Variable declaration' opcode handler. * * See also: ECMA-262 v5, 10.5 - Declaration binding instantiation (block 8). * * @return completion value * Returned value is simple and so need not be freed. * However, ecma_free_completion_value may be called for it, but it is a no-op. */ ecma_completion_value_t opfunc_var_decl (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { ecma_string_t *var_name_string_p = ecma_new_ecma_string_from_lit_index (opdata.data.var_decl.variable_name); if (ecma_is_completion_value_normal_false (ecma_op_has_binding (int_data->lex_env_p, var_name_string_p))) { FIXME ("Pass configurableBindings that is true if and only if current code is eval code"); ecma_completion_value_t completion = ecma_op_create_mutable_binding (int_data->lex_env_p, var_name_string_p, false); JERRY_ASSERT (ecma_is_empty_completion_value (completion)); /* Skipping SetMutableBinding as we have already checked that there were not * any binding with specified name in current lexical environment * and CreateMutableBinding sets the created binding's value to undefined */ JERRY_ASSERT (ecma_is_completion_value_normal_simple_value (ecma_op_get_binding_value (int_data->lex_env_p, var_name_string_p, true), ECMA_SIMPLE_VALUE_UNDEFINED)); } ecma_deref_ecma_string (var_name_string_p); int_data->pos++; return ecma_make_empty_completion_value (); } /* opfunc_var_decl */ /** * Function declaration helper * * @return completion value * returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t function_declaration (int_data_t *int_data, /**< interpreter context */ idx_t function_name_lit_idx, /**< identifier of literal with function name */ ecma_string_t* args_names[], /**< names of arguments */ ecma_length_t args_number) /**< number of arguments */ { TODO ("Check if code of function itself is strict"); const bool is_strict = int_data->is_strict; const bool is_configurable_bindings = int_data->is_eval_code; const opcode_counter_t jmp_down_opcode_idx = (opcode_counter_t) (int_data->pos); opcode_t jmp_down_opcode = read_opcode (jmp_down_opcode_idx); JERRY_ASSERT (jmp_down_opcode.op_idx == __op__idx_jmp_down); int_data->pos = (opcode_counter_t) (jmp_down_opcode_idx + jmp_down_opcode.data.jmp_down.opcode_count); const opcode_counter_t function_code_opcode_idx = (opcode_counter_t) (jmp_down_opcode_idx + 1); ecma_string_t *function_name_string_p = ecma_new_ecma_string_from_lit_index (function_name_lit_idx); ecma_completion_value_t ret_value = ecma_op_function_declaration (int_data->lex_env_p, function_name_string_p, function_code_opcode_idx, args_names, args_number, is_strict, is_configurable_bindings); ecma_deref_ecma_string (function_name_string_p); return ret_value; } /* function_declaration */ /** * 'Function declaration with no parameters' opcode handler. * * @return completion value * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_func_decl_0 (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { int_data->pos++; return function_declaration (int_data, opdata.data.func_decl_0.name_lit_idx, NULL, 0); } /* opfunc_func_decl_0 */ /** * 'Function declaration with one parameter' opcode handler. * * @return completion value * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_func_decl_1 (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { int_data->pos++; ecma_string_t *arg_name_string_p = ecma_new_ecma_string_from_lit_index (opdata.data.func_decl_1.arg1_lit_idx); ecma_completion_value_t ret_value = function_declaration (int_data, opdata.data.func_decl_1.name_lit_idx, &arg_name_string_p, 1); ecma_deref_ecma_string (arg_name_string_p); return ret_value; } /* opfunc_func_decl_1 */ /** * 'Function declaration with two parameters' opcode handler. * * @return completion value * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_func_decl_2 (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { int_data->pos++; ecma_string_t* arg_names_strings[2] = { ecma_new_ecma_string_from_lit_index (opdata.data.func_decl_2.arg1_lit_idx), ecma_new_ecma_string_from_lit_index (opdata.data.func_decl_2.arg2_lit_idx) }; ecma_completion_value_t ret_value = function_declaration (int_data, opdata.data.func_decl_1.name_lit_idx, arg_names_strings, 2); ecma_deref_ecma_string (arg_names_strings[0]); ecma_deref_ecma_string (arg_names_strings[1]); return ret_value; } /* opfunc_func_decl_2 */ /** * 'Function declaration' opcode handler. * * @return completion value * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_func_decl_n (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { int_data->pos++; const idx_t function_name_idx = opdata.data.func_decl_n.name_lit_idx; const ecma_length_t args_number = opdata.data.func_decl_n.arg_list; ecma_string_t *arg_names[args_number + 1]; ecma_length_t arg_index = 0; opcode_t next_opcode = read_opcode (int_data->pos); while (next_opcode.op_idx == __op__idx_varg_list) { const idx_t arg1_lit_idx = next_opcode.data.varg_list.arg1_lit_idx; const idx_t arg2_lit_idx = next_opcode.data.varg_list.arg2_lit_idx; const idx_t arg3_lit_idx = next_opcode.data.varg_list.arg3_lit_idx; arg_names[arg_index++] = ecma_new_ecma_string_from_lit_index (arg1_lit_idx); if (arg_index < args_number) { arg_names[arg_index++] = ecma_new_ecma_string_from_lit_index (arg2_lit_idx); } if (arg_index < args_number) { arg_names[arg_index++] = ecma_new_ecma_string_from_lit_index (arg3_lit_idx); } JERRY_ASSERT (arg_index <= args_number); int_data->pos++; next_opcode = read_opcode (int_data->pos); } JERRY_ASSERT (arg_index == args_number); ecma_completion_value_t ret_value = function_declaration (int_data, function_name_idx, arg_names, args_number); for (arg_index = 0; arg_index < args_number; arg_index++) { ecma_deref_ecma_string (arg_names[arg_index]); } return ret_value; } /* opfunc_func_decl_n */ /** * Function call with no arguments' opcode handler. * * See also: ECMA-262 v5, 11.2.3 * * @return completion value * Returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_call_0 (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t func_name_lit_idx = opdata.data.call_0.name_lit_idx; const idx_t lhs_var_idx = opdata.data.call_0.lhs; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (func_value, get_variable_value (int_data, func_name_lit_idx, false), ret_value); if (!ecma_op_is_callable (func_value.value)) { ret_value = ecma_make_throw_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); } else { ecma_object_t *func_obj_p = ECMA_GET_POINTER (func_value.value.value); ECMA_TRY_CATCH (this_value, ecma_op_implicit_this_value (int_data->lex_env_p), ret_value); ECMA_FUNCTION_CALL (call_completion, ecma_op_function_call (func_obj_p, this_value.value, NULL, 0), ret_value); ret_value = set_variable_value (int_data, lhs_var_idx, call_completion.value); ECMA_FINALIZE (call_completion); ECMA_FINALIZE (this_value); } ECMA_FINALIZE (func_value); return ret_value; } /* opfunc_call_0 */ /** * 'Function call' opcode handler. * * See also: ECMA-262 v5, 11.2.3 * * @return completion value * Returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_call_n (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t lhs_var_idx = opdata.data.call_n.lhs; const idx_t func_name_lit_idx = opdata.data.call_n.name_lit_idx; const idx_t args_number = opdata.data.call_n.arg_list; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (func_value, get_variable_value (int_data, func_name_lit_idx, false), ret_value); ecma_completion_value_t get_arg_completion = ecma_make_empty_completion_value (); ecma_value_t arg_values[args_number + 1]; ecma_length_t arg_index = 0; opcode_t next_opcode = read_opcode (int_data->pos); while (next_opcode.op_idx == __op__idx_varg_list && ecma_is_completion_value_normal (get_arg_completion)) { const idx_t arg_lits[3] = { next_opcode.data.varg_list.arg1_lit_idx, next_opcode.data.varg_list.arg2_lit_idx, next_opcode.data.varg_list.arg3_lit_idx }; ecma_length_t local_arg_index = 0; while (local_arg_index < 3 && arg_index < args_number) { get_arg_completion = get_variable_value (int_data, arg_lits[local_arg_index++], false); if (unlikely (ecma_is_completion_value_throw (get_arg_completion))) { break; } else { JERRY_ASSERT (ecma_is_completion_value_normal (get_arg_completion)); arg_values[arg_index++] = get_arg_completion.value; } } JERRY_ASSERT (arg_index <= args_number); int_data->pos++; next_opcode = read_opcode (int_data->pos); } JERRY_ASSERT (arg_index == args_number); ecma_completion_value_t this_value; if (next_opcode.op_idx == __op__idx_meta && next_opcode.data.meta.type == OPCODE_META_TYPE_THIS_ARG) { const idx_t this_arg_var_idx = next_opcode.data.meta.data_1; JERRY_ASSERT (is_reg_variable (int_data, this_arg_var_idx)); this_value = get_variable_value (int_data, this_arg_var_idx, false); } else { this_value = ecma_op_implicit_this_value (int_data->lex_env_p); JERRY_ASSERT (ecma_is_completion_value_normal (this_value)); } if (ecma_is_completion_value_normal (get_arg_completion)) { if (!ecma_op_is_callable (func_value.value)) { ret_value = ecma_make_throw_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); } else { ecma_object_t *func_obj_p = ECMA_GET_POINTER (func_value.value.value); ECMA_FUNCTION_CALL (call_completion, ecma_op_function_call (func_obj_p, this_value.value, arg_values, args_number), ret_value); ret_value = set_variable_value (int_data, lhs_var_idx, call_completion.value); ECMA_FINALIZE (call_completion); ecma_free_completion_value (this_value); } } else { ret_value = get_arg_completion; } for (ecma_length_t arg_index = 0; arg_index < args_number; arg_index++) { ecma_free_value (arg_values[arg_index], true); } ECMA_FINALIZE (func_value); return ret_value; } /* opfunc_call_n */ /** * 'Constructor call' opcode handler. * * See also: ECMA-262 v5, 11.2.2 * * @return completion value * Returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_construct_n (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t lhs_var_idx = opdata.data.construct_n.lhs; const idx_t constructor_name_lit_idx = opdata.data.construct_n.name_lit_idx; const idx_t args_number = opdata.data.construct_n.arg_list; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (constructor_value, get_variable_value (int_data, constructor_name_lit_idx, false), ret_value); ecma_completion_value_t get_arg_completion = ecma_make_empty_completion_value (); ecma_value_t arg_values[args_number + 1]; ecma_length_t arg_index = 0; opcode_t next_opcode = read_opcode (int_data->pos); while (next_opcode.op_idx == __op__idx_varg_list && ecma_is_completion_value_normal (get_arg_completion)) { const idx_t arg_lits[3] = { next_opcode.data.varg_list.arg1_lit_idx, next_opcode.data.varg_list.arg2_lit_idx, next_opcode.data.varg_list.arg3_lit_idx }; ecma_length_t local_arg_index = 0; while (local_arg_index < 3 && arg_index < args_number) { get_arg_completion = get_variable_value (int_data, arg_lits[local_arg_index++], false); if (unlikely (ecma_is_completion_value_throw (get_arg_completion))) { break; } else { JERRY_ASSERT (ecma_is_completion_value_normal (get_arg_completion)); arg_values[arg_index++] = get_arg_completion.value; } } JERRY_ASSERT (arg_index <= args_number); int_data->pos++; next_opcode = read_opcode (int_data->pos); } JERRY_ASSERT (arg_index == args_number); ecma_completion_value_t this_value; if (next_opcode.op_idx == __op__idx_meta && next_opcode.data.meta.type == OPCODE_META_TYPE_THIS_ARG) { const idx_t this_arg_var_idx = next_opcode.data.meta.data_1; JERRY_ASSERT (is_reg_variable (int_data, this_arg_var_idx)); this_value = get_variable_value (int_data, this_arg_var_idx, false); } else { this_value = ecma_op_implicit_this_value (int_data->lex_env_p); JERRY_ASSERT (ecma_is_completion_value_normal (this_value)); } if (ecma_is_completion_value_normal (get_arg_completion)) { if (!ecma_is_constructor (constructor_value.value)) { ret_value = ecma_make_throw_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); } else { ecma_object_t *constructor_obj_p = ECMA_GET_POINTER (constructor_value.value.value); ECMA_TRY_CATCH (construction_completion, ecma_op_function_construct (constructor_obj_p, arg_values, args_number), ret_value); ret_value = set_variable_value (int_data, lhs_var_idx, construction_completion.value); ECMA_FINALIZE (construction_completion); ecma_free_completion_value (this_value); } } else { ret_value = get_arg_completion; } for (ecma_length_t arg_index = 0; arg_index < args_number; arg_index++) { ecma_free_value (arg_values[arg_index], true); } ECMA_FINALIZE (constructor_value); return ret_value; } /* opfunc_construct_n */ /** * 'Return with no expression' opcode handler. * * See also: ECMA-262 v5, 12.9 * * @return completion value * Returned value is simple and so need not be freed. * However, ecma_free_completion_value may be called for it, but it is a no-op. */ ecma_completion_value_t opfunc_ret (opcode_t opdata __unused, /**< operation data */ int_data_t *int_data __unused) /**< interpreter context */ { return ecma_make_completion_value (ECMA_COMPLETION_TYPE_RETURN, ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED), ECMA_TARGET_ID_RESERVED); } /* opfunc_ret */ /** * 'Return with expression' opcode handler. * * See also: ECMA-262 v5, 12.9 * * @return completion value * Returned value is simple and so need not be freed. * However, ecma_free_completion_value may be called for it, but it is a no-op. */ ecma_completion_value_t opfunc_retval (opcode_t opdata __unused, /**< operation data */ int_data_t *int_data __unused) /**< interpreter context */ { ecma_completion_value_t ret_value; ECMA_TRY_CATCH (expr_val, get_variable_value (int_data, opdata.data.retval.ret_value, false), ret_value); ret_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_RETURN, ecma_copy_value (expr_val.value, true), ECMA_TARGET_ID_RESERVED); ECMA_FINALIZE (expr_val); return ret_value; } /* opfunc_retval */ /** * 'Property getter' opcode handler. * * See also: ECMA-262 v5, 11.2.1 * ECMA-262 v5, 11.13.1 * * @return completion value * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_prop_getter (opcode_t opdata __unused, /**< operation data */ int_data_t *int_data __unused) /**< interpreter context */ { const idx_t lhs_var_idx = opdata.data.prop_getter.lhs; const idx_t base_var_idx = opdata.data.prop_getter.obj; const idx_t prop_name_var_idx = opdata.data.prop_getter.prop; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (base_value, get_variable_value (int_data, base_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 (check_coercible_ret, ecma_op_check_object_coercible (base_value.value), ret_value); ECMA_TRY_CATCH (prop_name_str_value, ecma_op_to_string (prop_name_value.value), ret_value); ecma_string_t *prop_name_string_p = ECMA_GET_POINTER (prop_name_str_value.value.value); ecma_reference_t ref = ecma_make_reference (base_value.value, prop_name_string_p, int_data->is_strict); ECMA_TRY_CATCH (prop_value, ecma_op_get_value (ref), ret_value); ret_value = set_variable_value (int_data, lhs_var_idx, prop_value.value); ECMA_FINALIZE (prop_value); ecma_free_reference (ref); ECMA_FINALIZE (prop_name_str_value); ECMA_FINALIZE (check_coercible_ret); ECMA_FINALIZE (prop_name_value); ECMA_FINALIZE (base_value); return ret_value; } /* opfunc_prop_getter */ /** * 'Property setter' opcode handler. * * See also: ECMA-262 v5, 11.2.1 * ECMA-262 v5, 11.13.1 * * @return completion value * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t opfunc_prop_setter (opcode_t opdata __unused, /**< operation data */ int_data_t *int_data __unused) /**< interpreter context */ { const idx_t base_var_idx = opdata.data.prop_setter.obj; const idx_t prop_name_var_idx = opdata.data.prop_setter.prop; const idx_t rhs_var_idx = opdata.data.prop_setter.rhs; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (base_value, get_variable_value (int_data, base_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 (check_coercible_ret, ecma_op_check_object_coercible (base_value.value), ret_value); ECMA_TRY_CATCH (prop_name_str_value, ecma_op_to_string (prop_name_value.value), ret_value); ecma_string_t *prop_name_string_p = ECMA_GET_POINTER (prop_name_str_value.value.value); ecma_reference_t ref = ecma_make_reference (base_value.value, prop_name_string_p, int_data->is_strict); ECMA_TRY_CATCH (rhs_value, get_variable_value (int_data, rhs_var_idx, false), ret_value); ret_value = ecma_op_put_value (ref, rhs_value.value); ECMA_FINALIZE (rhs_value); ecma_free_reference (ref); ECMA_FINALIZE (prop_name_str_value); ECMA_FINALIZE (check_coercible_ret); ECMA_FINALIZE (prop_name_value); ECMA_FINALIZE (base_value); return ret_value; } /* opfunc_prop_setter */ /** * Exit from script with specified status code: * 0 - for successful completion * 1 - to indicate failure. * * Note: this is not ECMA specification-defined, but internal * implementation-defined opcode for end of script * and assertions inside of unit tests. * * @return completion value * Returned value is simple and so need not be freed. * However, ecma_free_completion_value may be called for it, but it is a no-op. */ ecma_completion_value_t opfunc_exitval (opcode_t opdata, /**< operation data */ int_data_t *int_data __unused) /**< interpreter context */ { JERRY_ASSERT (opdata.data.exitval.status_code == 0 || opdata.data.exitval.status_code == 1); ecma_value_t exit_status = ecma_make_simple_value (opdata.data.exitval.status_code == 0 ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE); return ecma_make_completion_value (ECMA_COMPLETION_TYPE_EXIT, exit_status, ECMA_TARGET_ID_RESERVED); } /* opfunc_exitval */ /** * 'Logical NOT Operator' opcode handler. * * See also: ECMA-262 v5, 11.4.9 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_logical_not (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.logical_not.dst; const idx_t right_var_idx = opdata.data.logical_not.var_right; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (right_value, get_variable_value (int_data, right_var_idx, false), ret_value); ecma_simple_value_t old_value = ECMA_SIMPLE_VALUE_TRUE; ecma_completion_value_t to_bool_value = ecma_op_to_boolean (right_value.value); if (ecma_is_value_true (to_bool_value.value)) { old_value = ECMA_SIMPLE_VALUE_FALSE; } ret_value = set_variable_value (int_data, dst_var_idx, ecma_make_simple_value (old_value)); ECMA_FINALIZE (right_value); return ret_value; } /* opfunc_logical_not */ /** * 'Logical OR Operator' opcode handler. * * See also: ECMA-262 v5, 11.11 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_logical_or (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.logical_or.dst; const idx_t left_var_idx = opdata.data.logical_or.var_left; const idx_t right_var_idx = opdata.data.logical_or.var_right; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (left_value, get_variable_value (int_data, left_var_idx, false), ret_value); ECMA_TRY_CATCH (right_value, get_variable_value (int_data, right_var_idx, false), ret_value); ecma_completion_value_t to_bool_value = ecma_op_to_boolean (left_value.value); if (ecma_is_value_true (to_bool_value.value)) { ret_value = set_variable_value (int_data, dst_var_idx, left_value.value); } else { ret_value = set_variable_value (int_data, dst_var_idx, right_value.value); } ECMA_FINALIZE (right_value); ECMA_FINALIZE (left_value); return ret_value; } /* opfunc_logical_or */ /** * 'Logical AND Operator' opcode handler. * * See also: ECMA-262 v5, 11.11 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_logical_and (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.logical_and.dst; const idx_t left_var_idx = opdata.data.logical_and.var_left; const idx_t right_var_idx = opdata.data.logical_and.var_right; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (left_value, get_variable_value (int_data, left_var_idx, false), ret_value); ECMA_TRY_CATCH (right_value, get_variable_value (int_data, right_var_idx, false), ret_value); ecma_completion_value_t to_bool_value = ecma_op_to_boolean (left_value.value); if (ecma_is_value_true (to_bool_value.value) == false) { ret_value = set_variable_value (int_data, dst_var_idx, left_value.value); } else { ret_value = set_variable_value (int_data, dst_var_idx, right_value.value); } ECMA_FINALIZE (right_value); ECMA_FINALIZE (left_value); return ret_value; } /* opfunc_logical_and */ /** * 'This' opcode handler. * * See also: ECMA-262 v5, 11.1.1 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_this (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.this.lhs; int_data->pos++; ecma_completion_value_t ret_value; ret_value = set_variable_value (int_data, dst_var_idx, int_data->this_binding); return ret_value; } /* opfunc_this */ /** * Evaluate argument of typeof. * * See also: ECMA-262 v5, 11.4.3 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ static ecma_completion_value_t evaluate_arg_for_typeof (int_data_t *int_data, /**< interpreter context */ idx_t var_idx) /**< arg variable identifier */ { ecma_completion_value_t ret_value; if (is_reg_variable (int_data, var_idx)) { // 2.b ret_value = get_variable_value (int_data, var_idx, false); JERRY_ASSERT (ecma_is_completion_value_normal (ret_value)); } else { ecma_string_t *var_name_string_p = ecma_new_ecma_string_from_lit_index (var_idx); ecma_reference_t ref = ecma_op_get_identifier_reference (int_data->lex_env_p, var_name_string_p, int_data->is_strict); ecma_deref_ecma_string (var_name_string_p); const bool is_unresolvable_reference = ecma_is_value_undefined (ref.base); if (is_unresolvable_reference) { ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED); } else { ret_value = ecma_op_get_value (ref); } ecma_free_reference (ref); } return ret_value; } /* evaluate_arg_for_typeof */ /** * 'typeof' opcode handler. * * See also: ECMA-262 v5, 11.4.3 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t opfunc_typeof (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { const idx_t dst_var_idx = opdata.data.typeof.lhs; const idx_t obj_var_idx = opdata.data.typeof.obj; int_data->pos++; ecma_completion_value_t ret_value; ECMA_TRY_CATCH (typeof_evaluate_arg_completion, evaluate_arg_for_typeof (int_data, obj_var_idx), ret_value); ecma_value_t typeof_arg = typeof_evaluate_arg_completion.value; ecma_string_t *type_str_p = NULL; switch ((ecma_type_t)typeof_arg.value_type) { case ECMA_TYPE_SIMPLE: { switch ((ecma_simple_value_t)typeof_arg.value) { case ECMA_SIMPLE_VALUE_UNDEFINED: { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_UNDEFINED); break; } case ECMA_SIMPLE_VALUE_NULL: { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_NULL); break; } case ECMA_SIMPLE_VALUE_FALSE: { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_FALSE); break; } case ECMA_SIMPLE_VALUE_TRUE: { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_TRUE); break; } case ECMA_SIMPLE_VALUE_EMPTY: case ECMA_SIMPLE_VALUE_ARRAY_REDIRECT: case ECMA_SIMPLE_VALUE__COUNT: { JERRY_UNREACHABLE (); } } break; } case ECMA_TYPE_NUMBER: { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_NUMBER); break; } case ECMA_TYPE_STRING: { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_STRING); break; } case ECMA_TYPE_OBJECT: { if (ecma_op_is_callable (typeof_arg)) { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_FUNCTION); } else { type_str_p = ecma_get_magic_string (ECMA_MAGIC_STRING_OBJECT); } break; } } ret_value = set_variable_value (int_data, dst_var_idx, ecma_make_string_value (type_str_p)); ecma_deref_ecma_string (type_str_p); ECMA_FINALIZE (typeof_evaluate_arg_completion); return ret_value; } /* opfunc_typeof */ #define GETOP_DEF_1(a, name, field1) \ inline opcode_t getop_##name (idx_t arg1) \ { \ opcode_t opdata; \ opdata.op_idx = __op__idx_##name; \ opdata.data.name.field1 = arg1; \ return opdata; \ } #define GETOP_DEF_2(a, name, field1, field2) \ inline opcode_t getop_##name (idx_t arg1, idx_t arg2) \ { \ opcode_t opdata; \ opdata.op_idx = __op__idx_##name; \ opdata.data.name.field1 = arg1; \ opdata.data.name.field2 = arg2; \ return opdata; \ } #define GETOP_DEF_3(a, name, field1, field2, field3) \ inline opcode_t getop_##name (idx_t arg1, idx_t arg2, idx_t arg3) \ { \ opcode_t opdata; \ opdata.op_idx = __op__idx_##name; \ opdata.data.name.field1 = arg1; \ opdata.data.name.field2 = arg2; \ opdata.data.name.field3 = arg3; \ return opdata; \ } OP_ARGS_LIST (GETOP_DEF)