mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Those macros are legacy and are not used consitently throughout the code base. This patch eliminates their definitions and rewrites their remaining occurrences to TODO comments. All occurrences have been checked and made sure that the comments used a consistent style. JerryScript-DCO-1.0-Signed-off-by: Akos Kiss akiss@inf.u-szeged.hu
2585 lines
76 KiB
C
2585 lines
76 KiB
C
/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
|
|
* Copyright 2015-2016 University of Szeged.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
#include "ecma-alloc.h"
|
|
#include "ecma-array-object.h"
|
|
#include "ecma-builtins.h"
|
|
#include "ecma-conversion.h"
|
|
#include "ecma-exceptions.h"
|
|
#include "ecma-function-object.h"
|
|
#include "ecma-gc.h"
|
|
#include "ecma-helpers.h"
|
|
#include "ecma-lex-env.h"
|
|
#include "ecma-objects.h"
|
|
#include "ecma-objects-general.h"
|
|
#include "ecma-regexp-object.h"
|
|
#include "ecma-try-catch-macro.h"
|
|
#include "lit-literal-storage.h"
|
|
#include "opcodes.h"
|
|
#include "vm.h"
|
|
#include "vm-stack.h"
|
|
|
|
#include <alloca.h>
|
|
|
|
/** \addtogroup vm Virtual machine
|
|
* @{
|
|
*
|
|
* \addtogroup vm_executor Executor
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Top (current) interpreter context
|
|
*/
|
|
static vm_frame_ctx_t *vm_top_context_p = NULL;
|
|
|
|
/**
|
|
* Direct call from eval;
|
|
*/
|
|
static bool is_direct_eval_form_call = false;
|
|
|
|
/**
|
|
* Program bytecode pointer
|
|
*/
|
|
static ecma_compiled_code_t *__program = NULL;
|
|
|
|
/**
|
|
* Get the value of object[property].
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t
|
|
vm_op_get_value (ecma_value_t object, /**< base object */
|
|
ecma_value_t property, /**< property name */
|
|
bool is_strict) /**< strict mode */
|
|
{
|
|
if (unlikely (ecma_is_value_undefined (object) || ecma_is_value_null (object)))
|
|
{
|
|
return ecma_raise_type_error (ECMA_ERR_MSG (""));
|
|
}
|
|
|
|
ecma_value_t completion_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);
|
|
|
|
ECMA_TRY_CATCH (property_val,
|
|
ecma_op_to_string (property),
|
|
completion_value);
|
|
|
|
ecma_string_t *property_p = ecma_get_string_from_value (property_val);
|
|
|
|
ecma_reference_t reference = ecma_make_reference (object, property_p, is_strict);
|
|
|
|
completion_value = ecma_op_get_value_object_base (reference);
|
|
|
|
ecma_free_reference (reference);
|
|
|
|
ECMA_FINALIZE (property_val);
|
|
|
|
return completion_value;
|
|
} /* vm_op_get_value */
|
|
|
|
/**
|
|
* Set the value of object[property].
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t
|
|
vm_op_set_value (ecma_value_t object, /**< base object */
|
|
ecma_value_t property, /**< property name */
|
|
ecma_value_t value, /**< ecma value */
|
|
bool is_strict) /**< strict mode */
|
|
{
|
|
ecma_value_t completion_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);
|
|
|
|
ECMA_TRY_CATCH (obj_val,
|
|
ecma_op_to_object (object),
|
|
completion_value);
|
|
|
|
ECMA_TRY_CATCH (property_val,
|
|
ecma_op_to_string (property),
|
|
completion_value);
|
|
|
|
ecma_object_t *object_p = ecma_get_object_from_value (obj_val);
|
|
ecma_string_t *property_p = ecma_get_string_from_value (property_val);
|
|
|
|
if (ecma_is_lexical_environment (object_p))
|
|
{
|
|
completion_value = ecma_op_put_value_lex_env_base (object_p,
|
|
property_p,
|
|
is_strict,
|
|
value);
|
|
}
|
|
else
|
|
{
|
|
completion_value = ecma_op_object_put (object_p,
|
|
property_p,
|
|
value,
|
|
is_strict);
|
|
}
|
|
|
|
ECMA_FINALIZE (property_val);
|
|
ECMA_FINALIZE (obj_val);
|
|
|
|
return completion_value;
|
|
} /* vm_op_set_value */
|
|
|
|
/**
|
|
* Initialize interpreter.
|
|
*/
|
|
void
|
|
vm_init (ecma_compiled_code_t *program_p) /**< pointer to byte-code data */
|
|
{
|
|
JERRY_ASSERT (__program == NULL);
|
|
|
|
__program = program_p;
|
|
} /* vm_init */
|
|
|
|
#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg4,
|
|
|
|
/**
|
|
* Decode table for opcodes.
|
|
*/
|
|
static const uint32_t vm_decode_table[] =
|
|
{
|
|
CBC_OPCODE_LIST
|
|
};
|
|
|
|
/**
|
|
* Decode table for extended opcodes.
|
|
*/
|
|
static const uint32_t vm_ext_decode_table[] =
|
|
{
|
|
CBC_EXT_OPCODE_LIST
|
|
};
|
|
|
|
#undef CBC_OPCODE
|
|
|
|
/**
|
|
* Run global code
|
|
*
|
|
* @return completion code
|
|
*/
|
|
jerry_completion_code_t
|
|
vm_run_global (ecma_object_t **error_obj_p)
|
|
{
|
|
jerry_completion_code_t ret_code;
|
|
|
|
JERRY_ASSERT (__program != NULL);
|
|
|
|
ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL);
|
|
ecma_object_t *lex_env_p = ecma_get_global_environment ();
|
|
|
|
ecma_value_t ret_value = vm_run (__program,
|
|
ecma_make_object_value (glob_obj_p),
|
|
lex_env_p,
|
|
false,
|
|
NULL,
|
|
0);
|
|
|
|
if (ecma_is_value_error (ret_value))
|
|
{
|
|
*error_obj_p = ecma_get_object_from_value (ret_value);
|
|
ret_code = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION;
|
|
}
|
|
else
|
|
{
|
|
ecma_free_value (ret_value);
|
|
ret_code = JERRY_COMPLETION_CODE_OK;
|
|
}
|
|
|
|
ecma_deref_object (glob_obj_p);
|
|
ecma_deref_object (lex_env_p);
|
|
|
|
return ret_code;
|
|
} /* vm_run_global */
|
|
|
|
/**
|
|
* Run specified eval-mode bytecode
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
ecma_value_t
|
|
vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */
|
|
bool is_direct) /**< is eval called in direct mode? */
|
|
{
|
|
bool is_strict = ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0);
|
|
|
|
ecma_value_t this_binding;
|
|
ecma_object_t *lex_env_p;
|
|
|
|
/* ECMA-262 v5, 10.4.2 */
|
|
if (is_direct)
|
|
{
|
|
this_binding = ecma_copy_value (vm_top_context_p->this_binding);
|
|
lex_env_p = vm_top_context_p->lex_env_p;
|
|
ecma_ref_object (vm_top_context_p->lex_env_p);
|
|
}
|
|
else
|
|
{
|
|
this_binding = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL));
|
|
lex_env_p = ecma_get_global_environment ();
|
|
}
|
|
|
|
if (is_strict)
|
|
{
|
|
ecma_object_t *strict_lex_env_p = ecma_create_decl_lex_env (lex_env_p);
|
|
ecma_deref_object (lex_env_p);
|
|
|
|
lex_env_p = strict_lex_env_p;
|
|
}
|
|
|
|
ecma_value_t completion_value = vm_run (bytecode_data_p,
|
|
this_binding,
|
|
lex_env_p,
|
|
true,
|
|
NULL,
|
|
0);
|
|
|
|
ecma_deref_object (lex_env_p);
|
|
ecma_free_value (this_binding);
|
|
ecma_bytecode_deref (bytecode_data_p);
|
|
|
|
return completion_value;
|
|
} /* vm_run_eval */
|
|
|
|
/**
|
|
* Construct object
|
|
*
|
|
* @return object value
|
|
*/
|
|
static ecma_value_t
|
|
vm_construct_literal_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|
lit_cpointer_t lit_cp) /**< literal */
|
|
{
|
|
ecma_compiled_code_t *bytecode_p = ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t,
|
|
lit_cp);
|
|
bool is_function = ((bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION) != 0);
|
|
|
|
if (is_function)
|
|
{
|
|
bool is_strict = ((frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0);
|
|
|
|
ecma_object_t *func_obj_p = ecma_op_create_function_object (frame_ctx_p->lex_env_p,
|
|
is_strict,
|
|
bytecode_p);
|
|
|
|
return ecma_make_object_value (func_obj_p);
|
|
}
|
|
else
|
|
{
|
|
#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN
|
|
ecma_value_t ret_value;
|
|
ret_value = ecma_op_create_regexp_object_from_bytecode ((re_compiled_code_t *) bytecode_p);
|
|
|
|
if (ecma_is_value_error (ret_value))
|
|
{
|
|
/* TODO: throw exception instead of define an 'undefined' value. */
|
|
return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
|
|
return ret_value;
|
|
#else /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */
|
|
JERRY_UNIMPLEMENTED ("Regular Expressions are not supported in compact profile!");
|
|
#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */
|
|
}
|
|
} /* vm_construct_literal_object */
|
|
|
|
/**
|
|
* Get implicit this value
|
|
*
|
|
* @return true, if the implicit 'this' value is updated,
|
|
* false - otherwise.
|
|
*/
|
|
static inline bool __attr_always_inline___
|
|
vm_get_implicit_this_value (ecma_value_t *this_value_p) /**< [in,out] this value */
|
|
{
|
|
if (ecma_is_value_object (*this_value_p))
|
|
{
|
|
ecma_object_t *this_obj_p = ecma_get_object_from_value (*this_value_p);
|
|
|
|
if (ecma_is_lexical_environment (this_obj_p))
|
|
{
|
|
ecma_value_t completion_value = ecma_op_implicit_this_value (this_obj_p);
|
|
|
|
JERRY_ASSERT (!ecma_is_value_error (completion_value));
|
|
|
|
*this_value_p = completion_value;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} /* vm_get_implicit_this_value */
|
|
|
|
/**
|
|
* 'Function call' opcode handler.
|
|
*
|
|
* See also: ECMA-262 v5, 11.2.3
|
|
*/
|
|
static void
|
|
opfunc_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
|
{
|
|
uint8_t opcode = frame_ctx_p->byte_code_p[0];
|
|
uint32_t arguments_list_len;
|
|
|
|
if (opcode >= CBC_CALL0)
|
|
{
|
|
arguments_list_len = (unsigned int) ((opcode - CBC_CALL0) / 6);
|
|
}
|
|
else
|
|
{
|
|
arguments_list_len = frame_ctx_p->byte_code_p[1];
|
|
}
|
|
|
|
bool is_call_prop = ((opcode - CBC_CALL) % 6) >= 3;
|
|
|
|
ecma_value_t this_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
ecma_value_t *stack_top_p = frame_ctx_p->stack_top_p - arguments_list_len;
|
|
|
|
if (is_call_prop)
|
|
{
|
|
this_value = stack_top_p[-3];
|
|
|
|
if (vm_get_implicit_this_value (&this_value))
|
|
{
|
|
ecma_free_value (stack_top_p[-3]);
|
|
stack_top_p[-3] = this_value;
|
|
}
|
|
}
|
|
|
|
ecma_value_t func_value = stack_top_p[-1];
|
|
ecma_value_t completion_value;
|
|
|
|
if (!ecma_op_is_callable (func_value))
|
|
{
|
|
completion_value = ecma_raise_type_error (ECMA_ERR_MSG (""));
|
|
}
|
|
else
|
|
{
|
|
ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value);
|
|
|
|
completion_value = ecma_op_function_call (func_obj_p,
|
|
this_value,
|
|
stack_top_p,
|
|
arguments_list_len);
|
|
}
|
|
|
|
is_direct_eval_form_call = false;
|
|
|
|
/* Free registers. */
|
|
for (uint32_t i = 0; i < arguments_list_len; i++)
|
|
{
|
|
ecma_free_value (stack_top_p[i]);
|
|
}
|
|
|
|
if (is_call_prop)
|
|
{
|
|
ecma_free_value (*(--stack_top_p));
|
|
ecma_free_value (*(--stack_top_p));
|
|
}
|
|
|
|
ecma_free_value (stack_top_p[-1]);
|
|
stack_top_p[-1] = completion_value;
|
|
|
|
frame_ctx_p->stack_top_p = stack_top_p;
|
|
} /* opfunc_call */
|
|
|
|
/**
|
|
* 'Constructor call' opcode handler.
|
|
*
|
|
* See also: ECMA-262 v5, 11.2.2
|
|
*/
|
|
static void
|
|
opfunc_construct (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
|
{
|
|
uint8_t opcode = frame_ctx_p->byte_code_p[0];
|
|
unsigned int arguments_list_len;
|
|
|
|
if (opcode >= CBC_NEW0)
|
|
{
|
|
arguments_list_len = (unsigned int) (opcode - CBC_NEW0);
|
|
}
|
|
else
|
|
{
|
|
arguments_list_len = frame_ctx_p->byte_code_p[1];
|
|
}
|
|
|
|
ecma_value_t *stack_top_p = frame_ctx_p->stack_top_p - arguments_list_len;
|
|
ecma_value_t constructor_value = stack_top_p[-1];
|
|
ecma_value_t completion_value;
|
|
|
|
if (!ecma_is_constructor (constructor_value))
|
|
{
|
|
completion_value = ecma_raise_type_error (ECMA_ERR_MSG (""));
|
|
}
|
|
else
|
|
{
|
|
ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor_value);
|
|
|
|
completion_value = ecma_op_function_construct (constructor_obj_p,
|
|
stack_top_p,
|
|
arguments_list_len);
|
|
}
|
|
|
|
/* Free registers. */
|
|
for (uint32_t i = 0; i < arguments_list_len; i++)
|
|
{
|
|
ecma_free_value (stack_top_p[i]);
|
|
}
|
|
|
|
ecma_free_value (stack_top_p[-1]);
|
|
stack_top_p[-1] = completion_value;
|
|
|
|
frame_ctx_p->stack_top_p = stack_top_p;
|
|
} /* opfunc_construct */
|
|
|
|
/**
|
|
* Indicate which value should be freed.
|
|
*/
|
|
enum
|
|
{
|
|
VM_FREE_LEFT_VALUE = 0x1,
|
|
VM_FREE_RIGHT_VALUE = 0x2,
|
|
};
|
|
|
|
#define READ_LITERAL_INDEX(destination) \
|
|
do \
|
|
{ \
|
|
(destination) = *byte_code_p++; \
|
|
if ((destination) >= encoding_limit) \
|
|
{ \
|
|
(destination) = (uint16_t) ((((destination) << 8) | *byte_code_p++) - encoding_delta); \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
/* TODO: For performance reasons, we define this as a macro.
|
|
* When we are able to construct a function with similar speed,
|
|
* we can remove this macro. */
|
|
#define READ_LITERAL(literal_index, target_value, target_free_op) \
|
|
do \
|
|
{ \
|
|
if ((literal_index) < ident_end) \
|
|
{ \
|
|
if ((literal_index) < register_end) \
|
|
{ \
|
|
/* Note: There should be no specialization for arguments. */ \
|
|
(target_value) = ecma_copy_value (frame_ctx_p->registers_p[literal_index]); \
|
|
target_free_op; \
|
|
} \
|
|
else \
|
|
{ \
|
|
ecma_string_t *name_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]); \
|
|
ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, \
|
|
name_p); \
|
|
if (ref_base_lex_env_p != NULL) \
|
|
{ \
|
|
last_completion_value = ecma_op_get_value_lex_env_base (ref_base_lex_env_p, \
|
|
name_p, \
|
|
is_strict); \
|
|
} \
|
|
else \
|
|
{ \
|
|
last_completion_value = ecma_raise_reference_error (ECMA_ERR_MSG ("")); \
|
|
} \
|
|
\
|
|
ecma_deref_ecma_string (name_p); \
|
|
\
|
|
if (ecma_is_value_error (last_completion_value)) \
|
|
{ \
|
|
goto error; \
|
|
} \
|
|
(target_value) = last_completion_value; \
|
|
target_free_op; \
|
|
} \
|
|
} \
|
|
else if (literal_index < const_literal_end) \
|
|
{ \
|
|
lit_cpointer_t lit_cpointer = literal_start_p[literal_index]; \
|
|
lit_literal_t lit = lit_cpointer_decompress (lit_cpointer); \
|
|
if (unlikely (LIT_RECORD_IS_NUMBER (lit))) \
|
|
{ \
|
|
ecma_number_t *number_p = ecma_alloc_number (); \
|
|
*number_p = lit_number_literal_get_number (lit); \
|
|
(target_value) = ecma_make_number_value (number_p); \
|
|
} \
|
|
else \
|
|
{ \
|
|
ecma_string_t *string_p = ecma_new_ecma_string_from_lit_cp (lit_cpointer); \
|
|
(target_value) = ecma_make_string_value (string_p); \
|
|
} \
|
|
target_free_op; \
|
|
} \
|
|
else \
|
|
{ \
|
|
/* Object construction. */ \
|
|
(target_value) = vm_construct_literal_object (frame_ctx_p, literal_start_p[literal_index]); \
|
|
target_free_op; \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
/**
|
|
* Cleanup interpreter
|
|
*/
|
|
void
|
|
vm_finalize (void)
|
|
{
|
|
if (__program)
|
|
{
|
|
ecma_bytecode_deref (__program);
|
|
}
|
|
|
|
__program = NULL;
|
|
} /* vm_finalize */
|
|
|
|
/**
|
|
* Run initializer byte codes.
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t
|
|
vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
|
{
|
|
const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p;
|
|
uint8_t *byte_code_p = frame_ctx_p->byte_code_p;
|
|
ecma_value_t last_completion_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);
|
|
uint16_t encoding_limit;
|
|
uint16_t encoding_delta;
|
|
uint16_t register_end;
|
|
lit_cpointer_t *literal_start_p = frame_ctx_p->literal_start_p;
|
|
bool is_strict = ((frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0);
|
|
|
|
/* Prepare. */
|
|
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING))
|
|
{
|
|
encoding_limit = 255;
|
|
encoding_delta = 0xfe01;
|
|
}
|
|
else
|
|
{
|
|
encoding_limit = 128;
|
|
encoding_delta = 0x8000;
|
|
}
|
|
|
|
if (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) (frame_ctx_p->bytecode_header_p);
|
|
register_end = args_p->register_end;
|
|
}
|
|
else
|
|
{
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) (frame_ctx_p->bytecode_header_p);
|
|
register_end = args_p->register_end;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
switch (*byte_code_p)
|
|
{
|
|
case CBC_DEFINE_VARS:
|
|
{
|
|
uint32_t literal_index_end;
|
|
uint32_t literal_index = register_end;
|
|
|
|
byte_code_p++;
|
|
READ_LITERAL_INDEX (literal_index_end);
|
|
|
|
while (literal_index <= literal_index_end)
|
|
{
|
|
ecma_string_t *name_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]);
|
|
vm_var_decl (frame_ctx_p, name_p);
|
|
ecma_deref_ecma_string (name_p);
|
|
literal_index++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CBC_INITIALIZE_VAR:
|
|
case CBC_INITIALIZE_VARS:
|
|
{
|
|
uint8_t type = *byte_code_p;
|
|
uint32_t literal_index;
|
|
uint32_t literal_index_end;
|
|
|
|
byte_code_p++;
|
|
READ_LITERAL_INDEX (literal_index);
|
|
|
|
if (type == CBC_INITIALIZE_VAR)
|
|
{
|
|
literal_index_end = literal_index;
|
|
}
|
|
else
|
|
{
|
|
READ_LITERAL_INDEX (literal_index_end);
|
|
}
|
|
|
|
while (literal_index <= literal_index_end)
|
|
{
|
|
uint32_t value_index;
|
|
ecma_value_t lit_value;
|
|
ecma_string_t *name_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]);
|
|
|
|
vm_var_decl (frame_ctx_p, name_p);
|
|
|
|
READ_LITERAL_INDEX (value_index);
|
|
|
|
ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, name_p);
|
|
|
|
if (value_index < register_end)
|
|
{
|
|
lit_value = frame_ctx_p->registers_p[value_index];
|
|
}
|
|
else
|
|
{
|
|
lit_value = vm_construct_literal_object (frame_ctx_p,
|
|
literal_start_p[value_index]);
|
|
}
|
|
|
|
/* TODO: check the return value */
|
|
ecma_op_put_value_lex_env_base (ref_base_lex_env_p,
|
|
name_p,
|
|
is_strict,
|
|
lit_value);
|
|
|
|
if (value_index >= register_end)
|
|
{
|
|
ecma_free_value (lit_value);
|
|
}
|
|
|
|
ecma_deref_ecma_string (name_p);
|
|
literal_index++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef JERRY_ENABLE_SNAPSHOT_EXEC
|
|
case CBC_SET_BYTECODE_PTR:
|
|
{
|
|
memcpy (&byte_code_p, byte_code_p + 1, sizeof (uint8_t *));
|
|
frame_ctx_p->byte_code_start_p = byte_code_p;
|
|
break;
|
|
}
|
|
#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */
|
|
|
|
default:
|
|
{
|
|
frame_ctx_p->byte_code_p = byte_code_p;
|
|
return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return last_completion_value;
|
|
} /* vm_init_loop */
|
|
|
|
/**
|
|
* Run generic byte code.
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t __attr_noinline___
|
|
vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
|
{
|
|
const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p;
|
|
uint8_t *byte_code_p = frame_ctx_p->byte_code_p;
|
|
lit_cpointer_t *literal_start_p = frame_ctx_p->literal_start_p;
|
|
ecma_value_t last_completion_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);
|
|
|
|
ecma_value_t *stack_top_p;
|
|
uint16_t encoding_limit;
|
|
uint16_t encoding_delta;
|
|
uint16_t register_end;
|
|
uint16_t ident_end;
|
|
uint16_t const_literal_end;
|
|
int32_t branch_offset = 0;
|
|
ecma_value_t left_value = 0;
|
|
ecma_value_t right_value = 0;
|
|
ecma_value_t result = 0;
|
|
ecma_value_t block_result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
uint8_t free_flags = 0;
|
|
bool is_strict = ((frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0);
|
|
|
|
/* Prepare for byte code execution. */
|
|
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING))
|
|
{
|
|
encoding_limit = 255;
|
|
encoding_delta = 0xfe01;
|
|
}
|
|
else
|
|
{
|
|
encoding_limit = 128;
|
|
encoding_delta = 0x8000;
|
|
}
|
|
|
|
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) (bytecode_header_p);
|
|
register_end = args_p->register_end;
|
|
ident_end = args_p->ident_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
}
|
|
else
|
|
{
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) (bytecode_header_p);
|
|
register_end = args_p->register_end;
|
|
ident_end = args_p->ident_end;
|
|
const_literal_end = args_p->const_literal_end;
|
|
}
|
|
|
|
stack_top_p = frame_ctx_p->stack_top_p;
|
|
|
|
/* Outer loop for exception handling. */
|
|
while (true)
|
|
{
|
|
/* Internal loop for byte code execution. */
|
|
while (true)
|
|
{
|
|
uint8_t *byte_code_start_p = byte_code_p;
|
|
uint8_t opcode;
|
|
uint8_t opcode_flags;
|
|
uint32_t opcode_data;
|
|
|
|
opcode = *byte_code_p++;
|
|
if (opcode == CBC_EXT_OPCODE)
|
|
{
|
|
opcode = *byte_code_p++;
|
|
opcode_flags = cbc_ext_flags[opcode];
|
|
opcode_data = vm_ext_decode_table[opcode];
|
|
}
|
|
else
|
|
{
|
|
opcode_flags = cbc_flags[opcode];
|
|
opcode_data = vm_decode_table[opcode];
|
|
}
|
|
|
|
if (opcode_flags & CBC_HAS_BRANCH_ARG)
|
|
{
|
|
branch_offset = 0;
|
|
switch (CBC_BRANCH_OFFSET_LENGTH (opcode))
|
|
{
|
|
case 3:
|
|
{
|
|
branch_offset = *(byte_code_p++);
|
|
/* FALLTHRU */
|
|
}
|
|
case 2:
|
|
{
|
|
branch_offset <<= 8;
|
|
branch_offset |= *(byte_code_p++);
|
|
/* FALLTHRU */
|
|
}
|
|
default:
|
|
{
|
|
JERRY_ASSERT (CBC_BRANCH_OFFSET_LENGTH (opcode) > 0);
|
|
branch_offset <<= 8;
|
|
branch_offset |= *(byte_code_p++);
|
|
break;
|
|
}
|
|
}
|
|
if (CBC_BRANCH_IS_BACKWARD (opcode_flags))
|
|
{
|
|
branch_offset = -branch_offset;
|
|
}
|
|
}
|
|
|
|
free_flags = 0;
|
|
if (opcode_data & (VM_OC_GET_DATA_MASK << VM_OC_GET_DATA_SHIFT))
|
|
{
|
|
uint32_t operands = VM_OC_GET_DATA_GET_ID (opcode_data);
|
|
|
|
if (operands >= VM_OC_GET_DATA_GET_ID (VM_OC_GET_LITERAL))
|
|
{
|
|
uint16_t literal_index;
|
|
READ_LITERAL_INDEX (literal_index);
|
|
READ_LITERAL (literal_index,
|
|
left_value,
|
|
free_flags = VM_FREE_LEFT_VALUE);
|
|
|
|
switch (operands)
|
|
{
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_STACK_LITERAL):
|
|
{
|
|
JERRY_ASSERT (stack_top_p > frame_ctx_p->registers_p + register_end);
|
|
right_value = left_value;
|
|
left_value = *(--stack_top_p);
|
|
free_flags = (uint8_t) ((free_flags << 1) | VM_FREE_LEFT_VALUE);
|
|
break;
|
|
}
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_LITERAL_BYTE):
|
|
{
|
|
right_value = *(byte_code_p++);
|
|
break;
|
|
}
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_LITERAL_LITERAL):
|
|
{
|
|
uint16_t literal_index;
|
|
READ_LITERAL_INDEX (literal_index);
|
|
READ_LITERAL (literal_index,
|
|
right_value,
|
|
free_flags |= VM_FREE_RIGHT_VALUE);
|
|
break;
|
|
}
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_THIS_LITERAL):
|
|
{
|
|
right_value = left_value;
|
|
left_value = ecma_copy_value (frame_ctx_p->this_binding);
|
|
free_flags = (uint8_t) ((free_flags << 1) | VM_FREE_LEFT_VALUE);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_ASSERT (operands == VM_OC_GET_DATA_GET_ID (VM_OC_GET_LITERAL));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (operands)
|
|
{
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_STACK):
|
|
{
|
|
JERRY_ASSERT (stack_top_p > frame_ctx_p->registers_p + register_end);
|
|
left_value = *(--stack_top_p);
|
|
free_flags = VM_FREE_LEFT_VALUE;
|
|
break;
|
|
}
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_STACK_STACK):
|
|
{
|
|
JERRY_ASSERT (stack_top_p > frame_ctx_p->registers_p + register_end + 1);
|
|
right_value = *(--stack_top_p);
|
|
left_value = *(--stack_top_p);
|
|
free_flags = VM_FREE_LEFT_VALUE | VM_FREE_RIGHT_VALUE;
|
|
break;
|
|
}
|
|
case VM_OC_GET_DATA_GET_ID (VM_OC_GET_BYTE):
|
|
{
|
|
right_value = *(byte_code_p++);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_UNREACHABLE ();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (VM_OC_GROUP_GET_INDEX (opcode_data))
|
|
{
|
|
case VM_OC_NONE:
|
|
{
|
|
JERRY_ASSERT (opcode == CBC_EXT_DEBUGGER);
|
|
break;
|
|
}
|
|
case VM_OC_POP:
|
|
{
|
|
JERRY_ASSERT (stack_top_p > frame_ctx_p->registers_p + register_end);
|
|
ecma_free_value (*(--stack_top_p));
|
|
break;
|
|
}
|
|
case VM_OC_POP_BLOCK:
|
|
{
|
|
result = *(--stack_top_p);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH:
|
|
{
|
|
*(stack_top_p++) = left_value;
|
|
continue;
|
|
}
|
|
case VM_OC_PUSH_TWO:
|
|
{
|
|
*(stack_top_p++) = left_value;
|
|
*(stack_top_p++) = right_value;
|
|
continue;
|
|
}
|
|
case VM_OC_PUSH_THREE:
|
|
{
|
|
uint16_t literal_index;
|
|
|
|
*(stack_top_p++) = left_value;
|
|
*(stack_top_p++) = right_value;
|
|
free_flags = 0;
|
|
|
|
READ_LITERAL_INDEX (literal_index);
|
|
READ_LITERAL (literal_index,
|
|
left_value,
|
|
(void) 0);
|
|
*(stack_top_p++) = left_value;
|
|
continue;
|
|
}
|
|
case VM_OC_PUSH_UNDEFINED:
|
|
case VM_OC_VOID:
|
|
{
|
|
result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_TRUE:
|
|
{
|
|
result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_TRUE);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_FALSE:
|
|
{
|
|
result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_FALSE);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_NULL:
|
|
{
|
|
result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_THIS:
|
|
{
|
|
result = ecma_copy_value (frame_ctx_p->this_binding);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_NUMBER:
|
|
{
|
|
ecma_number_t *number_p = ecma_alloc_number ();
|
|
|
|
if (opcode == CBC_PUSH_NUMBER_0)
|
|
{
|
|
*number_p = 0;
|
|
}
|
|
else
|
|
{
|
|
int value = *byte_code_p++;
|
|
|
|
JERRY_ASSERT (opcode == CBC_PUSH_NUMBER_1);
|
|
|
|
if (value >= CBC_PUSH_NUMBER_1_RANGE_END)
|
|
{
|
|
value = -(value - CBC_PUSH_NUMBER_1_RANGE_END);
|
|
}
|
|
*number_p = (ecma_number_t) value;
|
|
}
|
|
|
|
result = ecma_make_number_value (number_p);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_OBJECT:
|
|
{
|
|
ecma_object_t *prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE);
|
|
ecma_object_t *obj_p = ecma_create_object (prototype_p,
|
|
true,
|
|
ECMA_OBJECT_TYPE_GENERAL);
|
|
result = ecma_make_object_value (obj_p);
|
|
ecma_deref_object (prototype_p);
|
|
break;
|
|
}
|
|
case VM_OC_SET_PROPERTY:
|
|
{
|
|
ecma_object_t *object_p = ecma_get_object_from_value (stack_top_p[-1]);
|
|
ecma_string_t *prop_name_p;
|
|
ecma_property_t *property_p;
|
|
|
|
if (ecma_is_value_string (right_value))
|
|
{
|
|
prop_name_p = ecma_get_string_from_value (right_value);
|
|
property_p = ecma_find_named_property (object_p, prop_name_p);
|
|
}
|
|
else
|
|
{
|
|
last_completion_value = ecma_op_to_string (right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
prop_name_p = ecma_get_string_from_value (last_completion_value);
|
|
property_p = ecma_find_named_property (object_p, prop_name_p);
|
|
}
|
|
|
|
if (property_p != NULL && !(property_p->flags & ECMA_PROPERTY_FLAG_NAMEDDATA))
|
|
{
|
|
ecma_delete_property (object_p, property_p);
|
|
property_p = NULL;
|
|
}
|
|
|
|
if (property_p == NULL)
|
|
{
|
|
property_p = ecma_create_named_data_property (object_p,
|
|
prop_name_p,
|
|
true,
|
|
true,
|
|
true);
|
|
}
|
|
|
|
ecma_named_data_property_assign_value (object_p, property_p, left_value);
|
|
|
|
if (!ecma_is_value_string (right_value))
|
|
{
|
|
ecma_deref_ecma_string (prop_name_p);
|
|
}
|
|
break;
|
|
}
|
|
case VM_OC_SET_GETTER:
|
|
case VM_OC_SET_SETTER:
|
|
{
|
|
opfunc_set_accessor (VM_OC_GROUP_GET_INDEX (opcode_data) == VM_OC_SET_GETTER ? true : false,
|
|
stack_top_p[-1],
|
|
left_value,
|
|
right_value);
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_ARRAY:
|
|
{
|
|
last_completion_value = ecma_op_create_array_object (NULL, 0, false);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_ELISON:
|
|
{
|
|
result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_ARRAY_HOLE);
|
|
break;
|
|
}
|
|
case VM_OC_APPEND_ARRAY:
|
|
{
|
|
ecma_object_t *array_obj_p;
|
|
ecma_string_t *length_str_p;
|
|
ecma_property_t *length_prop_p;
|
|
ecma_number_t *length_num_p;
|
|
ecma_property_descriptor_t prop_desc;
|
|
|
|
prop_desc = ecma_make_empty_property_descriptor ();
|
|
prop_desc.is_value_defined = true;
|
|
|
|
prop_desc.is_writable_defined = true;
|
|
prop_desc.is_writable = true;
|
|
|
|
prop_desc.is_enumerable_defined = true;
|
|
prop_desc.is_enumerable = true;
|
|
|
|
prop_desc.is_configurable_defined = true;
|
|
prop_desc.is_configurable = true;
|
|
|
|
stack_top_p -= right_value;
|
|
|
|
array_obj_p = ecma_get_object_from_value (stack_top_p[-1]);
|
|
length_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
|
|
length_prop_p = ecma_get_named_property (array_obj_p, length_str_p);
|
|
|
|
JERRY_ASSERT (length_prop_p != NULL);
|
|
|
|
left_value = ecma_get_named_data_property_value (length_prop_p);
|
|
length_num_p = ecma_get_number_from_value (left_value);
|
|
|
|
ecma_deref_ecma_string (length_str_p);
|
|
|
|
for (uint32_t i = 0; i < right_value; i++)
|
|
{
|
|
if (!ecma_is_value_array_hole (stack_top_p[i]))
|
|
{
|
|
ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 ((uint32_t) *length_num_p);
|
|
|
|
prop_desc.value = stack_top_p[i];
|
|
|
|
ecma_op_general_object_define_own_property (array_obj_p,
|
|
index_str_p,
|
|
&prop_desc,
|
|
false);
|
|
|
|
ecma_deref_ecma_string (index_str_p);
|
|
|
|
ecma_free_value (stack_top_p[i]);
|
|
}
|
|
|
|
(*length_num_p)++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VM_OC_PUSH_UNDEFINED_BASE:
|
|
{
|
|
result = stack_top_p[-1];
|
|
stack_top_p[-1] = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
break;
|
|
}
|
|
case VM_OC_IDENT_REFERENCE:
|
|
{
|
|
uint16_t literal_index;
|
|
|
|
READ_LITERAL_INDEX (literal_index);
|
|
|
|
JERRY_ASSERT (literal_index < ident_end);
|
|
|
|
if (literal_index < register_end)
|
|
{
|
|
*stack_top_p++ = ecma_make_simple_value (ECMA_SIMPLE_VALUE_REGISTER_REF);
|
|
*stack_top_p++ = literal_index;
|
|
result = ecma_copy_value (frame_ctx_p->registers_p[literal_index]);
|
|
}
|
|
else
|
|
{
|
|
ecma_string_t *name_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]);
|
|
ecma_object_t *ref_base_lex_env_p;
|
|
|
|
ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p,
|
|
name_p);
|
|
|
|
if (ref_base_lex_env_p != NULL)
|
|
{
|
|
last_completion_value = ecma_op_get_value_lex_env_base (ref_base_lex_env_p,
|
|
name_p,
|
|
is_strict);
|
|
}
|
|
else
|
|
{
|
|
last_completion_value = ecma_raise_reference_error (ECMA_ERR_MSG (""));
|
|
}
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
ecma_deref_ecma_string (name_p);
|
|
goto error;
|
|
}
|
|
|
|
ecma_ref_object (ref_base_lex_env_p);
|
|
*stack_top_p++ = ecma_make_object_value (ref_base_lex_env_p);
|
|
*stack_top_p++ = ecma_make_string_value (name_p);
|
|
|
|
result = last_completion_value;
|
|
}
|
|
break;
|
|
}
|
|
case VM_OC_PROP_REFERENCE:
|
|
{
|
|
/* Forms with reference requires preserving the base and offset. */
|
|
|
|
if (opcode == CBC_PUSH_PROP_REFERENCE)
|
|
{
|
|
left_value = stack_top_p[-2];
|
|
right_value = stack_top_p[-1];
|
|
}
|
|
else if (opcode == CBC_PUSH_PROP_LITERAL_REFERENCE)
|
|
{
|
|
*stack_top_p++ = left_value;
|
|
right_value = left_value;
|
|
left_value = stack_top_p[-2];
|
|
free_flags = 0;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (opcode == CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE
|
|
|| opcode == CBC_PUSH_PROP_THIS_LITERAL_REFERENCE);
|
|
*stack_top_p++ = ecma_copy_value (left_value);
|
|
*stack_top_p++ = ecma_copy_value (right_value);
|
|
}
|
|
/* FALLTHRU */
|
|
}
|
|
case VM_OC_PROP_GET:
|
|
case VM_OC_PROP_PRE_INCR:
|
|
case VM_OC_PROP_PRE_DECR:
|
|
case VM_OC_PROP_POST_INCR:
|
|
case VM_OC_PROP_POST_DECR:
|
|
{
|
|
last_completion_value = vm_op_get_value (left_value,
|
|
right_value,
|
|
is_strict);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
|
|
if (opcode < CBC_PRE_INCR)
|
|
{
|
|
break;
|
|
}
|
|
|
|
stack_top_p += 2;
|
|
left_value = result;
|
|
free_flags = VM_FREE_LEFT_VALUE;
|
|
/* FALLTHRU */
|
|
}
|
|
case VM_OC_PRE_INCR:
|
|
case VM_OC_PRE_DECR:
|
|
case VM_OC_POST_INCR:
|
|
case VM_OC_POST_DECR:
|
|
{
|
|
uint32_t base = VM_OC_GROUP_GET_INDEX (opcode_data) - VM_OC_PROP_PRE_INCR;
|
|
ecma_number_t increase = ECMA_NUMBER_ONE;
|
|
ecma_number_t *result_p;
|
|
|
|
last_completion_value = ecma_op_to_number (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
byte_code_p = byte_code_start_p + 1;
|
|
result = last_completion_value;
|
|
result_p = ecma_get_number_from_value (result);
|
|
|
|
if (base & 0x2)
|
|
{
|
|
/* For decrement operators */
|
|
increase = ECMA_NUMBER_MINUS_ONE;
|
|
}
|
|
|
|
/* Post operators require the unmodifed number value. */
|
|
if (base & 0x4)
|
|
{
|
|
if (opcode_data & VM_OC_PUT_STACK)
|
|
{
|
|
if (base & 0x1)
|
|
{
|
|
JERRY_ASSERT (opcode == CBC_POST_INCR_IDENT_PUSH_RESULT
|
|
|| opcode == CBC_POST_DECR_IDENT_PUSH_RESULT);
|
|
|
|
*stack_top_p++ = ecma_copy_value (result);
|
|
}
|
|
else
|
|
{
|
|
/* The parser ensures there is enough space for the
|
|
* extra value on the stack. See js-parser-expr.c. */
|
|
|
|
JERRY_ASSERT (opcode == CBC_POST_INCR_PUSH_RESULT
|
|
|| opcode == CBC_POST_DECR_PUSH_RESULT);
|
|
|
|
stack_top_p++;
|
|
stack_top_p[-1] = stack_top_p[-2];
|
|
stack_top_p[-2] = stack_top_p[-3];
|
|
stack_top_p[-3] = ecma_copy_value (result);
|
|
}
|
|
opcode_data &= (uint32_t)~VM_OC_PUT_STACK;
|
|
}
|
|
else if (opcode_data & VM_OC_PUT_BLOCK)
|
|
{
|
|
ecma_free_value (block_result);
|
|
block_result = ecma_copy_value (result);
|
|
opcode_data &= (uint32_t) ~VM_OC_PUT_BLOCK;
|
|
}
|
|
}
|
|
|
|
*result_p = ecma_number_add (*result_p, increase);
|
|
break;
|
|
}
|
|
case VM_OC_ASSIGN:
|
|
{
|
|
result = left_value;
|
|
free_flags = 0;
|
|
break;
|
|
}
|
|
case VM_OC_ASSIGN_PROP:
|
|
{
|
|
result = stack_top_p[-1];
|
|
stack_top_p[-1] = left_value;
|
|
free_flags = 0;
|
|
break;
|
|
}
|
|
case VM_OC_ASSIGN_PROP_THIS:
|
|
{
|
|
result = stack_top_p[-1];
|
|
stack_top_p[-1] = ecma_copy_value (frame_ctx_p->this_binding);
|
|
*stack_top_p++ = left_value;
|
|
free_flags = 0;
|
|
break;
|
|
}
|
|
case VM_OC_RET:
|
|
{
|
|
JERRY_ASSERT (opcode == CBC_RETURN
|
|
|| opcode == CBC_RETURN_WITH_BLOCK
|
|
|| opcode == CBC_RETURN_WITH_LITERAL);
|
|
|
|
if (opcode == CBC_RETURN_WITH_BLOCK)
|
|
{
|
|
left_value = block_result;
|
|
block_result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
|
|
last_completion_value = left_value;
|
|
free_flags = 0;
|
|
goto error;
|
|
}
|
|
case VM_OC_THROW:
|
|
{
|
|
last_completion_value = ecma_make_error_value (left_value);
|
|
free_flags = 0;
|
|
goto error;
|
|
}
|
|
case VM_OC_THROW_REFERENCE_ERROR:
|
|
{
|
|
last_completion_value = ecma_raise_reference_error (ECMA_ERR_MSG (""));
|
|
goto error;
|
|
}
|
|
case VM_OC_EVAL:
|
|
{
|
|
is_direct_eval_form_call = true;
|
|
JERRY_ASSERT (*byte_code_p >= CBC_CALL && *byte_code_p <= CBC_CALL2_PROP_BLOCK);
|
|
continue;
|
|
}
|
|
case VM_OC_CALL:
|
|
{
|
|
JERRY_ASSERT (free_flags == 0);
|
|
|
|
if (frame_ctx_p->call_operation == VM_NO_EXEC_OP)
|
|
{
|
|
frame_ctx_p->call_operation = VM_EXEC_CALL;
|
|
frame_ctx_p->byte_code_p = byte_code_start_p;
|
|
frame_ctx_p->stack_top_p = stack_top_p;
|
|
frame_ctx_p->call_block_result = block_result;
|
|
return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
frame_ctx_p->call_operation = VM_NO_EXEC_OP;
|
|
|
|
last_completion_value = *(--stack_top_p);
|
|
block_result = frame_ctx_p->call_block_result;
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if (opcode_data & (VM_OC_PUT_DATA_MASK << VM_OC_PUT_DATA_SHIFT))
|
|
{
|
|
result = last_completion_value;
|
|
}
|
|
else
|
|
{
|
|
ecma_free_value (last_completion_value);
|
|
}
|
|
break;
|
|
}
|
|
case VM_OC_NEW:
|
|
{
|
|
JERRY_ASSERT (free_flags == 0);
|
|
|
|
if (frame_ctx_p->call_operation == VM_NO_EXEC_OP)
|
|
{
|
|
frame_ctx_p->call_operation = VM_EXEC_CONSTRUCT;
|
|
frame_ctx_p->byte_code_p = byte_code_start_p;
|
|
frame_ctx_p->stack_top_p = stack_top_p;
|
|
frame_ctx_p->call_block_result = block_result;
|
|
return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
frame_ctx_p->call_operation = VM_NO_EXEC_OP;
|
|
|
|
last_completion_value = *(--stack_top_p);
|
|
block_result = frame_ctx_p->call_block_result;
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
JERRY_ASSERT (opcode_data & (VM_OC_PUT_DATA_MASK << VM_OC_PUT_DATA_SHIFT));
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_PROP_DELETE:
|
|
{
|
|
last_completion_value = vm_op_delete_prop (left_value, right_value, is_strict);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_DELETE:
|
|
{
|
|
uint16_t literal_index;
|
|
|
|
READ_LITERAL_INDEX (literal_index);
|
|
|
|
if (literal_index < register_end)
|
|
{
|
|
result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_FALSE);
|
|
break;
|
|
}
|
|
|
|
last_completion_value = vm_op_delete_var (literal_start_p[literal_index],
|
|
frame_ctx_p->lex_env_p,
|
|
is_strict);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_JUMP:
|
|
{
|
|
byte_code_p = byte_code_start_p + branch_offset;
|
|
break;
|
|
}
|
|
case VM_OC_BRANCH_IF_STRICT_EQUAL:
|
|
{
|
|
JERRY_ASSERT (stack_top_p > frame_ctx_p->registers_p + register_end);
|
|
|
|
last_completion_value = opfunc_equal_value_type (left_value,
|
|
stack_top_p[-1]);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
|
|
if (result == ecma_make_simple_value (ECMA_SIMPLE_VALUE_TRUE))
|
|
{
|
|
byte_code_p = byte_code_start_p + branch_offset;
|
|
ecma_free_value (*--stack_top_p);
|
|
}
|
|
break;
|
|
}
|
|
case VM_OC_BRANCH_IF_TRUE:
|
|
case VM_OC_BRANCH_IF_FALSE:
|
|
case VM_OC_BRANCH_IF_LOGICAL_TRUE:
|
|
case VM_OC_BRANCH_IF_LOGICAL_FALSE:
|
|
{
|
|
uint32_t base = VM_OC_GROUP_GET_INDEX (opcode_data) - VM_OC_BRANCH_IF_TRUE;
|
|
|
|
last_completion_value = ecma_op_to_boolean (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
JERRY_ASSERT (free_flags & VM_FREE_LEFT_VALUE);
|
|
if (last_completion_value == ecma_make_simple_value ((base & 0x1) ? ECMA_SIMPLE_VALUE_FALSE
|
|
: ECMA_SIMPLE_VALUE_TRUE))
|
|
{
|
|
byte_code_p = byte_code_start_p + branch_offset;
|
|
if (base & 0x2)
|
|
{
|
|
free_flags = 0;
|
|
++stack_top_p;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case VM_OC_PLUS:
|
|
{
|
|
last_completion_value = opfunc_unary_plus (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_MINUS:
|
|
{
|
|
last_completion_value = opfunc_unary_minus (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_NOT:
|
|
{
|
|
last_completion_value = opfunc_logical_not (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_BIT_NOT:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_NOT,
|
|
left_value,
|
|
left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_TYPEOF_IDENT:
|
|
{
|
|
uint16_t literal_index;
|
|
|
|
READ_LITERAL_INDEX (literal_index);
|
|
|
|
JERRY_ASSERT (literal_index < ident_end);
|
|
|
|
if (literal_index < register_end)
|
|
{
|
|
left_value = ecma_copy_value (frame_ctx_p->registers_p[literal_index]);
|
|
free_flags = VM_FREE_LEFT_VALUE;
|
|
}
|
|
else
|
|
{
|
|
ecma_string_t *name_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]);
|
|
ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p,
|
|
name_p);
|
|
|
|
if (ref_base_lex_env_p == NULL)
|
|
{
|
|
ecma_deref_ecma_string (name_p);
|
|
|
|
ecma_string_t *string_p = ecma_get_magic_string (LIT_MAGIC_STRING_UNDEFINED);
|
|
result = ecma_make_string_value (string_p);
|
|
break;
|
|
}
|
|
|
|
last_completion_value = ecma_op_get_value_lex_env_base (ref_base_lex_env_p,
|
|
name_p,
|
|
is_strict);
|
|
|
|
ecma_deref_ecma_string (name_p);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
left_value = last_completion_value;
|
|
free_flags = VM_FREE_LEFT_VALUE;
|
|
}
|
|
/* FALLTHRU */
|
|
}
|
|
case VM_OC_TYPEOF:
|
|
{
|
|
last_completion_value = opfunc_typeof (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_ADD:
|
|
{
|
|
last_completion_value = opfunc_addition (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_SUB:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_SUBSTRACTION,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_MUL:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_MULTIPLICATION,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_DIV:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_DIVISION,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_MOD:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_REMAINDER,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_equal_value (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_NOT_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_not_equal_value (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_STRICT_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_equal_value_type (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_STRICT_NOT_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_not_equal_value_type (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_BIT_OR:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_LOGIC_OR,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_BIT_XOR:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_LOGIC_XOR,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_BIT_AND:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_LOGIC_AND,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_LEFT_SHIFT:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_SHIFT_LEFT,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_RIGHT_SHIFT:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_SHIFT_RIGHT,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_UNS_RIGHT_SHIFT:
|
|
{
|
|
last_completion_value = do_number_bitwise_logic (NUMBER_BITWISE_SHIFT_URIGHT,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_LESS:
|
|
{
|
|
last_completion_value = opfunc_less_than (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_GREATER:
|
|
{
|
|
last_completion_value = opfunc_greater_than (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_LESS_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_less_or_equal_than (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_GREATER_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_greater_or_equal_than (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_IN:
|
|
{
|
|
last_completion_value = opfunc_in (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_INSTANCEOF:
|
|
{
|
|
last_completion_value = opfunc_instanceof (left_value, right_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = last_completion_value;
|
|
break;
|
|
}
|
|
case VM_OC_WITH:
|
|
{
|
|
ecma_object_t *object_p;
|
|
ecma_object_t *with_env_p;
|
|
|
|
branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);
|
|
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
last_completion_value = ecma_op_to_object (left_value);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
object_p = ecma_get_object_from_value (last_completion_value);
|
|
|
|
with_env_p = ecma_create_object_lex_env (frame_ctx_p->lex_env_p,
|
|
object_p,
|
|
true);
|
|
|
|
ecma_deref_object (object_p);
|
|
|
|
VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p += PARSER_WITH_CONTEXT_STACK_ALLOCATION;
|
|
|
|
stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_WITH, branch_offset);
|
|
stack_top_p[-2] = ecma_make_object_value (frame_ctx_p->lex_env_p);
|
|
|
|
frame_ctx_p->lex_env_p = with_env_p;
|
|
break;
|
|
}
|
|
case VM_OC_FOR_IN_CREATE_CONTEXT:
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
ecma_value_t expr_obj_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
ecma_collection_header_t *header_p = opfunc_for_in (left_value, &expr_obj_value);
|
|
|
|
if (header_p == NULL)
|
|
{
|
|
byte_code_p = byte_code_start_p + branch_offset;
|
|
break;
|
|
}
|
|
|
|
branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);
|
|
|
|
VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p += PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION;
|
|
stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_FOR_IN, branch_offset);
|
|
stack_top_p[-2] = header_p->first_chunk_cp;
|
|
stack_top_p[-3] = expr_obj_value;
|
|
|
|
ecma_dealloc_collection_header (header_p);
|
|
break;
|
|
}
|
|
case VM_OC_FOR_IN_GET_NEXT:
|
|
{
|
|
ecma_value_t *context_top_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth;
|
|
ecma_collection_chunk_t *chunk_p = MEM_CP_GET_NON_NULL_POINTER (ecma_collection_chunk_t, context_top_p[-2]);
|
|
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (context_top_p[-1]) == VM_CONTEXT_FOR_IN);
|
|
|
|
lit_utf8_byte_t *data_ptr = chunk_p->data;
|
|
result = *(ecma_value_t *) data_ptr;
|
|
context_top_p[-2] = chunk_p->next_chunk_cp;
|
|
|
|
ecma_dealloc_collection_chunk (chunk_p);
|
|
break;
|
|
}
|
|
case VM_OC_FOR_IN_HAS_NEXT:
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
while (true)
|
|
{
|
|
if (stack_top_p[-2] == MEM_CP_NULL)
|
|
{
|
|
ecma_free_value (stack_top_p[-3]);
|
|
|
|
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p -= PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION;
|
|
break;
|
|
}
|
|
|
|
ecma_collection_chunk_t *chunk_p = MEM_CP_GET_NON_NULL_POINTER (ecma_collection_chunk_t, stack_top_p[-2]);
|
|
|
|
lit_utf8_byte_t *data_ptr = chunk_p->data;
|
|
ecma_string_t *prop_name_p = ecma_get_string_from_value (*(ecma_value_t *) data_ptr);
|
|
|
|
if (ecma_op_object_get_property (ecma_get_object_from_value (stack_top_p[-3]),
|
|
prop_name_p) == NULL)
|
|
{
|
|
stack_top_p[-2] = chunk_p->next_chunk_cp;
|
|
ecma_deref_ecma_string (prop_name_p);
|
|
ecma_dealloc_collection_chunk (chunk_p);
|
|
}
|
|
else
|
|
{
|
|
byte_code_p = byte_code_start_p + branch_offset;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VM_OC_TRY:
|
|
{
|
|
/* Try opcode simply creates the try context. */
|
|
branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);
|
|
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p += PARSER_TRY_CONTEXT_STACK_ALLOCATION;
|
|
|
|
stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_TRY, branch_offset);
|
|
break;
|
|
}
|
|
case VM_OC_CATCH:
|
|
{
|
|
/* Catches are ignored and turned to jumps. */
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY);
|
|
|
|
byte_code_p = byte_code_start_p + branch_offset;
|
|
break;
|
|
}
|
|
case VM_OC_FINALLY:
|
|
{
|
|
branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);
|
|
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY
|
|
|| VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH);
|
|
|
|
if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH)
|
|
{
|
|
ecma_deref_object (frame_ctx_p->lex_env_p);
|
|
frame_ctx_p->lex_env_p = ecma_get_object_from_value (stack_top_p[-2]);
|
|
}
|
|
|
|
stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_FINALLY_JUMP, branch_offset);
|
|
stack_top_p[-2] = (ecma_value_t) branch_offset;
|
|
break;
|
|
}
|
|
case VM_OC_CONTEXT_END:
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
switch (VM_GET_CONTEXT_TYPE (stack_top_p[-1]))
|
|
{
|
|
case VM_CONTEXT_FINALLY_JUMP:
|
|
{
|
|
uint32_t jump_target = stack_top_p[-2];
|
|
|
|
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth,
|
|
PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION;
|
|
|
|
if (vm_stack_find_finally (frame_ctx_p,
|
|
&stack_top_p,
|
|
VM_CONTEXT_FINALLY_JUMP,
|
|
jump_target))
|
|
{
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP);
|
|
byte_code_p = frame_ctx_p->byte_code_p;
|
|
stack_top_p[-2] = jump_target;
|
|
}
|
|
else
|
|
{
|
|
byte_code_p = frame_ctx_p->byte_code_start_p + jump_target;
|
|
}
|
|
break;
|
|
}
|
|
case VM_CONTEXT_FINALLY_THROW:
|
|
{
|
|
last_completion_value = stack_top_p[-2];
|
|
|
|
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth,
|
|
PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION;
|
|
goto error;
|
|
}
|
|
case VM_CONTEXT_FINALLY_RETURN:
|
|
{
|
|
last_completion_value = stack_top_p[-2];
|
|
|
|
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth,
|
|
PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION;
|
|
goto error;
|
|
}
|
|
default:
|
|
{
|
|
stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p);
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
break;
|
|
}
|
|
case VM_OC_JUMP_AND_EXIT_CONTEXT:
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p);
|
|
|
|
if (vm_stack_find_finally (frame_ctx_p,
|
|
&stack_top_p,
|
|
VM_CONTEXT_FINALLY_JUMP,
|
|
(uint32_t) branch_offset))
|
|
{
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP);
|
|
byte_code_p = frame_ctx_p->byte_code_p;
|
|
stack_top_p[-2] = (uint32_t) branch_offset;
|
|
}
|
|
else
|
|
{
|
|
byte_code_p = frame_ctx_p->byte_code_start_p + branch_offset;
|
|
}
|
|
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_UNREACHABLE ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (opcode_data & (VM_OC_PUT_DATA_MASK << VM_OC_PUT_DATA_SHIFT))
|
|
{
|
|
if (opcode_data & VM_OC_PUT_IDENT)
|
|
{
|
|
uint16_t literal_index;
|
|
|
|
READ_LITERAL_INDEX (literal_index);
|
|
|
|
if (literal_index < register_end)
|
|
{
|
|
ecma_free_value (frame_ctx_p->registers_p[literal_index]);
|
|
|
|
frame_ctx_p->registers_p[literal_index] = result;
|
|
|
|
if (opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))
|
|
{
|
|
result = ecma_copy_value (result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ecma_string_t *var_name_str_p;
|
|
ecma_object_t *ref_base_lex_env_p;
|
|
|
|
var_name_str_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]);
|
|
ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p,
|
|
var_name_str_p);
|
|
|
|
last_completion_value = ecma_op_put_value_lex_env_base (ref_base_lex_env_p,
|
|
var_name_str_p,
|
|
is_strict,
|
|
result);
|
|
|
|
ecma_deref_ecma_string (var_name_str_p);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
ecma_free_value (result);
|
|
goto error;
|
|
}
|
|
|
|
if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK)))
|
|
{
|
|
ecma_free_value (result);
|
|
}
|
|
}
|
|
}
|
|
else if (opcode_data & VM_OC_PUT_REFERENCE)
|
|
{
|
|
ecma_value_t property = *(--stack_top_p);
|
|
ecma_value_t object = *(--stack_top_p);
|
|
|
|
if (object == ecma_make_simple_value (ECMA_SIMPLE_VALUE_REGISTER_REF))
|
|
{
|
|
ecma_free_value (frame_ctx_p->registers_p[property]);
|
|
|
|
frame_ctx_p->registers_p[property] = result;
|
|
|
|
if (opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))
|
|
{
|
|
result = ecma_copy_value (result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_completion_value = vm_op_set_value (object,
|
|
property,
|
|
result,
|
|
is_strict);
|
|
|
|
ecma_free_value (object);
|
|
ecma_free_value (property);
|
|
|
|
if (ecma_is_value_error (last_completion_value))
|
|
{
|
|
ecma_free_value (result);
|
|
goto error;
|
|
}
|
|
|
|
if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK)))
|
|
{
|
|
ecma_free_value (result);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opcode_data & VM_OC_PUT_STACK)
|
|
{
|
|
*stack_top_p++ = result;
|
|
}
|
|
else if (opcode_data & VM_OC_PUT_BLOCK)
|
|
{
|
|
ecma_free_value (block_result);
|
|
block_result = result;
|
|
}
|
|
}
|
|
|
|
if (free_flags & VM_FREE_LEFT_VALUE)
|
|
{
|
|
ecma_free_value (left_value);
|
|
}
|
|
|
|
if (free_flags & VM_FREE_RIGHT_VALUE)
|
|
{
|
|
ecma_free_value (right_value);
|
|
}
|
|
}
|
|
error:
|
|
|
|
if (free_flags & VM_FREE_LEFT_VALUE)
|
|
{
|
|
ecma_free_value (left_value);
|
|
}
|
|
|
|
if (free_flags & VM_FREE_RIGHT_VALUE)
|
|
{
|
|
ecma_free_value (right_value);
|
|
}
|
|
|
|
if (unlikely (ecma_is_value_error (last_completion_value)))
|
|
{
|
|
ecma_value_t *vm_stack_p = stack_top_p;
|
|
|
|
for (vm_stack_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth;
|
|
vm_stack_p < stack_top_p;
|
|
vm_stack_p++)
|
|
{
|
|
if (*vm_stack_p == ecma_make_simple_value (ECMA_SIMPLE_VALUE_REGISTER_REF))
|
|
{
|
|
JERRY_ASSERT (vm_stack_p < stack_top_p);
|
|
vm_stack_p++;
|
|
}
|
|
else
|
|
{
|
|
ecma_free_value (*vm_stack_p);
|
|
}
|
|
}
|
|
|
|
stack_top_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth;
|
|
}
|
|
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
if (frame_ctx_p->context_depth == 0)
|
|
{
|
|
/* In most cases there is no context. */
|
|
|
|
ecma_free_value (block_result);
|
|
return last_completion_value;
|
|
}
|
|
|
|
if (!ecma_is_value_error (last_completion_value))
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
stack_top_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth;
|
|
|
|
if (vm_stack_find_finally (frame_ctx_p,
|
|
&stack_top_p,
|
|
VM_CONTEXT_FINALLY_RETURN,
|
|
0))
|
|
{
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN);
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
byte_code_p = frame_ctx_p->byte_code_p;
|
|
stack_top_p[-2] = last_completion_value;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (vm_stack_find_finally (frame_ctx_p,
|
|
&stack_top_p,
|
|
VM_CONTEXT_FINALLY_THROW,
|
|
0))
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p);
|
|
|
|
result = last_completion_value;
|
|
byte_code_p = frame_ctx_p->byte_code_p;
|
|
|
|
if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH)
|
|
{
|
|
uint32_t literal_index;
|
|
ecma_object_t *catch_env_p;
|
|
ecma_string_t *catch_name_p;
|
|
|
|
*stack_top_p++ = ecma_get_value_from_error_value (result);
|
|
|
|
JERRY_ASSERT (byte_code_p[0] == CBC_ASSIGN_SET_IDENT);
|
|
|
|
literal_index = byte_code_p[1];
|
|
if (literal_index >= encoding_limit)
|
|
{
|
|
literal_index = ((literal_index << 8) | byte_code_p[2]) - encoding_delta;
|
|
}
|
|
|
|
catch_env_p = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p);
|
|
|
|
catch_name_p = ecma_new_ecma_string_from_lit_cp (literal_start_p[literal_index]);
|
|
|
|
ecma_op_create_mutable_binding (catch_env_p, catch_name_p, false);
|
|
|
|
ecma_deref_ecma_string (catch_name_p);
|
|
|
|
stack_top_p[-2 - 1] = ecma_make_object_value (frame_ctx_p->lex_env_p);
|
|
frame_ctx_p->lex_env_p = catch_env_p;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW);
|
|
stack_top_p[-2] = result;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ecma_free_value (block_result);
|
|
return last_completion_value;
|
|
}
|
|
} /* vm_loop */
|
|
|
|
#undef READ_LITERAL
|
|
#undef READ_LITERAL_INDEX
|
|
|
|
/**
|
|
* Execute code block.
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t __attr_noinline___
|
|
vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|
const void *arg_p, /**< arguments list */
|
|
ecma_length_t arg_list_len) /**< length of arguments list */
|
|
{
|
|
const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p;
|
|
ecma_value_t completion_value;
|
|
vm_frame_ctx_t *prev_context_p;
|
|
uint16_t argument_end;
|
|
uint16_t register_end;
|
|
|
|
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
|
|
|
|
argument_end = args_p->argument_end;
|
|
register_end = args_p->register_end;
|
|
}
|
|
else
|
|
{
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
|
|
|
|
argument_end = args_p->argument_end;
|
|
register_end = args_p->register_end;
|
|
}
|
|
|
|
frame_ctx_p->stack_top_p = frame_ctx_p->registers_p + register_end;
|
|
|
|
if (arg_list_len == 0)
|
|
{
|
|
ecma_collection_header_t *arg_collection_p = (ecma_collection_header_t *) arg_p;
|
|
ecma_collection_iterator_t arguments_iterator;
|
|
|
|
arg_list_len = arg_collection_p->unit_number;
|
|
if (arg_list_len > argument_end)
|
|
{
|
|
arg_list_len = argument_end;
|
|
}
|
|
|
|
ecma_collection_iterator_init (&arguments_iterator, arg_collection_p);
|
|
|
|
for (uint32_t i = 0; i < arg_list_len; i++)
|
|
{
|
|
ecma_value_t value;
|
|
|
|
ecma_collection_iterator_next (&arguments_iterator);
|
|
value = *arguments_iterator.current_value_p;
|
|
frame_ctx_p->registers_p[i] = ecma_copy_value (value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ecma_value_t *src_p = (ecma_value_t *) arg_p;
|
|
arg_list_len--;
|
|
|
|
if (arg_list_len > argument_end)
|
|
{
|
|
arg_list_len = argument_end;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < arg_list_len; i++)
|
|
{
|
|
frame_ctx_p->registers_p[i] = ecma_copy_value (src_p[i]);
|
|
}
|
|
}
|
|
|
|
/* The arg_list_len contains the end of the copied arguments.
|
|
* Fill everything else with undefined. */
|
|
if (register_end > arg_list_len)
|
|
{
|
|
ecma_value_t *stack_p = frame_ctx_p->registers_p + arg_list_len;
|
|
|
|
for (uint32_t i = arg_list_len; i < register_end; i++)
|
|
{
|
|
*stack_p++ = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
}
|
|
|
|
is_direct_eval_form_call = false;
|
|
|
|
prev_context_p = vm_top_context_p;
|
|
vm_top_context_p = frame_ctx_p;
|
|
|
|
completion_value = vm_init_loop (frame_ctx_p);
|
|
|
|
if (!ecma_is_value_error (completion_value))
|
|
{
|
|
while (true)
|
|
{
|
|
completion_value = vm_loop (frame_ctx_p);
|
|
|
|
if (frame_ctx_p->call_operation == VM_NO_EXEC_OP)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (frame_ctx_p->call_operation == VM_EXEC_CALL)
|
|
{
|
|
opfunc_call (frame_ctx_p);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (frame_ctx_p->call_operation == VM_EXEC_CONSTRUCT);
|
|
opfunc_construct (frame_ctx_p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Free arguments and registers */
|
|
for (uint32_t i = 0; i < register_end; i++)
|
|
{
|
|
ecma_free_value (frame_ctx_p->registers_p[i]);
|
|
}
|
|
|
|
vm_top_context_p = prev_context_p;
|
|
return completion_value;
|
|
} /* vm_execute */
|
|
|
|
#define INLINE_STACK_SIZE 16
|
|
|
|
/**
|
|
* Run the code with inline stack.
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t __attr_noinline___
|
|
vm_run_with_inline_stack (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|
const void *arg_p, /**< arguments list */
|
|
ecma_length_t arg_list_len) /**< length of arguments list */
|
|
{
|
|
ecma_value_t inline_stack[INLINE_STACK_SIZE];
|
|
|
|
frame_ctx_p->registers_p = inline_stack;
|
|
|
|
return vm_execute (frame_ctx_p, arg_p, arg_list_len);
|
|
} /* vm_run_with_inline_stack */
|
|
|
|
/**
|
|
* Run the code with inline stack.
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
static ecma_value_t __attr_noinline___
|
|
vm_run_with_alloca (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
|
const void *arg_p, /**< arguments list */
|
|
ecma_length_t arg_list_len, /**< length of arguments list */
|
|
uint32_t call_stack_size) /**< call stack size */
|
|
{
|
|
size_t size = call_stack_size * sizeof (ecma_value_t);
|
|
|
|
ecma_value_t *stack = (ecma_value_t *) alloca (size);
|
|
|
|
frame_ctx_p->registers_p = stack;
|
|
|
|
return vm_execute (frame_ctx_p, arg_p, arg_list_len);
|
|
} /* vm_run_with_alloca */
|
|
|
|
/**
|
|
* Run the code.
|
|
*
|
|
* @return ecma value
|
|
*/
|
|
ecma_value_t
|
|
vm_run (const ecma_compiled_code_t *bytecode_header_p, /**< byte-code data header */
|
|
ecma_value_t this_binding_value, /**< value of 'ThisBinding' */
|
|
ecma_object_t *lex_env_p, /**< lexical environment to use */
|
|
bool is_eval_code, /**< is the code is eval code (ECMA-262 v5, 10.1) */
|
|
const ecma_value_t *arg_list_p, /**< arguments list */
|
|
ecma_length_t arg_list_len) /**< length of arguments list */
|
|
{
|
|
lit_cpointer_t *literal_p;
|
|
vm_frame_ctx_t frame_ctx;
|
|
uint32_t call_stack_size;
|
|
|
|
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
|
{
|
|
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
|
|
uint8_t *byte_p = (uint8_t *) bytecode_header_p;
|
|
|
|
literal_p = (lit_cpointer_t *) (byte_p + sizeof (cbc_uint16_arguments_t));
|
|
frame_ctx.literal_start_p = literal_p;
|
|
literal_p += args_p->literal_end;
|
|
call_stack_size = (uint32_t) (args_p->register_end + args_p->stack_limit);
|
|
}
|
|
else
|
|
{
|
|
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
|
|
uint8_t *byte_p = (uint8_t *) bytecode_header_p;
|
|
|
|
literal_p = (lit_cpointer_t *) (byte_p + sizeof (cbc_uint8_arguments_t));
|
|
frame_ctx.literal_start_p = literal_p;
|
|
literal_p += args_p->literal_end;
|
|
call_stack_size = (uint32_t) (args_p->register_end + args_p->stack_limit);
|
|
}
|
|
|
|
frame_ctx.bytecode_header_p = bytecode_header_p;
|
|
frame_ctx.byte_code_p = (uint8_t *) literal_p;
|
|
frame_ctx.byte_code_start_p = (uint8_t *) literal_p;
|
|
frame_ctx.lex_env_p = lex_env_p;
|
|
frame_ctx.this_binding = this_binding_value;
|
|
frame_ctx.context_depth = 0;
|
|
frame_ctx.is_eval_code = is_eval_code;
|
|
frame_ctx.call_operation = VM_NO_EXEC_OP;
|
|
|
|
arg_list_len++;
|
|
|
|
if (call_stack_size <= INLINE_STACK_SIZE)
|
|
{
|
|
return vm_run_with_inline_stack (&frame_ctx,
|
|
arg_list_p,
|
|
arg_list_len);
|
|
}
|
|
else
|
|
{
|
|
return vm_run_with_alloca (&frame_ctx,
|
|
arg_list_p,
|
|
arg_list_len,
|
|
call_stack_size);
|
|
}
|
|
} /* vm_run */
|
|
|
|
/**
|
|
* Check whether currently executed code is strict mode code
|
|
*
|
|
* @return true - current code is executed in strict mode,
|
|
* false - otherwise.
|
|
*/
|
|
bool
|
|
vm_is_strict_mode (void)
|
|
{
|
|
JERRY_ASSERT (vm_top_context_p != NULL);
|
|
|
|
return vm_top_context_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE;
|
|
} /* vm_is_strict_mode */
|
|
|
|
/**
|
|
* Check whether currently performed call (on top of call-stack) is performed in form,
|
|
* meeting conditions of 'Direct Call to Eval' (see also: ECMA-262 v5, 15.1.2.1.1)
|
|
*
|
|
* Warning:
|
|
* the function should only be called from implementation
|
|
* of built-in 'eval' routine of Global object
|
|
*
|
|
* @return true - currently performed call is performed through 'eval' identifier,
|
|
* without 'this' argument,
|
|
* false - otherwise.
|
|
*/
|
|
bool
|
|
vm_is_direct_eval_form_call (void)
|
|
{
|
|
return is_direct_eval_form_call;
|
|
} /* vm_is_direct_eval_form_call */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|