mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
2501 lines
77 KiB
C++
2501 lines
77 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 "opcodes.h"
|
|
#include "rcs-records.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 completion value
|
|
*/
|
|
static ecma_completion_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)))
|
|
{
|
|
ecma_object_t *error = ecma_new_standard_error (ECMA_ERROR_TYPE);
|
|
return ecma_make_throw_obj_completion_value (error);
|
|
}
|
|
|
|
ecma_completion_value_t completion_value = ecma_make_empty_completion_value ();
|
|
|
|
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 completion value
|
|
*/
|
|
static ecma_completion_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_completion_value_t completion_value = ecma_make_empty_completion_value ();
|
|
|
|
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 */
|
|
bool dump_mem_stats) /**< dump per-instruction memory usage change statistics */
|
|
{
|
|
JERRY_ASSERT (!dump_mem_stats);
|
|
|
|
JERRY_ASSERT (__program == NULL);
|
|
|
|
__program = program_p;
|
|
} /* vm_init */
|
|
|
|
#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg4,
|
|
|
|
/**
|
|
* Decode table for opcodes.
|
|
*/
|
|
uint32_t vm_decode_table[] =
|
|
{
|
|
CBC_OPCODE_LIST
|
|
};
|
|
|
|
/**
|
|
* Decode table for extended opcodes.
|
|
*/
|
|
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 (void)
|
|
{
|
|
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_completion_value_t completion = vm_run (__program,
|
|
ecma_make_object_value (glob_obj_p),
|
|
lex_env_p,
|
|
false,
|
|
NULL,
|
|
0);
|
|
|
|
if (ecma_is_completion_value_return (completion))
|
|
{
|
|
ret_code = JERRY_COMPLETION_CODE_OK;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (ecma_is_completion_value_throw (completion));
|
|
|
|
ret_code = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION;
|
|
}
|
|
|
|
ecma_free_completion_value (completion);
|
|
|
|
ecma_deref_object (glob_obj_p);
|
|
ecma_deref_object (lex_env_p);
|
|
|
|
return ret_code;
|
|
} /* vm_run_global */
|
|
|
|
/**
|
|
* Run specified eval-mode bytecode
|
|
*
|
|
* @return completion value
|
|
*/
|
|
ecma_completion_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, true);
|
|
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_completion_value_t completion = vm_run (bytecode_data_p,
|
|
this_binding,
|
|
lex_env_p,
|
|
true,
|
|
NULL,
|
|
0);
|
|
|
|
if (ecma_is_completion_value_return (completion))
|
|
{
|
|
completion = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion));
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (ecma_is_completion_value_throw (completion));
|
|
}
|
|
|
|
ecma_deref_object (lex_env_p);
|
|
ecma_free_value (this_binding, true);
|
|
ecma_bytecode_deref (bytecode_data_p);
|
|
|
|
return completion;
|
|
} /* 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.value.base_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_completion_value_t ret_value;
|
|
ret_value = ecma_op_create_regexp_object_from_bytecode ((re_compiled_code_t *) bytecode_p);
|
|
|
|
if (ecma_is_completion_value_throw (ret_value))
|
|
{
|
|
// FIXME: throw exception instead of define an 'undefined' value.
|
|
return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
|
|
return ecma_get_completion_value_value (ret_value);
|
|
#else
|
|
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 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_completion_value_t completion_value = ecma_op_implicit_this_value (this_obj_p);
|
|
|
|
JERRY_ASSERT (ecma_is_completion_value_normal (completion_value));
|
|
|
|
*this_value_p = ecma_get_completion_value_value (completion_value);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} /* vm_get_implicit_this_value */
|
|
|
|
/**
|
|
* 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)
|
|
|
|
/* FIXME: 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], true); \
|
|
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 \
|
|
{ \
|
|
ecma_object_t *error = ecma_new_standard_error (ECMA_ERROR_REFERENCE); \
|
|
last_completion_value = ecma_make_throw_obj_completion_value (error); \
|
|
} \
|
|
\
|
|
ecma_deref_ecma_string (name_p); \
|
|
\
|
|
if (ecma_is_completion_value_throw (last_completion_value)) \
|
|
{ \
|
|
goto error; \
|
|
} \
|
|
(target_value) = ecma_get_completion_value_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 = rcs_cpointer_decompress (lit_cpointer); \
|
|
if (unlikely (RCS_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 completion value
|
|
*/
|
|
static ecma_completion_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_completion_value_t last_completion_value = ecma_make_empty_completion_value ();
|
|
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]);
|
|
}
|
|
|
|
// FIXME: 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, true);
|
|
}
|
|
|
|
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_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return last_completion_value;
|
|
} /* vm_init_loop */
|
|
|
|
/**
|
|
* Run generic byte code.
|
|
*
|
|
* @return completion value
|
|
*/
|
|
ecma_completion_value_t
|
|
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_completion_value_t last_completion_value = ecma_make_empty_completion_value ();
|
|
|
|
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->registers_p + register_end;
|
|
|
|
/* 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, true);
|
|
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), true);
|
|
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, true);
|
|
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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
ecma_value_t property_name = ecma_get_completion_value_value (last_completion_value);
|
|
|
|
prop_name_p = ecma_get_string_from_value (property_name);
|
|
property_p = ecma_find_named_property (object_p, prop_name_p);
|
|
}
|
|
|
|
if (property_p != NULL && property_p->type != ECMA_PROPERTY_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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
result = ecma_get_completion_value_value (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 = length_prop_p->u.named_data_property.value;
|
|
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], true);
|
|
}
|
|
|
|
(*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], true);
|
|
}
|
|
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
|
|
{
|
|
ecma_object_t *error = ecma_new_standard_error (ECMA_ERROR_REFERENCE);
|
|
last_completion_value = ecma_make_throw_obj_completion_value (error);
|
|
}
|
|
|
|
if (ecma_is_completion_value_throw (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 = ecma_get_completion_value_value (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, true);
|
|
*stack_top_p++ = ecma_copy_value (right_value, true);
|
|
}
|
|
/* 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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
byte_code_p = byte_code_start_p + 1;
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
result_p = ecma_get_number_from_value (result);
|
|
|
|
if (base & 0x2)
|
|
{
|
|
/* For decrement operators */
|
|
increase = -ECMA_NUMBER_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, true);
|
|
}
|
|
else
|
|
{
|
|
/* The parser ensures there is enough space for the
|
|
* extra value on the stack. See js-parser-expr.cpp. */
|
|
|
|
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, true);
|
|
}
|
|
opcode_data &= (uint32_t)~VM_OC_PUT_STACK;
|
|
}
|
|
else if (opcode_data & VM_OC_PUT_BLOCK)
|
|
{
|
|
ecma_free_value (block_result, true);
|
|
block_result = ecma_copy_value (result, true);
|
|
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, true);
|
|
*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 = ecma_make_completion_value (ECMA_COMPLETION_TYPE_RETURN, left_value);
|
|
free_flags = 0;
|
|
goto error;
|
|
}
|
|
case VM_OC_THROW:
|
|
{
|
|
last_completion_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_THROW, left_value);
|
|
free_flags = 0;
|
|
goto error;
|
|
}
|
|
case VM_OC_THROW_REFERENCE_ERROR:
|
|
{
|
|
ecma_object_t *error = ecma_new_standard_error (ECMA_ERROR_REFERENCE);
|
|
last_completion_value = ecma_make_throw_obj_completion_value (error);
|
|
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_N:
|
|
case VM_OC_CALL_PROP_N:
|
|
{
|
|
right_value = (unsigned int) ((opcode - CBC_CALL0) / 6);
|
|
/* FALLTHRU */
|
|
}
|
|
case VM_OC_CALL:
|
|
case VM_OC_CALL_PROP:
|
|
{
|
|
ecma_value_t this_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
|
|
|
stack_top_p -= right_value;
|
|
|
|
if (VM_OC_GROUP_GET_INDEX (opcode_data) >= VM_OC_CALL_PROP_N)
|
|
{
|
|
this_value = stack_top_p[-3];
|
|
|
|
if (vm_get_implicit_this_value (&this_value))
|
|
{
|
|
ecma_free_value (stack_top_p[-3], true);
|
|
stack_top_p[-3] = this_value;
|
|
}
|
|
}
|
|
|
|
last_completion_value = opfunc_call_n (this_value,
|
|
stack_top_p[-1],
|
|
stack_top_p,
|
|
right_value);
|
|
|
|
is_direct_eval_form_call = false;
|
|
|
|
/* Free registers. */
|
|
for (uint32_t i = 0; i < right_value; i++)
|
|
{
|
|
ecma_free_value (stack_top_p[i], true);
|
|
}
|
|
|
|
ecma_free_value (*(--stack_top_p), true);
|
|
|
|
if (VM_OC_GROUP_GET_INDEX (opcode_data) >= VM_OC_CALL_PROP_N)
|
|
{
|
|
ecma_free_value (*(--stack_top_p), true);
|
|
ecma_free_value (*(--stack_top_p), true);
|
|
}
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if (opcode_data & (VM_OC_PUT_DATA_MASK << VM_OC_PUT_DATA_SHIFT))
|
|
{
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
}
|
|
else
|
|
{
|
|
ecma_free_completion_value (last_completion_value);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VM_OC_NEW_N:
|
|
{
|
|
right_value = opcode - (uint32_t) CBC_NEW0;
|
|
/* FALLTHRU */
|
|
}
|
|
case VM_OC_NEW:
|
|
{
|
|
stack_top_p -= right_value;
|
|
|
|
last_completion_value = opfunc_construct_n (stack_top_p[-1],
|
|
stack_top_p,
|
|
right_value);
|
|
|
|
/* Free registers. */
|
|
for (uint32_t i = 0; i < right_value; i++)
|
|
{
|
|
ecma_free_value (stack_top_p[i], true);
|
|
}
|
|
|
|
ecma_free_value (*(--stack_top_p), true);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
JERRY_ASSERT (opcode_data & (VM_OC_PUT_DATA_MASK << VM_OC_PUT_DATA_SHIFT));
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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, true);
|
|
}
|
|
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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
ecma_value_t value = ecma_get_completion_value_value (last_completion_value);
|
|
|
|
JERRY_ASSERT (free_flags & VM_FREE_LEFT_VALUE);
|
|
if (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_MINUS:
|
|
{
|
|
last_completion_value = opfunc_unary_minus (left_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_NOT:
|
|
{
|
|
last_completion_value = opfunc_logical_not (left_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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], true);
|
|
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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
left_value = ecma_get_completion_value_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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_ADD:
|
|
{
|
|
last_completion_value = opfunc_addition (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_SUB:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_SUBSTRACTION,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_MUL:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_MULTIPLICATION,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_DIV:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_DIVISION,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_MOD:
|
|
{
|
|
last_completion_value = do_number_arithmetic (NUMBER_ARITHMETIC_REMAINDER,
|
|
left_value,
|
|
right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_equal_value (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_NOT_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_not_equal_value (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_STRICT_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_equal_value_type (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_LESS:
|
|
{
|
|
last_completion_value = opfunc_less_than (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_GREATER:
|
|
{
|
|
last_completion_value = opfunc_greater_than (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_LESS_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_less_or_equal_than (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_GREATER_EQUAL:
|
|
{
|
|
last_completion_value = opfunc_greater_or_equal_than (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_IN:
|
|
{
|
|
last_completion_value = opfunc_in (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_INSTANCEOF:
|
|
{
|
|
last_completion_value = opfunc_instanceof (left_value, right_value);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
result = ecma_get_completion_value_value (last_completion_value);
|
|
break;
|
|
}
|
|
case VM_OC_WITH:
|
|
{
|
|
ecma_value_t object;
|
|
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_completion_value_throw (last_completion_value))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
object = ecma_get_completion_value_value (last_completion_value);
|
|
object_p = ecma_get_object_from_value (object);
|
|
|
|
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);
|
|
|
|
result = *(ecma_value_t *) chunk_p->data;
|
|
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], true);
|
|
|
|
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]);
|
|
|
|
ecma_string_t *prop_name_p = ecma_get_string_from_value (*(ecma_value_t *) chunk_p->data);
|
|
|
|
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 = ecma_make_completion_value (ECMA_COMPLETION_TYPE_THROW,
|
|
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 = ecma_make_completion_value (ECMA_COMPLETION_TYPE_RETURN,
|
|
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], true);
|
|
|
|
frame_ctx_p->registers_p[literal_index] = result;
|
|
|
|
if (opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))
|
|
{
|
|
result = ecma_copy_value (result, true);
|
|
}
|
|
}
|
|
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_completion_value_throw (last_completion_value))
|
|
{
|
|
ecma_free_value (result, true);
|
|
goto error;
|
|
}
|
|
|
|
if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK)))
|
|
{
|
|
ecma_free_value (result, true);
|
|
}
|
|
}
|
|
}
|
|
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], true);
|
|
|
|
frame_ctx_p->registers_p[property] = result;
|
|
|
|
if (opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK))
|
|
{
|
|
result = ecma_copy_value (result, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_completion_value = vm_op_set_value (object,
|
|
property,
|
|
result,
|
|
is_strict);
|
|
|
|
ecma_free_value (object, true);
|
|
ecma_free_value (property, true);
|
|
|
|
if (ecma_is_completion_value_throw (last_completion_value))
|
|
{
|
|
ecma_free_value (result, true);
|
|
goto error;
|
|
}
|
|
|
|
if (!(opcode_data & (VM_OC_PUT_STACK | VM_OC_PUT_BLOCK)))
|
|
{
|
|
ecma_free_value (result, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opcode_data & VM_OC_PUT_STACK)
|
|
{
|
|
*stack_top_p++ = result;
|
|
}
|
|
else if (opcode_data & VM_OC_PUT_BLOCK)
|
|
{
|
|
ecma_free_value (block_result, true);
|
|
block_result = result;
|
|
}
|
|
}
|
|
|
|
if (free_flags & VM_FREE_LEFT_VALUE)
|
|
{
|
|
ecma_free_value (left_value, true);
|
|
}
|
|
|
|
if (free_flags & VM_FREE_RIGHT_VALUE)
|
|
{
|
|
ecma_free_value (right_value, true);
|
|
}
|
|
}
|
|
error:
|
|
|
|
if (free_flags & VM_FREE_LEFT_VALUE)
|
|
{
|
|
ecma_free_value (left_value, true);
|
|
}
|
|
|
|
if (free_flags & VM_FREE_RIGHT_VALUE)
|
|
{
|
|
ecma_free_value (right_value, true);
|
|
}
|
|
|
|
if (unlikely (ecma_is_completion_value_throw (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, true);
|
|
}
|
|
}
|
|
|
|
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, true);
|
|
return last_completion_value;
|
|
}
|
|
|
|
if (ecma_is_completion_value_return (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] = ecma_get_completion_value_value (last_completion_value);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (ecma_is_completion_value_throw (last_completion_value));
|
|
|
|
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 = ecma_get_completion_value_value (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++ = 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, true);
|
|
return last_completion_value;
|
|
}
|
|
} /* vm_loop */
|
|
|
|
#undef READ_LITERAL
|
|
#undef READ_LITERAL_INDEX
|
|
|
|
/**
|
|
* Execute code block.
|
|
*
|
|
* @return completion value
|
|
*/
|
|
static ecma_completion_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_completion_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;
|
|
}
|
|
|
|
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, true);
|
|
}
|
|
}
|
|
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], true);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
vm_init_loop (frame_ctx_p);
|
|
|
|
completion_value = vm_loop (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], true);
|
|
}
|
|
|
|
JERRY_ASSERT (ecma_is_completion_value_throw (completion_value)
|
|
|| ecma_is_completion_value_return (completion_value));
|
|
|
|
vm_top_context_p = prev_context_p;
|
|
return completion_value;
|
|
} /* vm_execute */
|
|
|
|
#define INLINE_STACK_SIZE 16
|
|
|
|
/**
|
|
* Run the code with inline stack.
|
|
*
|
|
* @return completion value
|
|
*/
|
|
static ecma_completion_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 completion value
|
|
*/
|
|
static ecma_completion_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 completion value
|
|
*/
|
|
ecma_completion_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;
|
|
|
|
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 */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|