diff --git a/CMakeLists.txt b/CMakeLists.txt index 506d9bcd4f..d5ee9b69bb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2015 Samsung Electronics Co., Ltd. +# Copyright 2015-2016 Samsung Electronics Co., Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -276,7 +276,7 @@ project (Jerry CXX C ASM) endmacro() add_jerry_compile_warnings(all extra format-nonliteral init-self conversion sign-conversion format-security missing-declarations) - add_jerry_compile_flags(-pedantic -Wno-stack-protector -Wno-attributes -Wfatal-errors) + add_jerry_compile_flags(-pedantic -Wno-stack-protector -Wno-attributes) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) add_jerry_compile_warnings(logical-op) else() diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index 2d9141dbd8..901388bba3 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2015 Samsung Electronics Co., Ltd. +# Copyright 2015-2016 Samsung Electronics Co., Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,7 +31,8 @@ project (JerryCore CXX C ASM) OUTPUT_STRIP_TRAILING_WHITESPACE) set(DEFINES_JERRY - JERRY_ENABLE_SNAPSHOT + JERRY_ENABLE_SNAPSHOT_SAVE + JERRY_ENABLE_SNAPSHOT_EXEC JERRY_BUILD_DATE="${JERRY_BUILD_DATE}" JERRY_COMMIT_HASH="${JERRY_GIT_COMMIT}" JERRY_BRANCH_NAME="${JERRY_GIT_BRANCH}") diff --git a/jerry-core/ecma/base/ecma-gc.cpp b/jerry-core/ecma/base/ecma-gc.cpp index 6e22dce57e..fb2ab7cc8f 100644 --- a/jerry-core/ecma/base/ecma-gc.cpp +++ b/jerry-core/ecma/base/ecma-gc.cpp @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ #include "jrt.h" #include "jrt-libc-includes.h" #include "jrt-bit-fields.h" +#include "vm-defines.h" #include "vm-stack.h" #define JERRY_INTERNAL @@ -335,13 +336,11 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ JERRY_UNREACHABLE (); } - case ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS: /* a collection of strings */ case ECMA_INTERNAL_PROPERTY_PRIMITIVE_STRING_VALUE: /* compressed pointer to a ecma_string_t */ case ECMA_INTERNAL_PROPERTY_PRIMITIVE_NUMBER_VALUE: /* compressed pointer to a ecma_number_t */ case ECMA_INTERNAL_PROPERTY_PRIMITIVE_BOOLEAN_VALUE: /* a simple boolean value */ case ECMA_INTERNAL_PROPERTY_CLASS: /* an enum */ case ECMA_INTERNAL_PROPERTY_CODE_BYTECODE: /* compressed pointer to a bytecode array */ - case ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET: /* an integer */ case ECMA_INTERNAL_PROPERTY_NATIVE_CODE: /* an external pointer */ case ECMA_INTERNAL_PROPERTY_NATIVE_HANDLE: /* an external pointer */ case ECMA_INTERNAL_PROPERTY_FREE_CALLBACK: /* an object's native free callback */ @@ -484,25 +483,6 @@ ecma_gc_run (void) } } - /* if some object is referenced from a register variable (i.e. it is root), - * start recursive marking traverse from the object */ - for (vm_stack_frame_t *frame_iter_p = vm_stack_get_top_frame (); - frame_iter_p != NULL; - frame_iter_p = frame_iter_p->prev_frame_p) - { - for (uint32_t reg_index = 0; reg_index < frame_iter_p->regs_number; reg_index++) - { - ecma_value_t reg_value = vm_stack_frame_get_reg_value (frame_iter_p, VM_REG_FIRST + reg_index); - - if (ecma_is_value_object (reg_value)) - { - ecma_object_t *obj_p = ecma_get_object_from_value (reg_value); - - ecma_gc_set_object_visited (obj_p, true); - } - } - } - bool marked_anything_during_current_iteration = false; do diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 3403b23ca5..32490c7195 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,6 +82,7 @@ typedef enum but are stored directly in the array's property list (used for array elements with non-default attribute values) */ ECMA_SIMPLE_VALUE_ARRAY_HOLE, /**< array hole, used for initialization of an array literal */ + ECMA_SIMPLE_VALUE_REGISTER_REF, /**< register reference, a special "base" value for vm */ ECMA_SIMPLE_VALUE__COUNT /** count of simple ecma-values */ } ecma_simple_value_t; @@ -170,21 +171,12 @@ typedef uint32_t ecma_completion_value_t; #define ECMA_COMPLETION_VALUE_VALUE_POS (0) #define ECMA_COMPLETION_VALUE_VALUE_WIDTH (ECMA_VALUE_SIZE) -/** - * Break / continue jump target - */ -#define ECMA_COMPLETION_VALUE_TARGET_POS (0) -#define ECMA_COMPLETION_VALUE_TARGET_WIDTH ((uint32_t) sizeof (vm_instr_counter_t) * JERRY_BITSINBYTE) - /** * Type (ecma_completion_type_t) */ -#define ECMA_COMPLETION_VALUE_TYPE_POS (JERRY_MAX (JERRY_ALIGNUP (ECMA_COMPLETION_VALUE_VALUE_POS + \ +#define ECMA_COMPLETION_VALUE_TYPE_POS (JERRY_ALIGNUP (ECMA_COMPLETION_VALUE_VALUE_POS + \ ECMA_COMPLETION_VALUE_VALUE_WIDTH, \ - JERRY_BITSINBYTE), \ - JERRY_ALIGNUP (ECMA_COMPLETION_VALUE_TARGET_POS + \ - ECMA_COMPLETION_VALUE_TARGET_WIDTH, \ - JERRY_BITSINBYTE))) + JERRY_BITSINBYTE)) #define ECMA_COMPLETION_VALUE_TYPE_WIDTH (8) /** @@ -218,12 +210,9 @@ typedef enum ECMA_INTERNAL_PROPERTY_SCOPE, /**< [[Scope]] */ ECMA_INTERNAL_PROPERTY_PARAMETERS_MAP, /**< [[ParametersMap]] */ ECMA_INTERNAL_PROPERTY_CODE_BYTECODE, /**< first part of [[Code]] - compressed pointer to bytecode array */ - ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET, /**< second part of [[Code]] - offset in bytecode array and code flags - * (see also: ecma_pack_code_internal_property_value) */ ECMA_INTERNAL_PROPERTY_NATIVE_CODE, /**< native handler location descriptor */ ECMA_INTERNAL_PROPERTY_NATIVE_HANDLE, /**< native handle associated with an object */ ECMA_INTERNAL_PROPERTY_FREE_CALLBACK, /**< object's native free callback */ - ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS, /**< [[FormalParameters]] */ ECMA_INTERNAL_PROPERTY_PRIMITIVE_STRING_VALUE, /**< [[Primitive value]] for String objects */ ECMA_INTERNAL_PROPERTY_PRIMITIVE_NUMBER_VALUE, /**< [[Primitive value]] for Number objects */ ECMA_INTERNAL_PROPERTY_PRIMITIVE_BOOLEAN_VALUE, /**< [[Primitive value]] for Boolean objects */ @@ -837,6 +826,22 @@ typedef struct ecma_string_t */ typedef uintptr_t ecma_external_pointer_t; +/** + * Compiled byte code data. + */ +typedef struct +{ + uint16_t status_flags; /**< various status flags */ +} ecma_compiled_code_t; + +/** + * Shift value for byte code reference counting. + * The last 10 bit of the first uint16_t value + * of compact byte code or regexp byte code + * is reserved for reference counting. + */ +#define ECMA_BYTECODE_REF_SHIFT 6 + /** * @} */ diff --git a/jerry-core/ecma/base/ecma-helpers-string.cpp b/jerry-core/ecma/base/ecma-helpers-string.cpp index 5b19d33ae6..0d019e7d3c 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.cpp +++ b/jerry-core/ecma/base/ecma-helpers-string.cpp @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ * @{ */ -#include "bytecode-data.h" #include "ecma-alloc.h" #include "ecma-gc.h" #include "ecma-globals.h" @@ -29,9 +28,10 @@ #include "jrt.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" +#include "lit-literal.h" #include "lit-magic-strings.h" -#include "vm.h" #include "rcs-records.h" +#include "vm.h" /** * Maximum length of strings' concatenation diff --git a/jerry-core/ecma/base/ecma-helpers-value.cpp b/jerry-core/ecma/base/ecma-helpers-value.cpp index 271380bad8..2ed4bd8e83 100644 --- a/jerry-core/ecma/base/ecma-helpers-value.cpp +++ b/jerry-core/ecma/base/ecma-helpers-value.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* Copyright 2015-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,16 +26,16 @@ #include "ecma-helpers.h" #include "jrt.h" #include "jrt-bit-fields.h" +#include "vm-defines.h" JERRY_STATIC_ASSERT (sizeof (ecma_value_t) * JERRY_BITSINBYTE >= ECMA_VALUE_SIZE); -JERRY_STATIC_ASSERT (sizeof (ecma_completion_value_t) * JERRY_BITSINBYTE >= ECMA_COMPLETION_VALUE_SIZE); /** * Get type field of ecma-value * * @return type field */ -static ecma_type_t __attr_pure___ +ecma_type_t __attr_pure___ ecma_get_value_type_field (ecma_value_t value) /**< ecma-value */ { return (ecma_type_t) jrt_extract_bit_field (value, @@ -474,19 +474,6 @@ ecma_get_completion_value_value_field (ecma_completion_value_t completion_value) ECMA_COMPLETION_VALUE_VALUE_WIDTH); } /* ecma_get_completion_value_value_field */ -/** - * Get target of break / continue completion value - * - * @return instruction counter - */ -static vm_instr_counter_t -ecma_get_completion_value_target (ecma_completion_value_t completion_value) /**< completion value */ -{ - return (vm_instr_counter_t) jrt_extract_bit_field (completion_value, - ECMA_COMPLETION_VALUE_TARGET_POS, - ECMA_COMPLETION_VALUE_TARGET_WIDTH); -} /* ecma_get_completion_value_target */ - /** * Set type field of completion value * @@ -519,21 +506,6 @@ ecma_set_completion_value_value_field (ecma_completion_value_t completion_value, ECMA_COMPLETION_VALUE_VALUE_WIDTH); } /* ecma_set_completion_value_value_field */ -/** - * Set target of break / continue completion value - * - * @return completion value with updated field - */ -static ecma_completion_value_t __attr_const___ -ecma_set_completion_value_target (ecma_completion_value_t completion_value, /**< completion value - * to set field in */ - vm_instr_counter_t target) /**< break / continue target */ -{ - return (ecma_completion_value_t) jrt_set_bit_field_value (completion_value, - target, - ECMA_COMPLETION_VALUE_TARGET_POS, - ECMA_COMPLETION_VALUE_TARGET_WIDTH); -} /* ecma_set_completion_value_target */ /** * Normal, throw, return, exit and meta completion values constructor @@ -652,24 +624,6 @@ ecma_make_meta_completion_value (void) ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY)); } /* ecma_make_meta_completion_value */ -/** - * Break / continue completion values constructor - * - * @return completion value - */ -ecma_completion_value_t __attr_const___ -ecma_make_jump_completion_value (vm_instr_counter_t target) /**< target break / continue */ -{ - ecma_completion_value_t completion_value = 0; - - completion_value = ecma_set_completion_value_type_field (completion_value, - ECMA_COMPLETION_TYPE_JUMP); - completion_value = ecma_set_completion_value_target (completion_value, - target); - - return completion_value; -} /* ecma_make_jump_completion_value */ - /** * Get ecma-value from specified completion value * @@ -722,20 +676,6 @@ ecma_get_object_from_completion_value (ecma_completion_value_t completion_value) return ecma_get_object_from_value (ecma_get_completion_value_value (completion_value)); } /* ecma_get_object_from_completion_value */ -/** - * Get break / continue target from completion value - * - * @return instruction counter - */ -vm_instr_counter_t -ecma_get_jump_target_from_completion_value (ecma_completion_value_t completion_value) /**< completion - * value */ -{ - JERRY_ASSERT (ecma_get_completion_value_type_field (completion_value) == ECMA_COMPLETION_TYPE_JUMP); - - return ecma_get_completion_value_target (completion_value); -} /* ecma_get_jump_target_from_completion_value */ - /** * Copy ecma-completion value. * diff --git a/jerry-core/ecma/base/ecma-helpers.cpp b/jerry-core/ecma/base/ecma-helpers.cpp index 8402e1cef0..077b701247 100644 --- a/jerry-core/ecma/base/ecma-helpers.cpp +++ b/jerry-core/ecma/base/ecma-helpers.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. + * Copyright 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. @@ -26,6 +27,8 @@ #include "ecma-helpers.h" #include "ecma-lcache.h" #include "jrt-bit-fields.h" +#include "byte-code.h" +#include "re-compiler.h" /** * Create an object with specified prototype object @@ -763,17 +766,6 @@ ecma_free_internal_property (ecma_property_t *property_p) /**< the property */ break; } - case ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS: /* a strings' collection */ - { - if (property_value != ECMA_NULL_POINTER) - { - ecma_free_values_collection (ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, - property_value), - false); - } - break; - } - case ECMA_INTERNAL_PROPERTY_PRIMITIVE_STRING_VALUE: /* compressed pointer to a ecma_string_t */ { ecma_string_t *str_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, @@ -807,8 +799,6 @@ ecma_free_internal_property (ecma_property_t *property_p) /**< the property */ case ECMA_INTERNAL_PROPERTY_PROTOTYPE: /* the property's value is located in ecma_object_t */ case ECMA_INTERNAL_PROPERTY_EXTENSIBLE: /* the property's value is located in ecma_object_t */ case ECMA_INTERNAL_PROPERTY_CLASS: /* an enum */ - case ECMA_INTERNAL_PROPERTY_CODE_BYTECODE: /* compressed pointer to a bytecode array */ - case ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET: /* an integer */ case ECMA_INTERNAL_PROPERTY_BUILT_IN_ID: /* an integer */ case ECMA_INTERNAL_PROPERTY_BUILT_IN_ROUTINE_DESC: /* an integer */ case ECMA_INTERNAL_PROPERTY_EXTENSION_ID: /* an integer */ @@ -840,15 +830,24 @@ ecma_free_internal_property (ecma_property_t *property_p) /**< the property */ * but number of the real internal property types */ { JERRY_UNREACHABLE (); + break; + } + + case ECMA_INTERNAL_PROPERTY_CODE_BYTECODE: /* compressed pointer to a bytecode array */ + { + ecma_bytecode_deref (ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t, property_value)); + break; } - case ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE: + + case ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE: /* compressed pointer to a regexp bytecode array */ { - void *bytecode_p = ECMA_GET_POINTER (void, property_value); + ecma_compiled_code_t *bytecode_p = ECMA_GET_POINTER (ecma_compiled_code_t, property_value); - if (bytecode_p) + if (bytecode_p != NULL) { - mem_heap_free_block (bytecode_p); + ecma_bytecode_deref (bytecode_p); } + break; } } @@ -1328,6 +1327,90 @@ ecma_get_property_descriptor_from_property (ecma_property_t *prop_p) /**< proper return prop_desc; } /* ecma_get_property_descriptor_from_property */ +/** + * Increase reference counter of Compact + * Byte Code or regexp byte code. + */ +void +ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */ +{ + /* Abort program if maximum reference number is reached. + * Note: This is not tested for objects. */ + if ((bytecode_p->status_flags >> ECMA_BYTECODE_REF_SHIFT) >= 0x3ff) + { + jerry_fatal (ERR_UNIMPLEMENTED_CASE); + } + + bytecode_p->status_flags = (uint16_t) (bytecode_p->status_flags + (1 << ECMA_BYTECODE_REF_SHIFT)); +} /* ecma_bytecode_ref */ + +/** + * Decrease reference counter of Compact + * Byte Code or regexp byte code. + */ +void +ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */ +{ + JERRY_ASSERT ((bytecode_p->status_flags >> ECMA_BYTECODE_REF_SHIFT) > 0); + + bytecode_p->status_flags = (uint16_t) (bytecode_p->status_flags - (1 << ECMA_BYTECODE_REF_SHIFT)); + + if (bytecode_p->status_flags >= (1 << ECMA_BYTECODE_REF_SHIFT)) + { + /* Non-zero reference counter. */ + return; + } + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION) + { + lit_cpointer_t *literal_start_p = NULL; + uint32_t literal_end; + uint32_t const_literal_end; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + uint8_t *byte_p = (uint8_t *) bytecode_p; + literal_start_p = (lit_cpointer_t *) (byte_p + sizeof (cbc_uint16_arguments_t)); + + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_p; + literal_end = args_p->literal_end; + const_literal_end = args_p->const_literal_end; + } + else + { + uint8_t *byte_p = (uint8_t *) bytecode_p; + literal_start_p = (lit_cpointer_t *) (byte_p + sizeof (cbc_uint8_arguments_t)); + + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_p; + literal_end = args_p->literal_end; + const_literal_end = args_p->const_literal_end; + } + + for (uint32_t i = const_literal_end; i < literal_end; i++) + { + mem_cpointer_t bytecode_cpointer = literal_start_p[i].value.base_cp; + ecma_compiled_code_t *bytecode_literal_p = ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t, + bytecode_cpointer); + + /* Self references are ignored. */ + if (bytecode_literal_p != bytecode_p) + { + ecma_bytecode_deref (bytecode_literal_p); + } + } + } + else + { +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN + re_compiled_code_t *re_bytecode_p = (re_compiled_code_t *) bytecode_p; + + ecma_deref_ecma_string (ECMA_GET_NON_NULL_POINTER (ecma_string_t, re_bytecode_p->pattern_cp)); +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ + } + + mem_heap_free_block (bytecode_p); +} /* ecma_bytecode_deref */ + /** * @} * @} diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index da36823ce4..d8a6e2be05 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -1,5 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* Copyright 2014-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. @@ -27,7 +27,6 @@ #include "ecma-globals.h" #include "lit-strings.h" #include "mem-allocator.h" -#include "opcodes.h" /** * Get value of pointer from specified non-null compressed pointer. @@ -53,6 +52,8 @@ #define ECMA_SET_POINTER(field, non_compressed_pointer) MEM_CP_SET_POINTER (field, non_compressed_pointer) /* ecma-helpers-value.cpp */ +extern ecma_type_t ecma_get_value_type_field (ecma_value_t) __attr_pure___; + extern bool ecma_is_value_empty (ecma_value_t); extern bool ecma_is_value_undefined (ecma_value_t); extern bool ecma_is_value_null (ecma_value_t); @@ -84,12 +85,10 @@ extern ecma_completion_value_t ecma_make_throw_obj_completion_value (ecma_object extern ecma_completion_value_t ecma_make_empty_completion_value (void); extern ecma_completion_value_t ecma_make_return_completion_value (ecma_value_t); extern ecma_completion_value_t ecma_make_meta_completion_value (void); -extern ecma_completion_value_t ecma_make_jump_completion_value (vm_instr_counter_t); extern ecma_value_t ecma_get_completion_value_value (ecma_completion_value_t); extern ecma_number_t *ecma_get_number_from_completion_value (ecma_completion_value_t) __attr_const___; extern ecma_string_t *ecma_get_string_from_completion_value (ecma_completion_value_t) __attr_const___; extern ecma_object_t *ecma_get_object_from_completion_value (ecma_completion_value_t) __attr_const___; -extern vm_instr_counter_t ecma_get_jump_target_from_completion_value (ecma_completion_value_t); extern ecma_completion_value_t ecma_copy_completion_value (ecma_completion_value_t); extern void ecma_free_completion_value (ecma_completion_value_t); @@ -254,6 +253,9 @@ extern ecma_property_descriptor_t ecma_make_empty_property_descriptor (void); extern void ecma_free_property_descriptor (ecma_property_descriptor_t *); extern ecma_property_descriptor_t ecma_get_property_descriptor_from_property (ecma_property_t *); +extern void ecma_bytecode_ref (ecma_compiled_code_t *); +extern void ecma_bytecode_deref (ecma_compiled_code_t *); + /* ecma-helpers-external-pointers.cpp */ extern bool ecma_create_external_pointer_property (ecma_object_t *, ecma_internal_property_id_t, ecma_external_pointer_t); diff --git a/jerry-core/ecma/base/ecma-init-finalize.cpp b/jerry-core/ecma/base/ecma-init-finalize.cpp index 8cdb23cf63..f9440d6ba5 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.cpp +++ b/jerry-core/ecma/base/ecma-init-finalize.cpp @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,8 +50,8 @@ ecma_finalize (void) mem_unregister_a_try_give_memory_back_callback (ecma_try_to_give_back_some_memory); ecma_finalize_environment (); - ecma_finalize_builtins (); ecma_lcache_invalidate_all (); + ecma_finalize_builtins (); ecma_gc_run (); } /* ecma_finalize */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp index f3c1349a99..06932ac127 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.cpp @@ -1,5 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -72,7 +72,7 @@ ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this argument else { ecma_string_t *pattern_string_p = NULL; - uint8_t flags = 0; + uint16_t flags = 0; if (ecma_is_value_object (pattern_arg) && ecma_object_get_class_name (ecma_get_object_from_value (pattern_arg)) == LIT_MAGIC_STRING_REGEXP_UL) @@ -125,17 +125,20 @@ ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this argument ecma_property_t *bc_prop_p = ecma_get_internal_property (this_obj_p, ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); - FIXME ("We currently have to re-compile the bytecode, because we can't copy it without knowing its length.") - re_bytecode_t *new_bc_p = NULL; + /* FIXME: "We currently have to re-compile the bytecode, because + * we can't copy it without knowing its length." + */ + re_compiled_code_t *new_bc_p = NULL; ecma_completion_value_t bc_comp = re_compile_bytecode (&new_bc_p, pattern_string_p, flags); /* Should always succeed, since we're compiling from a source that has been compiled previously. */ JERRY_ASSERT (ecma_is_completion_value_empty (bc_comp)); - re_bytecode_t *old_bc_p = ECMA_GET_POINTER (re_bytecode_t, - bc_prop_p->u.internal_property.value); + re_compiled_code_t *old_bc_p = ECMA_GET_POINTER (re_compiled_code_t, + bc_prop_p->u.internal_property.value); if (old_bc_p != NULL) { - mem_heap_free_block (old_bc_p); + /* Free the old bytecode */ + ecma_bytecode_deref ((ecma_compiled_code_t *) old_bc_p); } ECMA_SET_POINTER (bc_prop_p->u.internal_property.value, new_bc_p); @@ -189,17 +192,18 @@ ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this argument ecma_property_t *bc_prop_p = ecma_get_internal_property (this_obj_p, ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); /* Try to compile bytecode from new source. */ - re_bytecode_t *new_bc_p = NULL; - ECMA_TRY_CATCH (bc_dummy, re_compile_bytecode (&new_bc_p, pattern_string_p, flags), ret_value); - + re_compiled_code_t *new_bc_p = NULL; + ECMA_TRY_CATCH (bc_dummy, + re_compile_bytecode (&new_bc_p, pattern_string_p, flags), + ret_value); - re_bytecode_t *old_bc_p = ECMA_GET_POINTER (re_bytecode_t, - bc_prop_p->u.internal_property.value); + re_compiled_code_t *old_bc_p = ECMA_GET_POINTER (re_compiled_code_t, + bc_prop_p->u.internal_property.value); if (old_bc_p != NULL) { - /* Replace old bytecode with new one. */ - mem_heap_free_block (old_bc_p); + /* Free the old bytecode */ + ecma_bytecode_deref ((ecma_compiled_code_t *) old_bc_p); } ECMA_SET_POINTER (bc_prop_p->u.internal_property.value, new_bc_p); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.cpp b/jerry-core/ecma/builtin-objects/ecma-builtins.cpp index b4dcf3c689..6c14540a3e 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.cpp @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -271,7 +271,6 @@ ecma_finalize_builtins (void) if (ecma_builtin_objects[id] != NULL) { ecma_deref_object (ecma_builtin_objects[id]); - ecma_builtin_objects[id] = NULL; } } diff --git a/jerry-core/ecma/operations/ecma-eval.cpp b/jerry-core/ecma/operations/ecma-eval.cpp index 83c2cbff4a..f0da249cac 100644 --- a/jerry-core/ecma/operations/ecma-eval.cpp +++ b/jerry-core/ecma/operations/ecma-eval.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* Copyright 2015-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * limitations under the License. */ -#include "bytecode-data.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-eval.h" @@ -93,17 +92,15 @@ ecma_op_eval_chars_buffer (const jerry_api_char_t *code_p, /**< code characters ecma_completion_value_t completion; - const bytecode_data_header_t *bytecode_data_p; + ecma_compiled_code_t *bytecode_data_p; jsp_status_t parse_status; bool is_strict_call = (is_direct && is_called_from_strict_mode_code); - bool code_contains_functions; parse_status = parser_parse_eval (code_p, code_buffer_size, is_strict_call, - &bytecode_data_p, - &code_contains_functions); + &bytecode_data_p); if (parse_status == JSP_STATUS_SYNTAX_ERROR) { @@ -118,11 +115,6 @@ ecma_op_eval_chars_buffer (const jerry_api_char_t *code_p, /**< code characters JERRY_ASSERT (parse_status == JSP_STATUS_OK); completion = vm_run_eval (bytecode_data_p, is_direct); - - if (!code_contains_functions) - { - bc_remove_bytecode_data (bytecode_data_p); - } } return completion; diff --git a/jerry-core/ecma/operations/ecma-exceptions.h b/jerry-core/ecma/operations/ecma-exceptions.h index bc951b7bfd..ca6090f773 100644 --- a/jerry-core/ecma/operations/ecma-exceptions.h +++ b/jerry-core/ecma/operations/ecma-exceptions.h @@ -35,13 +35,13 @@ */ typedef enum { - ECMA_ERROR_COMMON, /**< Error */ - ECMA_ERROR_EVAL, /**< EvalError */ - ECMA_ERROR_RANGE, /**< RangeError */ + ECMA_ERROR_COMMON, /**< Error */ + ECMA_ERROR_EVAL, /**< EvalError */ + ECMA_ERROR_RANGE, /**< RangeError */ ECMA_ERROR_REFERENCE, /**< ReferenceError */ - ECMA_ERROR_SYNTAX, /**< SyntaxError */ - ECMA_ERROR_TYPE, /**< TypeError */ - ECMA_ERROR_URI /**< URIError */ + ECMA_ERROR_SYNTAX, /**< SyntaxError */ + ECMA_ERROR_TYPE, /**< TypeError */ + ECMA_ERROR_URI /**< URIError */ } ecma_standard_error_t; extern ecma_object_t *ecma_new_standard_error (ecma_standard_error_t); diff --git a/jerry-core/ecma/operations/ecma-function-object.cpp b/jerry-core/ecma/operations/ecma-function-object.cpp index 19d1a2ddfc..904f19d495 100644 --- a/jerry-core/ecma/operations/ecma-function-object.cpp +++ b/jerry-core/ecma/operations/ecma-function-object.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. + * Copyright 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. @@ -13,7 +14,6 @@ * limitations under the License. */ -#include "bytecode-data.h" #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" @@ -37,93 +37,6 @@ * @{ */ -/** - * Pack 'is_strict', 'do_instantiate_arguments_object' flags and instruction position to value - * that can be stored in an [[Code]] internal property. - * - * @return packed value - */ -static uint32_t -ecma_pack_code_internal_property_value (bool is_strict, /**< is code strict? */ - bool do_instantiate_args_obj, /**< should an Arguments object be - * instantiated for the code */ - bool is_arguments_moved_to_regs, /**< values of the function's arguments - * are placed on registers */ - bool is_no_lex_env, /**< the function needs no lexical environment */ - vm_instr_counter_t instr_oc) /**< position of first instruction */ -{ - uint32_t value = instr_oc; - const uint32_t is_strict_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 1); - const uint32_t do_instantiate_arguments_object_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 2); - const uint32_t arguments_moved_to_regs_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 3); - const uint32_t no_lex_env_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 4); - - JERRY_ASSERT (((value) & (1u << is_strict_bit_offset)) == 0); - JERRY_ASSERT (((value) & (1u << do_instantiate_arguments_object_bit_offset)) == 0); - JERRY_ASSERT (((value) & (1u << arguments_moved_to_regs_bit_offset)) == 0); - JERRY_ASSERT (((value) & (1u << no_lex_env_bit_offset)) == 0); - - if (is_strict) - { - value |= (1u << is_strict_bit_offset); - } - - if (do_instantiate_args_obj) - { - value |= (1u << do_instantiate_arguments_object_bit_offset); - } - - if (is_arguments_moved_to_regs) - { - value |= (1u << arguments_moved_to_regs_bit_offset); - } - - if (is_no_lex_env) - { - value |= (1u << no_lex_env_bit_offset); - } - - return value; -} /* ecma_pack_code_internal_property_value */ - -/** - * Unpack 'is_strict', 'do_instantiate_arguments_object' flags and instruction position from value - * that can be stored in an [[Code]] internal property. - * - * @return instruction position - */ -static vm_instr_counter_t -ecma_unpack_code_internal_property_value (uint32_t value, /**< packed value */ - bool* out_is_strict_p, /**< out: is code strict? */ - bool* out_do_instantiate_args_obj_p, /**< should an Arguments object be - * instantiated for the code */ - bool* out_is_arguments_moved_to_regs_p, /**< values of the function's - * arguments are placed - * on registers */ - bool* out_is_no_lex_env_p) /**< the function needs no lexical environment */ -{ - JERRY_ASSERT (out_is_strict_p != NULL); - JERRY_ASSERT (out_do_instantiate_args_obj_p != NULL); - JERRY_ASSERT (out_is_arguments_moved_to_regs_p != NULL); - JERRY_ASSERT (out_is_no_lex_env_p != NULL); - - const uint32_t is_strict_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 1); - const uint32_t do_instantiate_arguments_object_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 2); - const uint32_t is_arguments_moved_to_regs_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 3); - const uint32_t is_no_lex_env_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 4); - - *out_is_strict_p = ((value & (1u << is_strict_bit_offset)) != 0); - *out_do_instantiate_args_obj_p = ((value & (1u << do_instantiate_arguments_object_bit_offset)) != 0); - *out_is_arguments_moved_to_regs_p = ((value & (1u << is_arguments_moved_to_regs_bit_offset)) != 0); - *out_is_no_lex_env_p = ((value & (1u << is_no_lex_env_bit_offset)) != 0); - value &= ~((1u << is_strict_bit_offset) - | (1u << do_instantiate_arguments_object_bit_offset) - | (1u << is_arguments_moved_to_regs_bit_offset) - | (1u << is_no_lex_env_bit_offset)); - - return (vm_instr_counter_t) value; -} /* ecma_unpack_code_internal_property_value */ - /** * IsCallable operation. * @@ -238,49 +151,17 @@ ecma_function_bind_merge_arg_lists (ecma_object_t *func_obj_p, /**< Function obj * @return pointer to newly created Function object */ ecma_object_t* -ecma_op_create_function_object (ecma_collection_header_t *formal_params_collection_p, /**< formal parameters collection - * Warning: - * the collection should not - * be changed / used / freed - * by caller after passing it - * to the routine */ - ecma_object_t *scope_p, /**< function's scope */ +ecma_op_create_function_object (ecma_object_t *scope_p, /**< function's scope */ bool is_decl_in_strict_mode, /**< is function declared in strict mode code? */ - const bytecode_data_header_t *bytecode_header_p, /**< byte-code */ - vm_instr_counter_t first_instr_pos) /**< position of first instruction - * of function's body */ + const ecma_compiled_code_t *bytecode_data_p) /**< byte-code array */ { bool is_strict_mode_code = is_decl_in_strict_mode; - bool do_instantiate_arguments_object = true; - bool is_arguments_moved_to_regs = false; - bool is_no_lex_env = false; - - vm_instr_counter_t instr_pos = first_instr_pos; - if (bytecode_header_p->is_strict) + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) { is_strict_mode_code = true; } - if (!bytecode_header_p->is_ref_arguments_identifier - && !bytecode_header_p->is_ref_eval_identifier) - { - /* the code doesn't use 'arguments' identifier - * and doesn't perform direct call to eval, - * so Arguments object can't be referenced */ - do_instantiate_arguments_object = false; - } - - if (bytecode_header_p->is_args_moved_to_regs) - { - is_arguments_moved_to_regs = true; - } - - if (bytecode_header_p->is_no_lex_env) - { - is_no_lex_env = true; - } - // 1., 4., 13. ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); @@ -306,25 +187,12 @@ ecma_op_create_function_object (ecma_collection_header_t *formal_params_collecti ecma_property_t *scope_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_SCOPE); ECMA_SET_POINTER (scope_prop_p->u.internal_property.value, scope_p); - // 10., 11. - if (formal_params_collection_p != NULL - && formal_params_collection_p->unit_number != 0) - { - ecma_property_t *formal_params_prop_p = ecma_create_internal_property (f, - ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS); - ECMA_SET_POINTER (formal_params_prop_p->u.internal_property.value, formal_params_collection_p); - } - + // 10. + // 11. // 12. ecma_property_t *bytecode_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); - MEM_CP_SET_NON_NULL_POINTER (bytecode_prop_p->u.internal_property.value, bytecode_header_p); - - ecma_property_t *code_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); - code_prop_p->u.internal_property.value = ecma_pack_code_internal_property_value (is_strict_mode_code, - do_instantiate_arguments_object, - is_arguments_moved_to_regs, - is_no_lex_env, - instr_pos); + MEM_CP_SET_NON_NULL_POINTER (bytecode_prop_p->u.internal_property.value, bytecode_data_p); + ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_data_p); // 14. // 15. @@ -446,37 +314,20 @@ ecma_op_function_try_lazy_instantiate_property (ecma_object_t *obj_p, /**< the f // 14 ecma_number_t *len_p = ecma_alloc_number (); - ecma_property_t *formal_parameters_prop_p = ecma_find_internal_property (obj_p, - ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS); - if (formal_parameters_prop_p == NULL) - { - ecma_property_t *bytecode_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); - ecma_property_t *code_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); - - uint32_t code_prop_value = code_prop_p->u.internal_property.value; - - bool is_strict; - bool do_instantiate_args_obj; - bool is_arguments_moved_to_regs; - bool is_no_lex_env; - - const bytecode_data_header_t *bytecode_header_p; - bytecode_header_p = MEM_CP_GET_POINTER (const bytecode_data_header_t, bytecode_prop_p->u.internal_property.value); + ecma_property_t *bytecode_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); - vm_instr_counter_t code_first_instr_pos = ecma_unpack_code_internal_property_value (code_prop_value, - &is_strict, - &do_instantiate_args_obj, - &is_arguments_moved_to_regs, - &is_no_lex_env); + const ecma_compiled_code_t *bytecode_data_p; + bytecode_data_p = MEM_CP_GET_POINTER (const ecma_compiled_code_t, bytecode_prop_p->u.internal_property.value); - *len_p = vm_get_scope_args_num (bytecode_header_p, code_first_instr_pos); + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_data_p; + *len_p = args_p->argument_end; } else { - ecma_collection_header_t *formal_parameters_p; - formal_parameters_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, - formal_parameters_prop_p->u.internal_property.value); - *len_p = ecma_uint32_to_number (formal_parameters_p->unit_number); + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_data_p; + *len_p = args_p->argument_end; } // 15 @@ -619,154 +470,6 @@ ecma_op_create_external_function_object (ecma_external_pointer_t code_p) /**< po return function_obj_p; } /* ecma_op_create_external_function_object */ -/** - * Setup variables for arguments listed in formal parameter list, - * and, if necessary, Arguments object with 'arguments' binding. - * - * See also: - * Declaration binding instantiation (ECMA-262 v5, 10.5), block 4 and 7 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -static ecma_completion_value_t -ecma_function_call_setup_args_variables (ecma_object_t *func_obj_p, /**< Function object */ - ecma_object_t *env_p, /**< lexical environment */ - ecma_collection_header_t *arg_collection_p, /**< arguments collection */ - bool is_strict, /**< flag indicating strict mode */ - bool do_instantiate_arguments_object) /**< flag indicating whether - * Arguments object should be - * instantiated */ -{ - ecma_collection_header_t *formal_parameters_p = NULL; - - ecma_property_t *formal_parameters_prop_p = ecma_find_internal_property (func_obj_p, - ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS); - - if (formal_parameters_prop_p != NULL) - { - formal_parameters_p = ECMA_GET_POINTER (ecma_collection_header_t, - formal_parameters_prop_p->u.internal_property.value); - - ecma_length_t formal_parameters_count = formal_parameters_p->unit_number; - - ecma_collection_iterator_t arguments_iterator, formal_params_iterator; - - ecma_collection_iterator_init (&arguments_iterator, arg_collection_p); - ecma_collection_iterator_init (&formal_params_iterator, formal_parameters_p); - - /* - * Formal parameter list is stored in reversed order - * - * Although, specification defines ascending order of formal parameters list enumeration, - * implementation enumerates the parameters in descending order. - * - * In the case, redundant SetMutableBinding invocation could be avoided. - */ - for (ecma_length_t n = 0; - n < formal_parameters_count; - n++) - { - ecma_value_t arg_value; - if (ecma_collection_iterator_next (&arguments_iterator)) - { - arg_value = *arguments_iterator.current_value_p; - } - else - { - arg_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); - } - - bool is_moved = ecma_collection_iterator_next (&formal_params_iterator); - JERRY_ASSERT (is_moved); - - ecma_value_t formal_parameter_name_value = *formal_params_iterator.current_value_p; - ecma_string_t *formal_parameter_name_string_p = ecma_get_string_from_value (formal_parameter_name_value); - - bool arg_already_declared = ecma_op_has_binding (env_p, formal_parameter_name_string_p); - if (!arg_already_declared) - { - ecma_completion_value_t completion = ecma_op_create_mutable_binding (env_p, - formal_parameter_name_string_p, - false); - if (ecma_is_completion_value_throw (completion)) - { - return completion; - } - - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - } - - ecma_completion_value_t completion = ecma_op_set_mutable_binding (env_p, - formal_parameter_name_string_p, - arg_value, - is_strict); - - if (ecma_is_completion_value_throw (completion)) - { - return completion; - } - - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - } - } - - if (do_instantiate_arguments_object) - { - /* - * According to ECMA-262 v5, 10.5, the Arguments object should be instantiated - * after instantiating declared functions, and only if there is no binding named 'arguments' - * by that time. - * - * However, we can setup Arguments object and 'arguments' binding here, because: - * - instantiation of Arguments object itself doesn't have any side effects; - * - if 'arguments' is name of a declared function in current scope, - * value of the binding would be overwritten, execution would proceed in correct state. - * - declaration of function, named 'arguments', is considered to be unrecommended (and so, rare) case, - * so instantiation of Arguments object here, in general, is supposed to not affect resource consumption - * significantly. - */ - - ecma_string_t *arguments_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_ARGUMENTS); - - bool binding_already_declared = ecma_op_has_binding (env_p, arguments_string_p); - - if (!binding_already_declared) - { - ecma_object_t *args_obj_p = ecma_op_create_arguments_object (func_obj_p, - env_p, - formal_parameters_p, - arg_collection_p, - is_strict); - - if (is_strict) - { - ecma_op_create_immutable_binding (env_p, arguments_string_p); - ecma_op_initialize_immutable_binding (env_p, arguments_string_p, ecma_make_object_value (args_obj_p)); - } - else - { - ecma_completion_value_t completion = ecma_op_create_mutable_binding (env_p, - arguments_string_p, - false); - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - - completion = ecma_op_set_mutable_binding (env_p, - arguments_string_p, - ecma_make_object_value (args_obj_p), - false); - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - } - - ecma_deref_object (args_obj_p); - } - - ecma_deref_ecma_string (arguments_string_p); - } - - return ecma_make_empty_completion_value (); -} /* ecma_function_call_setup_args_variables */ - /** * [[Call]] implementation for Function objects, * created through 13.2 (ECMA_OBJECT_TYPE_FUNCTION) @@ -869,27 +572,116 @@ ecma_op_function_has_instance (ecma_object_t *func_obj_p, /**< Function object * ecma_completion_value_t ecma_op_function_call_array_args (ecma_object_t *func_obj_p, /**< Function object */ ecma_value_t this_arg_value, /**< 'this' argument's value */ - const ecma_value_t* arguments_list_p, /**< arguments list */ + const ecma_value_t *arguments_list_p, /**< arguments list */ ecma_length_t arguments_list_len) /**< length of arguments list */ { if (arguments_list_len == 0) { return ecma_op_function_call (func_obj_p, this_arg_value, NULL); } - else + + if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION + && !ecma_get_object_is_builtin (func_obj_p)) { - ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (arguments_list_p, - arguments_list_len, - true); + /* Fast call. */ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* Entering Function Code (ECMA-262 v5, 10.4.3) */ + ecma_property_t *scope_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_SCOPE); + ecma_property_t *bytecode_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); + + ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, + scope_prop_p->u.internal_property.value); + + // 8. + ecma_value_t this_binding; + bool is_strict; + bool is_no_lex_env; + + const ecma_compiled_code_t *bytecode_data_p; + bytecode_data_p = MEM_CP_GET_POINTER (const ecma_compiled_code_t, + bytecode_prop_p->u.internal_property.value); - ecma_completion_value_t ret_value = ecma_op_function_call (func_obj_p, - this_arg_value, - arg_collection_p); + is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false; + is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false; - ecma_free_values_collection (arg_collection_p, true); + // 1. + if (is_strict) + { + this_binding = ecma_copy_value (this_arg_value, true); + } + else if (ecma_is_value_undefined (this_arg_value) + || ecma_is_value_null (this_arg_value)) + { + // 2. + this_binding = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL)); + } + else + { + // 3., 4. + ecma_completion_value_t completion = ecma_op_to_object (this_arg_value); + JERRY_ASSERT (ecma_is_completion_value_normal (completion)); + + this_binding = ecma_get_completion_value_value (completion); + } + // 5. + ecma_object_t *local_env_p; + if (is_no_lex_env) + { + local_env_p = scope_p; + } + else + { + local_env_p = ecma_create_decl_lex_env (scope_p); + + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_ARGUMENTS_NEEDED) + { + ecma_op_create_arguments_object_array_args (func_obj_p, + local_env_p, + arguments_list_p, + arguments_list_len, + bytecode_data_p); + } + } + + ecma_completion_value_t completion = vm_run_array_args (bytecode_data_p, + this_binding, + local_env_p, + false, + arguments_list_p, + arguments_list_len); + + if (ecma_is_completion_value_return (completion)) + { + ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); + } + else + { + ret_value = completion; + } + + if (!is_no_lex_env) + { + ecma_deref_object (local_env_p); + } + + ecma_free_value (this_binding, true); return ret_value; } + + /* Slow call. */ + + ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (arguments_list_p, + arguments_list_len, + true); + + ecma_completion_value_t ret_value = ecma_op_function_call (func_obj_p, + this_arg_value, + arg_collection_p); + + ecma_free_values_collection (arg_collection_p, true); + return ret_value; } /* ecma_op_function_call_array_args */ /** @@ -926,29 +718,21 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ /* Entering Function Code (ECMA-262 v5, 10.4.3) */ ecma_property_t *scope_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_SCOPE); ecma_property_t *bytecode_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); - ecma_property_t *code_prop_p = ecma_get_internal_property (func_obj_p, - ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, scope_prop_p->u.internal_property.value); - uint32_t code_prop_value = code_prop_p->u.internal_property.value; // 8. + ecma_value_t this_binding; bool is_strict; - bool do_instantiate_args_obj; - bool is_arguments_moved_to_regs; bool is_no_lex_env; - const bytecode_data_header_t *bytecode_data_p; - bytecode_data_p = MEM_CP_GET_POINTER (const bytecode_data_header_t, bytecode_prop_p->u.internal_property.value); + const ecma_compiled_code_t *bytecode_data_p; + bytecode_data_p = MEM_CP_GET_POINTER (const ecma_compiled_code_t, bytecode_prop_p->u.internal_property.value); - vm_instr_counter_t code_first_instr_pos = ecma_unpack_code_internal_property_value (code_prop_value, - &is_strict, - &do_instantiate_args_obj, - &is_arguments_moved_to_regs, - &is_no_lex_env); + is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) ? true : false; + is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) ? true : false; - ecma_value_t this_binding; // 1. if (is_strict) { @@ -978,63 +762,32 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ else { local_env_p = ecma_create_decl_lex_env (scope_p); - } - if (is_arguments_moved_to_regs) - { - ecma_completion_value_t completion = vm_run_from_pos (bytecode_data_p, - code_first_instr_pos, - this_binding, - local_env_p, - is_strict, - false, - arg_collection_p); - - if (ecma_is_completion_value_return (completion)) - { - ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); - } - else + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_ARGUMENTS_NEEDED) { - ret_value = completion; + ecma_op_create_arguments_object (func_obj_p, + local_env_p, + arg_collection_p, + bytecode_data_p); } } - else - { - // 9. - ECMA_TRY_CATCH (args_var_declaration_ret, - ecma_function_call_setup_args_variables (func_obj_p, - local_env_p, - arg_collection_p, - is_strict, - do_instantiate_args_obj), - ret_value); - - ecma_completion_value_t completion = vm_run_from_pos (bytecode_data_p, - code_first_instr_pos, - this_binding, - local_env_p, - is_strict, - false, - NULL); - - if (ecma_is_completion_value_return (completion)) - { - ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); - } - else - { - ret_value = completion; - } - ECMA_FINALIZE (args_var_declaration_ret); - } + ecma_completion_value_t completion = vm_run (bytecode_data_p, + this_binding, + local_env_p, + false, + arg_collection_p); - if (is_no_lex_env) + if (ecma_is_completion_value_return (completion)) { - /* do nothing */ + ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); } else + { + ret_value = completion; + } + + if (!is_no_lex_env) { ecma_deref_object (local_env_p); } @@ -1263,26 +1016,16 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ ecma_completion_value_t ecma_op_function_declaration (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *function_name_p, /**< function name */ - const bytecode_data_header_t *bytecode_data_p, /**< byte-code data */ - vm_instr_counter_t function_first_instr_pos, /**< position of first instruction - * of function code */ - ecma_collection_header_t *formal_params_collection_p, /**< formal parameters collection - * Warning: - * the collection should not - * be changed / used / freed - * by caller after passing it - * to the routine */ + const ecma_compiled_code_t *bytecode_data_p, /**< bytecode data */ bool is_decl_in_strict_mode, /**< flag, indicating if function is * declared in strict mode code */ bool is_configurable_bindings) /**< flag indicating whether function * is declared in eval code */ { // b. - ecma_object_t *func_obj_p = ecma_op_create_function_object (formal_params_collection_p, - lex_env_p, + ecma_object_t *func_obj_p = ecma_op_create_function_object (lex_env_p, is_decl_in_strict_mode, - bytecode_data_p, - function_first_instr_pos); + bytecode_data_p); // c. bool func_already_declared = ecma_op_has_binding (lex_env_p, function_name_p); diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index 7a5398de47..3e84142c59 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +30,7 @@ extern bool ecma_op_is_callable (ecma_value_t); extern bool ecma_is_constructor (ecma_value_t); extern ecma_object_t * -ecma_op_create_function_object (ecma_collection_header_t *, ecma_object_t *, - bool, const bytecode_data_header_t *, vm_instr_counter_t); +ecma_op_create_function_object (ecma_object_t *, bool, const ecma_compiled_code_t *); extern void ecma_op_function_list_lazy_property_names (bool, @@ -59,8 +58,8 @@ extern ecma_completion_value_t ecma_op_function_has_instance (ecma_object_t *, ecma_value_t); extern ecma_completion_value_t -ecma_op_function_declaration (ecma_object_t *, ecma_string_t *, const bytecode_data_header_t *, vm_instr_counter_t, - ecma_collection_header_t *, bool, bool); +ecma_op_function_declaration (ecma_object_t *, ecma_string_t *, + const ecma_compiled_code_t *, bool, bool); /** * @} diff --git a/jerry-core/ecma/operations/ecma-objects-arguments.cpp b/jerry-core/ecma/operations/ecma-objects-arguments.cpp index e48c8f4d59..e1eadc393b 100644 --- a/jerry-core/ecma/operations/ecma-objects-arguments.cpp +++ b/jerry-core/ecma/operations/ecma-objects-arguments.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. + * Copyright 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. @@ -35,33 +36,24 @@ #include "jrt.h" /** - * Arguments object creation operation. + * Arguments object creation common part. * * See also: ECMA-262 v5, 10.6 - * - * @return pointer to newly created Arguments object */ -ecma_object_t* -ecma_op_create_arguments_object (ecma_object_t *func_obj_p, /**< callee function */ - ecma_object_t *lex_env_p, /**< lexical environment the Arguments - object is created for */ - ecma_collection_header_t *formal_params_p, /**< formal parameters collection */ - ecma_collection_header_t *arg_collection_p, /**< arguments collection */ - bool is_strict) /**< flag indicating whether strict mode is enabled */ +static void +ecma_op_create_arguments_object_common (ecma_object_t *obj_p, /**< arguments object */ + ecma_object_t *func_obj_p, /**< callee function */ + ecma_object_t *lex_env_p, /**< lexical environment the Arguments + object is created for */ + ecma_length_t arguments_number, /**< length of arguments list */ + const ecma_compiled_code_t *bytecode_data_p) /**< bytecode data */ { - const ecma_length_t arguments_number = arg_collection_p != NULL ? arg_collection_p->unit_number : 0; + bool is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0; // 1. ecma_number_t *len_p = ecma_alloc_number (); *len_p = ecma_uint32_to_number (arguments_number); - // 2., 3., 6. - 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); - - ecma_deref_object (prototype_p); - // 4. ecma_property_t *class_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CLASS); class_prop_p->u.internal_property.value = LIT_MAGIC_STRING_ARGUMENTS_UL; @@ -81,39 +73,29 @@ ecma_op_create_arguments_object (ecma_object_t *func_obj_p, /**< callee function ecma_dealloc_number (len_p); - // 11.a, 11.b - ecma_collection_iterator_t args_iterator; - ecma_collection_iterator_init (&args_iterator, arg_collection_p); + ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); - for (ecma_length_t indx = 0; - indx < arguments_number; - indx++) + if (bytecode_data_p != NULL) { - bool is_moved = ecma_collection_iterator_next (&args_iterator); - JERRY_ASSERT (is_moved); - - ecma_string_t *indx_string_p = ecma_new_ecma_string_from_uint32 (indx); - completion = ecma_builtin_helper_def_prop (obj_p, - indx_string_p, - *args_iterator.current_value_p, - true, /* Writable */ - true, /* Enumerable */ - true, /* Configurable */ - false); /* Failure handling */ - - JERRY_ASSERT (ecma_is_completion_value_normal_true (completion)); - - ecma_deref_ecma_string (indx_string_p); - } + ecma_length_t formal_params_number; + lit_cpointer_t *literal_p; - ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_data_p; + uint8_t *byte_p = (uint8_t *) bytecode_data_p; - if (formal_params_p != NULL) - { - const ecma_length_t formal_params_number = formal_params_p->unit_number; + formal_params_number = args_p->argument_end; + literal_p = (lit_cpointer_t *) (byte_p + sizeof (cbc_uint16_arguments_t)); + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_data_p; + uint8_t *byte_p = (uint8_t *) bytecode_data_p; - ecma_collection_iterator_t formal_params_iterator; - ecma_collection_iterator_init (&formal_params_iterator, formal_params_p); + formal_params_number = args_p->argument_end; + literal_p = (lit_cpointer_t *) (byte_p + sizeof (cbc_uint8_arguments_t)); + } if (!is_strict && arguments_number > 0 @@ -123,68 +105,35 @@ ecma_op_create_arguments_object (ecma_object_t *func_obj_p, /**< callee function ecma_object_t *map_p = ecma_op_create_object_object_noarg (); // 11.c - MEM_DEFINE_LOCAL_ARRAY (formal_params, formal_params_number, ecma_string_t *); - - JERRY_ASSERT (formal_params_iterator.current_value_p == NULL); - - uint32_t param_index; - for (param_index = 0; - ecma_collection_iterator_next (&formal_params_iterator); - param_index++) - { - JERRY_ASSERT (formal_params_iterator.current_value_p != NULL); - JERRY_ASSERT (param_index < formal_params_number); - - JERRY_ASSERT (ecma_is_value_string (*formal_params_iterator.current_value_p)); - ecma_string_t *param_name_p = ecma_get_string_from_value (*formal_params_iterator.current_value_p); - - formal_params[param_index] = param_name_p; - } - JERRY_ASSERT (param_index == formal_params_number); - - for (int32_t indx = (int32_t) formal_params_number - 1; - indx >= 0; - indx--) + for (uint32_t indx = 0; + indx < formal_params_number; + indx++) { // i. - ecma_string_t *name_p = formal_params[indx]; - bool is_first_occurence = true; - - // ii. - for (int32_t indx2 = indx + 1; - indx2 < (int32_t) formal_params_number; - indx2++) + if (literal_p[indx].packed_value == MEM_CP_NULL) { - if (ecma_compare_ecma_strings (name_p, formal_params[indx2])) - { - is_first_occurence = false; - - break; - } + continue; } - if (is_first_occurence) - { - ecma_string_t *indx_string_p = ecma_new_ecma_string_from_uint32 ((uint32_t) indx); + ecma_string_t *name_p = ecma_new_ecma_string_from_lit_cp (literal_p[indx]); + ecma_string_t *indx_string_p = ecma_new_ecma_string_from_uint32 ((uint32_t) indx); - prop_desc.is_value_defined = true; - prop_desc.value = ecma_make_string_value (name_p); + prop_desc.is_value_defined = true; + prop_desc.value = ecma_make_string_value (name_p); - prop_desc.is_configurable_defined = true; - prop_desc.is_configurable = true; + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = true; - completion = ecma_op_object_define_own_property (map_p, - indx_string_p, - &prop_desc, - false); - JERRY_ASSERT (ecma_is_completion_value_normal_true (completion)); + completion = ecma_op_object_define_own_property (map_p, + indx_string_p, + &prop_desc, + false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (completion)); - ecma_deref_ecma_string (indx_string_p); - } + ecma_deref_ecma_string (indx_string_p); + ecma_deref_ecma_string (name_p); } - MEM_FINALIZE_LOCAL_ARRAY (formal_params); - // 12. ecma_set_object_type (obj_p, ECMA_OBJECT_TYPE_ARGUMENTS); @@ -264,9 +213,138 @@ ecma_op_create_arguments_object (ecma_object_t *func_obj_p, /**< callee function ecma_deref_object (thrower_p); } - return obj_p; + ecma_string_t *arguments_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_ARGUMENTS); + + if (is_strict) + { + ecma_op_create_immutable_binding (lex_env_p, arguments_string_p); + ecma_op_initialize_immutable_binding (lex_env_p, + arguments_string_p, + ecma_make_object_value (obj_p)); + } + else + { + ecma_completion_value_t completion = ecma_op_create_mutable_binding (lex_env_p, + arguments_string_p, + false); + JERRY_ASSERT (ecma_is_completion_value_empty (completion)); + + completion = ecma_op_set_mutable_binding (lex_env_p, + arguments_string_p, + ecma_make_object_value (obj_p), + false); + + JERRY_ASSERT (ecma_is_completion_value_empty (completion)); + } + + ecma_deref_ecma_string (arguments_string_p); + ecma_deref_object (obj_p); +} /* ecma_op_create_arguments_object_common */ + +/** + * Arguments object creation operation. + * + * See also: ECMA-262 v5, 10.6 + * + * @return pointer to newly created Arguments object + */ +void +ecma_op_create_arguments_object (ecma_object_t *func_obj_p, /**< callee function */ + ecma_object_t *lex_env_p, /**< lexical environment the Arguments + object is created for */ + ecma_collection_header_t *arg_collection_p, /**< arguments collection */ + const ecma_compiled_code_t *bytecode_data_p) /**< byte code */ +{ + const ecma_length_t arguments_number = arg_collection_p != NULL ? arg_collection_p->unit_number : 0; + + // 2., 3., 6. + 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); + + ecma_deref_object (prototype_p); + + // 11.a, 11.b + ecma_collection_iterator_t args_iterator; + ecma_collection_iterator_init (&args_iterator, arg_collection_p); + + for (ecma_length_t indx = 0; + indx < arguments_number; + indx++) + { + ecma_completion_value_t completion; + bool is_moved = ecma_collection_iterator_next (&args_iterator); + JERRY_ASSERT (is_moved); + + ecma_string_t *indx_string_p = ecma_new_ecma_string_from_uint32 (indx); + completion = ecma_builtin_helper_def_prop (obj_p, + indx_string_p, + *args_iterator.current_value_p, + true, /* Writable */ + true, /* Enumerable */ + true, /* Configurable */ + false); /* Failure handling */ + + JERRY_ASSERT (ecma_is_completion_value_normal_true (completion)); + + ecma_deref_ecma_string (indx_string_p); + } + + ecma_op_create_arguments_object_common (obj_p, + func_obj_p, + lex_env_p, + arguments_number, + bytecode_data_p); } /* ecma_op_create_arguments_object */ +/** + * Arguments object creation operation. + * + * See also: ECMA-262 v5, 10.6 + */ +void +ecma_op_create_arguments_object_array_args (ecma_object_t *func_obj_p, /**< callee function */ + ecma_object_t *lex_env_p, /**< lexical environment the Arguments + object is created for */ + const ecma_value_t *arguments_list_p, /**< arguments list */ + ecma_length_t arguments_number, /**< length of arguments list */ + const ecma_compiled_code_t *bytecode_data_p) /**< byte code */ +{ + // 2., 3., 6. + 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); + + ecma_deref_object (prototype_p); + + // 11.a, 11.b + for (ecma_length_t indx = 0; + indx < arguments_number; + indx++) + { + ecma_completion_value_t completion; + ecma_string_t *indx_string_p = ecma_new_ecma_string_from_uint32 (indx); + + completion = ecma_builtin_helper_def_prop (obj_p, + indx_string_p, + arguments_list_p[indx], + true, /* Writable */ + true, /* Enumerable */ + true, /* Configurable */ + false); /* Failure handling */ + + JERRY_ASSERT (ecma_is_completion_value_normal_true (completion)); + + ecma_deref_ecma_string (indx_string_p); + } + + ecma_op_create_arguments_object_common (obj_p, + func_obj_p, + lex_env_p, + arguments_number, + bytecode_data_p); +} /* ecma_op_create_arguments_object_array_args */ + /** * Get value of function's argument mapped to index of Arguments object. * diff --git a/jerry-core/ecma/operations/ecma-objects-arguments.h b/jerry-core/ecma/operations/ecma-objects-arguments.h index f0bb2b0244..e9cf08e516 100644 --- a/jerry-core/ecma/operations/ecma-objects-arguments.h +++ b/jerry-core/ecma/operations/ecma-objects-arguments.h @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,13 @@ #include "ecma-globals.h" #include "ecma-helpers.h" -extern ecma_object_t * +extern void ecma_op_create_arguments_object (ecma_object_t *, ecma_object_t *, - ecma_collection_header_t *, ecma_collection_header_t *, bool); + ecma_collection_header_t *, const ecma_compiled_code_t *); + +extern void +ecma_op_create_arguments_object_array_args (ecma_object_t *, ecma_object_t *, const ecma_value_t *, + ecma_length_t, const ecma_compiled_code_t *); extern ecma_completion_value_t ecma_op_arguments_object_get (ecma_object_t *, ecma_string_t *); diff --git a/jerry-core/ecma/operations/ecma-regexp-object.cpp b/jerry-core/ecma/operations/ecma-regexp-object.cpp index 7bdd9bc989..06f803579a 100644 --- a/jerry-core/ecma/operations/ecma-regexp-object.cpp +++ b/jerry-core/ecma/operations/ecma-regexp-object.cpp @@ -1,5 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -65,7 +65,7 @@ */ ecma_completion_value_t re_parse_regexp_flags (ecma_string_t *flags_str_p, /**< Input string with flags */ - uint8_t *flags_p) /**< Output: parsed flag bits */ + uint16_t *flags_p) /**< Output: parsed flag bits */ { ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); @@ -129,7 +129,7 @@ re_parse_regexp_flags (ecma_string_t *flags_str_p, /**< Input string with flags void re_initialize_props (ecma_object_t *re_obj_p, /**< RegExp obejct */ ecma_string_t *source_p, /**< source string */ - uint8_t flags) /**< flags */ + uint16_t flags) /**< flags */ { /* Set source property. ECMA-262 v5, 15.10.7.1 */ ecma_string_t *magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_SOURCE); @@ -218,6 +218,42 @@ re_initialize_props (ecma_object_t *re_obj_p, /**< RegExp obejct */ ecma_dealloc_number (lastindex_num_p); } /* re_initialize_props */ +/** + * RegExp object creation operation. + * + * See also: ECMA-262 v5, 15.10.4.1 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value + */ +ecma_value_t +ecma_op_create_regexp_object_from_bytecode (re_compiled_code_t *bytecode_p) /**< input pattern */ +{ + JERRY_ASSERT (bytecode_p != NULL); + + ecma_object_t *re_prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_PROTOTYPE); + + ecma_object_t *obj_p = ecma_create_object (re_prototype_obj_p, true, ECMA_OBJECT_TYPE_GENERAL); + ecma_deref_object (re_prototype_obj_p); + + /* Set the internal [[Class]] property */ + ecma_property_t *class_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CLASS); + class_prop_p->u.internal_property.value = LIT_MAGIC_STRING_REGEXP_UL; + + /* Set bytecode internal property. */ + ecma_property_t *bytecode_prop_p; + bytecode_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); + ECMA_SET_NON_NULL_POINTER (bytecode_prop_p->u.internal_property.value, bytecode_p); + ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_p); + + /* Initialize RegExp object properties */ + re_initialize_props (obj_p, + ECMA_GET_NON_NULL_POINTER (ecma_string_t, bytecode_p->pattern_cp), + bytecode_p->flags); + + return ecma_make_object_value (obj_p); +} /* ecma_op_create_regexp_object_from_bytecode */ + /** * RegExp object creation operation. * @@ -232,7 +268,7 @@ ecma_op_create_regexp_object (ecma_string_t *pattern_p, /**< input pattern */ { JERRY_ASSERT (pattern_p != NULL); ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - uint8_t flags = 0; + uint16_t flags = 0; if (flags_str_p != NULL) { @@ -261,7 +297,7 @@ ecma_op_create_regexp_object (ecma_string_t *pattern_p, /**< input pattern */ bytecode_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); /* Compile bytecode. */ - re_bytecode_t *bc_p = NULL; + re_compiled_code_t *bc_p = NULL; ECMA_TRY_CATCH (empty, re_compile_bytecode (&bc_p, pattern_p, flags), ret_value); ECMA_SET_POINTER (bytecode_prop_p->u.internal_property.value, bc_p); @@ -336,7 +372,7 @@ re_canonicalize (ecma_char_t ch, /**< character */ */ static ecma_completion_value_t re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */ - re_bytecode_t *bc_p, /**< pointer to the current RegExp bytecode */ + uint8_t *bc_p, /**< pointer to the current RegExp bytecode */ lit_utf8_byte_t *str_p, /**< input string pointer */ lit_utf8_byte_t **out_str_p) /**< Output: matching substring iterator */ { @@ -651,7 +687,7 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */ } case RE_OP_SAVE_AT_START: { - re_bytecode_t *old_bc_p; + uint8_t *old_bc_p; JERRY_DDLOG ("Execute RE_OP_SAVE_AT_START\n"); lit_utf8_byte_t *old_start_p = re_ctx_p->saved_p[RE_GLOBAL_START_IDX]; @@ -717,7 +753,7 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */ uint32_t start_idx, iter_idx, offset; lit_utf8_byte_t *old_start_p = NULL; lit_utf8_byte_t *sub_str_p = NULL; - re_bytecode_t *old_bc_p; + uint8_t *old_bc_p; old_bc_p = bc_p; /* save the bytecode start position of the group start */ start_idx = re_get_value (&bc_p); @@ -770,8 +806,8 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */ { uint32_t start_idx, iter_idx, old_iteration_cnt, offset; lit_utf8_byte_t *sub_str_p = NULL; - re_bytecode_t *old_bc_p; - re_bytecode_t *end_bc_p = NULL; + uint8_t *old_bc_p; + uint8_t *end_bc_p = NULL; start_idx = re_get_value (&bc_p); if (op != RE_OP_CAPTURE_GROUP_START @@ -844,7 +880,7 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */ case RE_OP_NON_CAPTURE_NON_GREEDY_GROUP_END: { uint32_t end_idx, iter_idx, min, max; - re_bytecode_t *old_bc_p; + uint8_t *old_bc_p; /* * On non-greedy iterations we have to execute the bytecode @@ -904,7 +940,7 @@ re_match_regexp (re_matcher_ctx_t *re_ctx_p, /**< RegExp matcher context */ lit_utf8_byte_t *old_start_p = NULL; lit_utf8_byte_t *old_end_p = NULL; lit_utf8_byte_t *sub_str_p = NULL; - re_bytecode_t *old_bc_p; + uint8_t *old_bc_p; end_idx = re_get_value (&bc_p); min = re_get_value (&bc_p); @@ -1217,7 +1253,8 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */ ecma_property_t *bytecode_prop_p = ecma_get_internal_property (regexp_object_p, ECMA_INTERNAL_PROPERTY_REGEXP_BYTECODE); - re_bytecode_t *bc_p = ECMA_GET_POINTER (re_bytecode_t, bytecode_prop_p->u.internal_property.value); + re_compiled_code_t *bc_p = ECMA_GET_POINTER (re_compiled_code_t, + bytecode_prop_p->u.internal_property.value); ecma_string_t *input_string_p = ecma_get_string_from_value (input_string); lit_utf8_size_t input_string_size = ecma_string_get_size (input_string_p); @@ -1240,11 +1277,11 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */ re_ctx.input_end_p = input_buffer_p + input_string_size; /* 1. Read bytecode header and init regexp matcher context. */ - re_ctx.flags = (uint8_t) re_get_value (&bc_p); + re_ctx.flags = bc_p->flags; if (ignore_global) { - re_ctx.flags &= (uint8_t) ~RE_FLAG_GLOBAL; + re_ctx.flags &= (uint16_t) ~RE_FLAG_GLOBAL; } JERRY_DDLOG ("Exec with flags [global: %d, ignoreCase: %d, multiline: %d]\n", @@ -1252,10 +1289,9 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */ re_ctx.flags & RE_FLAG_IGNORE_CASE, re_ctx.flags & RE_FLAG_MULTILINE); - re_ctx.num_of_captures = re_get_value (&bc_p); + re_ctx.num_of_captures = bc_p->num_of_captures; JERRY_ASSERT (re_ctx.num_of_captures % 2 == 0); - re_ctx.num_of_non_captures = re_get_value (&bc_p); - + re_ctx.num_of_non_captures = bc_p->num_of_non_captures; MEM_DEFINE_LOCAL_ARRAY (saved_p, re_ctx.num_of_captures + re_ctx.num_of_non_captures, lit_utf8_byte_t *); @@ -1301,6 +1337,7 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */ /* 2. Try to match */ lit_utf8_byte_t *sub_str_p = NULL; + uint8_t *bc_start_p = (uint8_t *) (bc_p + 1); while (ecma_is_completion_value_empty (ret_value)) { @@ -1321,7 +1358,12 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */ } else { - ECMA_TRY_CATCH (match_value, re_match_regexp (&re_ctx, bc_p, input_curr_p, &sub_str_p), ret_value); + ECMA_TRY_CATCH (match_value, re_match_regexp (&re_ctx, + bc_start_p, + input_curr_p, + &sub_str_p), + ret_value); + if (ecma_is_value_true (match_value)) { is_match = true; @@ -1346,7 +1388,7 @@ ecma_regexp_exec_helper (ecma_value_t regexp_value, /**< RegExp object */ if (sub_str_p) { *lastindex_num_p = lit_utf8_string_length (input_buffer_p, - (lit_utf8_size_t) (sub_str_p - input_buffer_p)); + (lit_utf8_size_t) (sub_str_p - input_buffer_p)); } else { diff --git a/jerry-core/ecma/operations/ecma-regexp-object.h b/jerry-core/ecma/operations/ecma-regexp-object.h index ce315f535a..1f1bbaef91 100644 --- a/jerry-core/ecma/operations/ecma-regexp-object.h +++ b/jerry-core/ecma/operations/ecma-regexp-object.h @@ -1,5 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -32,9 +32,9 @@ /** * RegExp flags */ -#define RE_FLAG_GLOBAL (1 << 0) /* ECMA-262 v5, 15.10.7.2 */ -#define RE_FLAG_IGNORE_CASE (1 << 1) /* ECMA-262 v5, 15.10.7.3 */ -#define RE_FLAG_MULTILINE (1 << 2) /* ECMA-262 v5, 15.10.7.4 */ +#define RE_FLAG_GLOBAL (1 << 1) /* ECMA-262 v5, 15.10.7.2 */ +#define RE_FLAG_IGNORE_CASE (1 << 2) /* ECMA-262 v5, 15.10.7.3 */ +#define RE_FLAG_MULTILINE (1 << 3) /* ECMA-262 v5, 15.10.7.4 */ /** * RegExp executor context @@ -47,9 +47,12 @@ typedef struct uint32_t num_of_captures; /**< number of capture groups */ uint32_t num_of_non_captures; /**< number of non-capture groups */ uint32_t *num_of_iterations_p; /**< number of iterations */ - uint8_t flags; /**< RegExp flags */ + uint16_t flags; /**< RegExp flags */ } re_matcher_ctx_t; +extern ecma_value_t +ecma_op_create_regexp_object_from_bytecode (re_compiled_code_t *); + extern ecma_completion_value_t ecma_op_create_regexp_object (ecma_string_t *, ecma_string_t *); @@ -62,10 +65,10 @@ extern void re_set_result_array_properties (ecma_object_t *, ecma_string_t *, uint32_t, int32_t); extern ecma_completion_value_t -re_parse_regexp_flags (ecma_string_t *, uint8_t *); +re_parse_regexp_flags (ecma_string_t *, uint16_t *); extern void -re_initialize_props (ecma_object_t *, ecma_string_t *, uint8_t); +re_initialize_props (ecma_object_t *, ecma_string_t *, uint16_t); /** * @} diff --git a/jerry-core/jerry-internal.h b/jerry-core/jerry-internal.h index 0de4897671..bd81bcec74 100644 --- a/jerry-core/jerry-internal.h +++ b/jerry-core/jerry-internal.h @@ -32,21 +32,4 @@ jerry_dispatch_object_free_callback (ecma_external_pointer_t, ecma_external_poin extern bool jerry_is_abort_on_fail (void); -/** - * Snapshot header - */ -typedef struct -{ - uint32_t lit_table_size; /**< size of literal table */ - uint32_t scopes_num; /**< number of saved bytecode pieces in the snapshot */ - uint32_t is_run_global : 1; /**< flag, indicating whether the snapshot - * was dumped as 'Global scope'-mode code (true) - * or as eval-mode code (false) */ -} jerry_snapshot_header_t; - -/** - * Jerry snapshot format version - */ -#define JERRY_SNAPSHOT_VERSION (2u) - #endif /* !JERRY_INTERNAL_H */ diff --git a/jerry-core/jerry-snapshot.h b/jerry-core/jerry-snapshot.h new file mode 100644 index 0000000000..5b35af3bf5 --- /dev/null +++ b/jerry-core/jerry-snapshot.h @@ -0,0 +1,50 @@ +/* Copyright 2016 University of Szeged. + * Copyright 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JERRY_SNAPSHOT_H +#define JERRY_SNAPSHOT_H + +#include "ecma-globals.h" + +/** + * Snapshot header + */ +typedef struct +{ + uint32_t last_compiled_code_offset; /**< offset of the last compiled code */ + uint32_t lit_table_size; /**< size of literal table */ + uint32_t is_run_global : 1; /**< flag, indicating whether the snapshot + * was dumped as 'Global scope'-mode code (true) + * or as eval-mode code (false) */ +} jerry_snapshot_header_t; + +/** + * Jerry snapshot format version + */ +#define JERRY_SNAPSHOT_VERSION (3u) + +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + +/* Snapshot support functions */ + +extern bool snapshot_report_byte_code_compilation; + +extern void +snapshot_add_compiled_code (ecma_compiled_code_t *, const uint8_t *, uint32_t); + +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ + +#endif /* !JERRY_SNAPSHOT_H */ diff --git a/jerry-core/jerry.cpp b/jerry-core/jerry.cpp index c75f713fdc..343a9b60ba 100644 --- a/jerry-core/jerry.cpp +++ b/jerry-core/jerry.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* Copyright 2015-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ #include -#include "bytecode-data.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-builtins.h" @@ -28,8 +27,12 @@ #include "ecma-objects.h" #include "ecma-objects-general.h" #include "ecma-try-catch-macro.h" +#include "jerry-snapshot.h" +#include "lit-literal.h" #include "lit-magic-strings.h" +#include "lit-snapshot.h" #include "parser.h" +#include "re-compiler.h" #define JERRY_INTERNAL #include "jerry-internal.h" @@ -685,7 +688,7 @@ jerry_api_create_string (const jerry_api_char_t *v) /**< string value */ */ jerry_api_string_t * jerry_api_create_string_sz (const jerry_api_char_t *v, /**< string value */ - jerry_api_size_t v_size) + jerry_api_size_t v_size) /**< string size */ { jerry_assert_api_available (); @@ -1125,9 +1128,9 @@ jerry_api_delete_object_field (jerry_api_object_t *object_p, /**< object to dele * - there is field with specified name in the object; * false - otherwise. */ -bool jerry_api_get_object_field_value (jerry_api_object_t *object_p, - const jerry_api_char_t *field_name_p, - jerry_api_value_t *field_value_p) +bool jerry_api_get_object_field_value (jerry_api_object_t *object_p, /**< object */ + const jerry_api_char_t *field_name_p, /**< field name */ + jerry_api_value_t *field_value_p) /**< out: field value */ { return jerry_api_get_object_field_value_sz (object_p, field_name_p, @@ -1672,9 +1675,8 @@ jerry_cleanup (void) ecma_finalize (); lit_finalize (); - bc_finalize (); - mem_finalize (is_show_mem_stats); vm_finalize (); + mem_finalize (is_show_mem_stats); } /* jerry_cleanup */ /** @@ -1724,11 +1726,11 @@ jerry_parse (const jerry_api_char_t* source_p, /**< script source */ { jerry_assert_api_available (); - bool is_show_instructions = ((jerry_flags & JERRY_FLAG_SHOW_OPCODES) != 0); + int is_show_instructions = ((jerry_flags & JERRY_FLAG_SHOW_OPCODES) != 0); parser_set_show_instrs (is_show_instructions); - const bytecode_data_header_t *bytecode_data_p; + ecma_compiled_code_t *bytecode_data_p; jsp_status_t parse_status; parse_status = parser_parse_script (source_p, @@ -1865,6 +1867,212 @@ jerry_register_external_magic_strings (const jerry_api_char_ptr_t* ex_str_items, lit_magic_strings_ex_set ((const lit_utf8_byte_t **) ex_str_items, count, (const lit_utf8_size_t *) str_lengths); } /* jerry_register_external_magic_strings */ +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + +/* + * Tells whether snapshot taking is in progress. + */ +bool snapshot_report_byte_code_compilation = false; + +/* + * Mapping snapshot to offset. + */ +typedef struct +{ + mem_cpointer_t next_cp; + mem_cpointer_t compiled_code_cp; + uint16_t offset; +} compiled_code_map_entry_t; + +/* + * Variables required to take a snapshot. + */ +static size_t snapshot_buffer_write_offset; +static size_t snapshot_last_compiled_code_offset; +static bool snapshot_error_occured; +static uint8_t *snapshot_buffer_p; +static size_t snapshot_buffer_size; +static compiled_code_map_entry_t *snapshot_map_entries_p; + +/** + * Snapshot callback for byte codes. + */ +void +snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ + const uint8_t *regexp_pattern, /**< regular expression pattern */ + uint32_t size) /**< compiled code or regular expression size */ +{ + if (snapshot_error_occured) + { + return; + } + + JERRY_ASSERT ((snapshot_buffer_write_offset & (MEM_ALIGNMENT - 1)) == 0); + + if ((snapshot_buffer_write_offset >> MEM_ALIGNMENT_LOG) > 0xffffu) + { + snapshot_error_occured = true; + return; + } + + snapshot_last_compiled_code_offset = snapshot_buffer_write_offset; + + compiled_code_map_entry_t *new_entry = (compiled_code_map_entry_t *) mem_pools_alloc (); + + if (new_entry == NULL) + { + snapshot_error_occured = true; + return; + } + + ECMA_SET_POINTER (new_entry->next_cp, snapshot_map_entries_p); + ECMA_SET_POINTER (new_entry->compiled_code_cp, compiled_code_p); + + new_entry->offset = (uint16_t) (snapshot_buffer_write_offset >> MEM_ALIGNMENT_LOG); + snapshot_map_entries_p = new_entry; + + const void *data_p = (const void *) compiled_code_p; + + if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FUNCTION)) + { + size += (uint32_t) sizeof (uint16_t); + } + else + { + JERRY_ASSERT (regexp_pattern == NULL); + } + + if (!jrt_write_to_buffer_by_offset (snapshot_buffer_p, + snapshot_buffer_size, + &snapshot_buffer_write_offset, + &size, + sizeof (uint32_t))) + { + snapshot_error_occured = true; + return; + } + + if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FUNCTION)) + { + size -= (uint32_t) sizeof (uint16_t); + + if (!jrt_write_to_buffer_by_offset (snapshot_buffer_p, + snapshot_buffer_size, + &snapshot_buffer_write_offset, + &compiled_code_p->status_flags, + sizeof (uint16_t))) + { + snapshot_error_occured = true; + return; + } + + data_p = regexp_pattern; + } + + if (!jrt_write_to_buffer_by_offset (snapshot_buffer_p, + snapshot_buffer_size, + &snapshot_buffer_write_offset, + data_p, + size)) + { + snapshot_error_occured = true; + return; + } + + snapshot_buffer_write_offset = JERRY_ALIGNUP (snapshot_buffer_write_offset, MEM_ALIGNMENT); + + if (snapshot_buffer_write_offset > snapshot_buffer_size) + { + snapshot_error_occured = true; + return; + } +} /* snapshot_add_compiled_code */ + +/** + * Set the uint16_t offsets in the code area. + */ +static void +jerry_snapshot_set_offsets (uint8_t *buffer_p, /**< buffer */ + uint32_t size, /**< buffer size */ + lit_mem_to_snapshot_id_map_entry_t *lit_map_p) /**< literal map */ +{ + JERRY_ASSERT (size > 0); + + do + { + uint32_t code_size = *(uint32_t *) buffer_p; + + buffer_p += sizeof (uint32_t); + + ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p; + + /* Set reference counter to 1. */ + bytecode_p->status_flags &= (1 << ECMA_BYTECODE_REF_SHIFT) - 1; + bytecode_p->status_flags |= 1 << ECMA_BYTECODE_REF_SHIFT; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION) + { + lit_cpointer_t *literal_start_p; + uint32_t literal_end; + uint32_t const_literal_end; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + literal_start_p = (lit_cpointer_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); + + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; + literal_end = args_p->literal_end; + const_literal_end = args_p->const_literal_end; + } + else + { + literal_start_p = (lit_cpointer_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); + + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; + literal_end = args_p->literal_end; + const_literal_end = args_p->const_literal_end; + } + + for (uint32_t i = 0; i < const_literal_end; i++) + { + lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; + + if (literal_start_p[i].packed_value != MEM_CP_NULL) + { + while (current_p->literal_id.packed_value != literal_start_p[i].packed_value) + { + current_p++; + } + + literal_start_p[i].packed_value = (uint16_t) current_p->literal_offset; + } + } + + for (uint32_t i = const_literal_end; i < literal_end; i++) + { + compiled_code_map_entry_t *current_p = snapshot_map_entries_p; + + while (current_p->compiled_code_cp != literal_start_p[i].value.base_cp) + { + current_p = ECMA_GET_NON_NULL_POINTER (compiled_code_map_entry_t, + current_p->next_cp); + } + + literal_start_p[i].packed_value = (uint16_t) current_p->offset; + } + } + + code_size += (uint32_t) sizeof (uint32_t); + code_size = JERRY_ALIGNUP (code_size, MEM_ALIGNMENT); + + buffer_p += code_size - sizeof (uint32_t); + size -= code_size; + } + while (size > 0); +} /* jerry_snapshot_set_offsets */ + +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ + /** * Generate snapshot from specified source * @@ -1881,98 +2089,123 @@ jerry_parse_and_save_snapshot (const jerry_api_char_t* source_p, /**< script sou uint8_t *buffer_p, /**< buffer to dump snapshot to */ size_t buffer_size) /**< the buffer's size */ { -#ifdef JERRY_ENABLE_SNAPSHOT +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE jsp_status_t parse_status; - const bytecode_data_header_t *bytecode_data_p; - - if (is_for_global) - { - parse_status = parser_parse_script (source_p, source_size, &bytecode_data_p); - } - else - { - bool code_contains_functions; - - parse_status = parser_parse_eval (source_p, - source_size, - false, - &bytecode_data_p, - &code_contains_functions); - } - - if (parse_status != JSP_STATUS_OK) - { - return 0; - } + ecma_compiled_code_t *bytecode_data_p; - size_t buffer_write_offset = 0; - - jerry_snapshot_header_t header; - header.is_run_global = is_for_global; + snapshot_buffer_write_offset = 0; + snapshot_last_compiled_code_offset = 0; + snapshot_error_occured = false; + snapshot_buffer_p = buffer_p; + snapshot_buffer_size = buffer_size; + snapshot_map_entries_p = NULL; uint64_t version = JERRY_SNAPSHOT_VERSION; if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, - &buffer_write_offset, + &snapshot_buffer_write_offset, &version, sizeof (version))) { return 0; } - size_t header_offset = buffer_write_offset; + snapshot_buffer_write_offset = JERRY_ALIGNUP (snapshot_buffer_write_offset, MEM_ALIGNMENT); + + size_t header_offset = snapshot_buffer_write_offset; + + snapshot_buffer_write_offset += JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), MEM_ALIGNMENT); - if (buffer_write_offset + JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), MEM_ALIGNMENT) > buffer_size) + if (snapshot_buffer_write_offset > buffer_size) { return 0; } - buffer_write_offset += JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), MEM_ALIGNMENT); - lit_mem_to_snapshot_id_map_entry_t* lit_map_p = NULL; - uint32_t literals_num; + size_t compiled_code_start = snapshot_buffer_write_offset; - if (!lit_dump_literals_for_snapshot (buffer_p, - buffer_size, - &buffer_write_offset, - &lit_map_p, - &literals_num, - &header.lit_table_size)) + snapshot_report_byte_code_compilation = true; + + if (is_for_global) { - JERRY_ASSERT (lit_map_p == NULL); - return 0; + parse_status = parser_parse_script (source_p, source_size, &bytecode_data_p); + } + else + { + parse_status = parser_parse_eval (source_p, + source_size, + false, + &bytecode_data_p); } - size_t bytecode_offset = (sizeof (version) - + JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), MEM_ALIGNMENT) - + header.lit_table_size); + snapshot_report_byte_code_compilation = false; + + if (parse_status == JSP_STATUS_OK + && !snapshot_error_occured) + { + JERRY_ASSERT (snapshot_last_compiled_code_offset != 0); - JERRY_ASSERT (JERRY_ALIGNUP (bytecode_offset, MEM_ALIGNMENT) == bytecode_offset); + jerry_snapshot_header_t header; + header.last_compiled_code_offset = (uint32_t) snapshot_last_compiled_code_offset; + header.is_run_global = is_for_global; - bool is_ok = bc_save_bytecode_data (buffer_p, - buffer_size, - &buffer_write_offset, - bytecode_data_p, - lit_map_p, - literals_num, - &header.scopes_num); + size_t compiled_code_size = snapshot_buffer_write_offset - compiled_code_start; - JERRY_ASSERT (header.scopes_num != 0); + lit_mem_to_snapshot_id_map_entry_t* lit_map_p = NULL; + uint32_t literals_num; - if (lit_map_p != NULL) + if (!lit_dump_literals_for_snapshot (buffer_p, + buffer_size, + &snapshot_buffer_write_offset, + &lit_map_p, + &literals_num, + &header.lit_table_size)) + { + JERRY_ASSERT (lit_map_p == NULL); + snapshot_buffer_write_offset = 0; + } + else + { + if (header.lit_table_size > 0xffff) + { + /* Aligning literals could increase this range, but + * it is not a requirement for low-memory environments. */ + snapshot_buffer_write_offset = 0; + } + else + { + jerry_snapshot_set_offsets (buffer_p + compiled_code_start, + (uint32_t) compiled_code_size, + lit_map_p); + + jrt_write_to_buffer_by_offset (buffer_p, + buffer_size, + &header_offset, + &header, + sizeof (header)); + } + + mem_heap_free_block (lit_map_p); + } + + ecma_bytecode_deref (bytecode_data_p); + } + else { - mem_heap_free_block (lit_map_p); + snapshot_buffer_write_offset = 0; } - if (!is_ok) + compiled_code_map_entry_t *current_p = snapshot_map_entries_p; + + while (current_p != NULL) { - return 0; + compiled_code_map_entry_t *next_p = ECMA_GET_POINTER (compiled_code_map_entry_t, + current_p->next_cp); + mem_pools_free ((uint8_t *) current_p); + current_p = next_p; } - is_ok = jrt_write_to_buffer_by_offset (buffer_p, buffer_size, &header_offset, &header, sizeof (header)); - JERRY_ASSERT (is_ok && header_offset < buffer_write_offset); - - return buffer_write_offset; -#else /* JERRY_ENABLE_SNAPSHOT */ + return snapshot_buffer_write_offset; +#else /* JERRY_ENABLE_SNAPSHOT_SAVE */ (void) source_p; (void) source_size; (void) is_for_global; @@ -1980,9 +2213,144 @@ jerry_parse_and_save_snapshot (const jerry_api_char_t* source_p, /**< script sou (void) buffer_size; return 0; -#endif /* !JERRY_ENABLE_SNAPSHOT */ +#endif /* !JERRY_ENABLE_SNAPSHOT_SAVE */ } /* jerry_parse_and_save_snapshot */ +#ifdef JERRY_ENABLE_SNAPSHOT_EXEC + +/** + * Load byte code from snapshot. + * + * @return byte code + */ +static ecma_compiled_code_t * +snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data */ + size_t offset, /**< byte code offset */ + lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< literal map */ + bool copy_bytecode) /**< byte code should be copied to memory */ +{ + uint32_t code_size = *(uint32_t *) (snapshot_data_p + offset); + + ecma_compiled_code_t *bytecode_p; + + bytecode_p = (ecma_compiled_code_t *) (snapshot_data_p + offset + sizeof (uint32_t)); + + if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION)) + { +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN + re_compiled_code_t *re_bytecode_p = NULL; + + const uint8_t *regex_start_p = ((const uint8_t *) bytecode_p) + sizeof (uint16_t); + + code_size -= (uint32_t) sizeof (uint16_t); + + ecma_string_t *pattern_str_p = ecma_new_ecma_string_from_utf8 (regex_start_p, + code_size); + + re_compile_bytecode (&re_bytecode_p, + pattern_str_p, + bytecode_p->status_flags); + + ecma_deref_ecma_string (pattern_str_p); + + return (ecma_compiled_code_t *) re_bytecode_p; +#else + JERRY_UNIMPLEMENTED ("RegExp is not supported in compact profile."); +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ + } + + size_t header_size; + uint32_t literal_end; + uint32_t const_literal_end; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + uint8_t *byte_p = (uint8_t *) bytecode_p; + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) byte_p; + literal_end = args_p->literal_end; + const_literal_end = args_p->const_literal_end; + header_size = sizeof (cbc_uint16_arguments_t); + } + else + { + uint8_t *byte_p = (uint8_t *) bytecode_p; + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) byte_p; + literal_end = args_p->literal_end; + const_literal_end = args_p->const_literal_end; + header_size = sizeof (cbc_uint8_arguments_t); + } + + if (copy_bytecode) + { + bytecode_p = (ecma_compiled_code_t *) mem_heap_alloc_block (code_size, + MEM_HEAP_ALLOC_LONG_TERM); + + memcpy (bytecode_p, snapshot_data_p + offset + sizeof (uint32_t), code_size); + } + else + { + code_size = (uint32_t) (header_size + literal_end * sizeof (lit_cpointer_t)); + + uint8_t *real_bytecode_p = ((uint8_t *) bytecode_p) + code_size; + + bytecode_p = (ecma_compiled_code_t *) mem_heap_alloc_block (code_size + 1 + sizeof (uint8_t *), + MEM_HEAP_ALLOC_LONG_TERM); + + memcpy (bytecode_p, snapshot_data_p + offset + sizeof (uint32_t), code_size); + + uint8_t *instructions_p = ((uint8_t *) bytecode_p); + + instructions_p[code_size] = CBC_SET_BYTECODE_PTR; + memcpy (instructions_p + code_size + 1, &real_bytecode_p, sizeof (uint8_t *)); + } + + JERRY_ASSERT ((bytecode_p->status_flags >> ECMA_BYTECODE_REF_SHIFT) == 1); + + lit_cpointer_t *literal_start_p = (lit_cpointer_t *) (((uint8_t *) bytecode_p) + header_size); + + for (uint32_t i = 0; i < const_literal_end; i++) + { + lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; + + if (literal_start_p[i].packed_value != 0) + { + while (current_p->literal_offset != literal_start_p[i].packed_value) + { + current_p++; + } + + literal_start_p[i] = current_p->literal_id; + } + } + + for (uint32_t i = const_literal_end; i < literal_end; i++) + { + size_t literal_offset = ((size_t) literal_start_p[i].packed_value) << MEM_ALIGNMENT_LOG; + + if (literal_offset == offset) + { + /* Self reference */ + ECMA_SET_NON_NULL_POINTER (literal_start_p[i].value.base_cp, + bytecode_p); + } + else + { + ecma_compiled_code_t *literal_bytecode_p; + literal_bytecode_p = snapshot_load_compiled_code (snapshot_data_p, + literal_offset, + lit_map_p, + copy_bytecode); + + ECMA_SET_NON_NULL_POINTER (literal_start_p[i].value.base_cp, + literal_bytecode_p); + } + } + + return bytecode_p; +} /* snapshot_load_compiled_code */ + +#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ + /** * Execute snapshot from specified buffer * @@ -1991,19 +2359,19 @@ jerry_parse_and_save_snapshot (const jerry_api_char_t* source_p, /**< script sou jerry_completion_code_t jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ size_t snapshot_size, /**< size of snapshot */ - bool is_copy, /**< flag, indicating whether the passed snapshot - * buffer should be copied to engine's memory, - * so engine should not reference the buffer - * after the function returns (in the case, the passed - * buffer could be freed after the call); - * otherwise (if flag not set) - the buffer could be freed - * only after engine stops (i.e. after call to jerry_cleanup). */ - jerry_api_value_t *retval_p) /**< out: returned value (ECMA-262 'undefined' if code is executed - * as global scope code) */ + bool copy_bytecode, /**< flag, indicating whether the passed snapshot + * buffer should be copied to the engine's memory. + * If set the engine should not reference the buffer + * after the function returns (in this case, the passed + * buffer could be freed after the call). + * Otherwise (if the flag is not set) - the buffer could only be + * freed after the engine stops (i.e. after call to jerry_cleanup). */ + jerry_api_value_t *retval_p) /**< out: returned value (ECMA-262 'undefined' if + * code is executed as global scope code) */ { jerry_api_convert_ecma_value_to_api_value (retval_p, ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); -#ifdef JERRY_ENABLE_SNAPSHOT +#ifdef JERRY_ENABLE_SNAPSHOT_EXEC JERRY_ASSERT (snapshot_p != NULL); const uint8_t *snapshot_data_p = (uint8_t *) snapshot_p; @@ -2025,21 +2393,20 @@ jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ } const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) (snapshot_data_p + snapshot_read); - if (snapshot_read + JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), MEM_ALIGNMENT) > snapshot_size) - { - return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; - } - snapshot_read += JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), MEM_ALIGNMENT); + snapshot_read = header_p->last_compiled_code_offset; - if (snapshot_read + header_p->lit_table_size > snapshot_size) - { - return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; - } + JERRY_ASSERT (snapshot_read == JERRY_ALIGNUP (snapshot_read, MEM_ALIGNMENT)); + + uint32_t last_code_size = *(uint32_t *) (snapshot_data_p + snapshot_read); + + snapshot_read += JERRY_ALIGNUP (last_code_size + sizeof (uint32_t), MEM_ALIGNMENT); lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL; uint32_t literals_num; + JERRY_ASSERT (snapshot_read + header_p->lit_table_size <= snapshot_size); + if (!lit_load_literals_from_snapshot (snapshot_data_p + snapshot_read, header_p->lit_table_size, &lit_map_p, @@ -2049,28 +2416,21 @@ jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; } - snapshot_read += header_p->lit_table_size; + ecma_compiled_code_t *bytecode_p; - if (snapshot_read > snapshot_size) - { - mem_heap_free_block (lit_map_p); - return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; - } + snapshot_read = header_p->last_compiled_code_offset; - const bytecode_data_header_t *bytecode_data_p; - bytecode_data_p = bc_load_bytecode_data (snapshot_data_p + snapshot_read, - snapshot_size - snapshot_read, - lit_map_p, - literals_num, - is_copy, - header_p->scopes_num); + bytecode_p = snapshot_load_compiled_code (snapshot_data_p, + snapshot_read, + lit_map_p, + copy_bytecode); if (lit_map_p != NULL) { mem_heap_free_block (lit_map_p); } - if (bytecode_data_p == NULL) + if (bytecode_p == NULL) { return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; } @@ -2079,7 +2439,7 @@ jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ if (header_p->is_run_global) { - vm_init (bytecode_data_p, false); + vm_init (bytecode_p, false); ret_code = vm_run_global (); @@ -2088,7 +2448,7 @@ jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ else { /* vm should be already initialized */ - ecma_completion_value_t completion = vm_run_eval (bytecode_data_p, false); + ecma_completion_value_t completion = vm_run_eval (bytecode_p, false); ret_code = jerry_api_convert_eval_completion_to_retval (retval_p, completion); @@ -2096,12 +2456,12 @@ jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ } return ret_code; -#else /* JERRY_ENABLE_SNAPSHOT */ +#else /* JERRY_ENABLE_SNAPSHOT_EXEC */ (void) snapshot_p; (void) snapshot_size; - (void) is_copy; + (void) copy_bytecode; (void) retval_p; return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_VERSION; -#endif /* !JERRY_ENABLE_SNAPSHOT */ +#endif /* !JERRY_ENABLE_SNAPSHOT_EXEC */ } /* jerry_exec_snapshot */ diff --git a/jerry-core/jrt/jrt.h b/jerry-core/jrt/jrt.h index f8da01a02c..5a23227b1d 100644 --- a/jerry-core/jrt/jrt.h +++ b/jerry-core/jrt/jrt.h @@ -268,7 +268,7 @@ jrt_write_to_buffer_by_offset (uint8_t *buffer_p, /**< buffer */ size_t buffer_size, /**< size of buffer */ size_t *in_out_buffer_offset_p, /**< in: offset to read from, * out: offset, incremented on sizeof (T) */ - void *data_p, /**< data */ + const void *data_p, /**< data */ size_t data_size) /**< size of the writable data */ { if (*in_out_buffer_offset_p + data_size > buffer_size) diff --git a/jerry-core/lit/lit-snapshot.cpp b/jerry-core/lit/lit-snapshot.cpp index 7adb3ff050..38e0f3f1e5 100644 --- a/jerry-core/lit/lit-snapshot.cpp +++ b/jerry-core/lit/lit-snapshot.cpp @@ -22,7 +22,7 @@ #include "rcs-iterator.h" #include "rcs-records.h" -#ifdef JERRY_ENABLE_SNAPSHOT +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE /** * Dump a record to specified snapshot buffer. @@ -250,6 +250,10 @@ lit_dump_literals_for_snapshot (uint8_t *buffer_p, /**< output snapshot buffer * return true; } /* lit_dump_literals_for_snapshot */ +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ + +#ifdef JERRY_ENABLE_SNAPSHOT_EXEC + /** * Load literals from snapshot. * @@ -408,4 +412,4 @@ lit_load_literals_from_snapshot (const uint8_t *lit_table_p, /**< buffer with li return false; } /* rcs_load_literals_from_snapshot */ -#endif /* JERRY_ENABLE_SNAPSHOT */ +#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ diff --git a/jerry-core/lit/lit-snapshot.h b/jerry-core/lit/lit-snapshot.h index ca466f1bb8..61f8c6f9db 100644 --- a/jerry-core/lit/lit-snapshot.h +++ b/jerry-core/lit/lit-snapshot.h @@ -25,7 +25,7 @@ typedef struct uint32_t literal_offset; } lit_mem_to_snapshot_id_map_entry_t; -#ifdef JERRY_ENABLE_SNAPSHOT +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE extern bool lit_dump_literals_for_snapshot (uint8_t *, size_t, @@ -33,12 +33,14 @@ lit_dump_literals_for_snapshot (uint8_t *, lit_mem_to_snapshot_id_map_entry_t **, uint32_t *, uint32_t *); +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ +#ifdef JERRY_ENABLE_SNAPSHOT_EXEC extern bool lit_load_literals_from_snapshot (const uint8_t *, uint32_t, lit_mem_to_snapshot_id_map_entry_t **, uint32_t *); -#endif /* JERRY_ENABLE_SNAPSHOT */ +#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ #endif /* !RCS_SNAPSHOT_H */ diff --git a/jerry-core/lit/lit-strings.cpp b/jerry-core/lit/lit-strings.cpp index 9a53be2bf4..2bf608b109 100644 --- a/jerry-core/lit/lit-strings.cpp +++ b/jerry-core/lit/lit-strings.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* Copyright 2015-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -277,12 +277,12 @@ lit_utf8_iterator_seek_bos (lit_utf8_iterator_t *iter_p) /**< iterator to reset } /* lit_utf8_iterator_seek_bos */ /** - * Save iterator's position to restore it later + * Get iterator's position * * @return current position of the iterator */ lit_utf8_iterator_pos_t -lit_utf8_iterator_get_pos (const lit_utf8_iterator_t *iter_p) +lit_utf8_iterator_get_pos (const lit_utf8_iterator_t *iter_p) /**< iterator */ { return iter_p->buf_pos; } /* lit_utf8_iterator_get_pos */ @@ -586,7 +586,7 @@ lit_read_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with cha */ lit_utf8_size_t lit_read_prev_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ - ecma_char_t *code_point) /**< @out: code point */ + ecma_char_t *code_point) /**< @out: code point */ { JERRY_ASSERT (buf_p); @@ -635,7 +635,7 @@ lit_utf8_read_prev (lit_utf8_byte_t **buf_p) /**< in-out:buffer with characters * @return next code unit */ ecma_char_t -lit_utf8_peek_next (lit_utf8_byte_t *buf_p) /**< in-out:buffer with characters */ +lit_utf8_peek_next (const lit_utf8_byte_t *buf_p) /**< in-out:buffer with characters */ { JERRY_ASSERT (buf_p); ecma_char_t ch; @@ -651,7 +651,7 @@ lit_utf8_peek_next (lit_utf8_byte_t *buf_p) /**< in-out:buffer with characters * * @return previous code unit */ ecma_char_t -lit_utf8_peek_prev (lit_utf8_byte_t *buf_p) /**< in-out:buffer with characters */ +lit_utf8_peek_prev (const lit_utf8_byte_t *buf_p) /**< in-out:buffer with characters */ { JERRY_ASSERT (buf_p); ecma_char_t ch; diff --git a/jerry-core/lit/lit-strings.h b/jerry-core/lit/lit-strings.h index f40ca34506..bb101d0a42 100644 --- a/jerry-core/lit/lit-strings.h +++ b/jerry-core/lit/lit-strings.h @@ -1,4 +1,4 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* Copyright 2015-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -183,8 +183,8 @@ lit_utf8_size_t lit_read_prev_code_unit_from_utf8 (const lit_utf8_byte_t *, ecma_char_t lit_utf8_read_next (lit_utf8_byte_t **); ecma_char_t lit_utf8_read_prev (lit_utf8_byte_t **); -ecma_char_t lit_utf8_peek_next (lit_utf8_byte_t *); -ecma_char_t lit_utf8_peek_prev (lit_utf8_byte_t *); +ecma_char_t lit_utf8_peek_next (const lit_utf8_byte_t *); +ecma_char_t lit_utf8_peek_prev (const lit_utf8_byte_t *); void lit_utf8_incr (lit_utf8_byte_t **); void lit_utf8_decr (lit_utf8_byte_t **); diff --git a/jerry-core/parser/js/bc/bytecode-data.cpp b/jerry-core/parser/js/bc/bytecode-data.cpp deleted file mode 100644 index 524bbdc390..0000000000 --- a/jerry-core/parser/js/bc/bytecode-data.cpp +++ /dev/null @@ -1,834 +0,0 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 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 "bytecode-data.h" -#include "pretty-printer.h" -#include "opcodes-dumper.h" - -/** - * First node of the list of bytecodes - */ -static bytecode_data_header_t *first_bytecode_header_p = NULL; - -/** - * Bytecode header in snapshot - */ -typedef struct -{ - uint32_t size; /**< size of this bytecode data record */ - uint32_t instrs_size; /**< size of instructions array */ - uint32_t idx_to_lit_map_size; /**< size of idx-to-lit map */ - uint32_t func_scopes_count; /**< count of function scopes inside current scope */ - uint32_t var_decls_count; /**< count of variable declarations insdie current scope */ - - uint8_t is_strict : 1; /**< code is strict mode code */ - uint8_t is_ref_arguments_identifier : 1; /**< code doesn't reference 'arguments' identifier */ - uint8_t is_ref_eval_identifier : 1; /**< code doesn't reference 'eval' identifier */ - uint8_t is_args_moved_to_regs : 1; /**< the function's arguments are moved to registers, - * so should be initialized in vm registers, - * and not in lexical environment */ - uint8_t is_no_lex_env : 1; /**< no lex. env. is necessary for the scope */ -} jerry_snapshot_bytecode_header_t; - -/** - * Fill the fields of bytecode data header with specified values - */ -static void -bc_fill_bytecode_data_header (bytecode_data_header_t *bc_header_p, /**< byte-code scope data header to fill */ - lit_id_hash_table *lit_id_hash_table_p, /**< (idx, block id) -> literal hash table */ - vm_instr_t *bytecode_p, /**< byte-code instructions array */ - mem_cpointer_t *declarations_p, /**< array of function / variable declarations */ - uint16_t func_scopes_count, /**< number of function declarations / expressions - * located immediately in the corresponding scope */ - uint16_t var_decls_count, /**< number of variable declarations immediately in the scope */ - bool is_strict, /**< is the scope's code strict mode code? */ - bool is_ref_arguments_identifier, /**< does the scope's code - * reference 'arguments' identifier? */ - bool is_ref_eval_identifier, /**< does the scope's code - * reference 'eval' identifier? */ - bool is_vars_and_args_to_regs_possible, /**< is it scope, for which variables / arguments - * can be moved to registers */ - bool is_arguments_moved_to_regs, /**< is it function scope, for which arguments - * are located on registers, not in variables? */ - bool is_no_lex_env) /**< is lexical environment unused in the scope? */ - -{ - MEM_CP_SET_POINTER (bc_header_p->lit_id_hash_cp, lit_id_hash_table_p); - bc_header_p->instrs_p = bytecode_p; - bc_header_p->instrs_count = 0; - MEM_CP_SET_POINTER (bc_header_p->declarations_cp, declarations_p); - bc_header_p->func_scopes_count = func_scopes_count; - bc_header_p->var_decls_count = var_decls_count; - bc_header_p->next_header_cp = MEM_CP_NULL; - - bc_header_p->is_strict = is_strict; - bc_header_p->is_ref_arguments_identifier = is_ref_arguments_identifier; - bc_header_p->is_ref_eval_identifier = is_ref_eval_identifier; - bc_header_p->is_vars_and_args_to_regs_possible = is_vars_and_args_to_regs_possible; - bc_header_p->is_args_moved_to_regs = is_arguments_moved_to_regs; - bc_header_p->is_no_lex_env = is_no_lex_env; -} /* bc_fill_bytecode_data_header */ - -/** - * Free memory occupied by bytecode data - */ -static void -bc_free_bytecode_data (bytecode_data_header_t *bytecode_data_p) /**< byte-code scope data header */ -{ - bytecode_data_header_t *next_to_handle_list_p = bytecode_data_p; - - while (next_to_handle_list_p != NULL) - { - bytecode_data_header_t *bc_header_list_iter_p = next_to_handle_list_p; - next_to_handle_list_p = NULL; - - while (bc_header_list_iter_p != NULL) - { - bytecode_data_header_t *header_p = bc_header_list_iter_p; - - bc_header_list_iter_p = MEM_CP_GET_POINTER (bytecode_data_header_t, header_p->next_header_cp); - - mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, header_p->declarations_cp); - - for (uint32_t index = 0; index < header_p->func_scopes_count; index++) - { - bytecode_data_header_t *child_scope_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, - declarations_p[index]); - JERRY_ASSERT (child_scope_header_p->next_header_cp == MEM_CP_NULL); - - MEM_CP_SET_POINTER (child_scope_header_p->next_header_cp, next_to_handle_list_p); - - next_to_handle_list_p = child_scope_header_p; - } - - mem_heap_free_block (header_p); - } - - JERRY_ASSERT (bc_header_list_iter_p == NULL); - } -} /* bc_free_bytecode_data */ - -/** - * Delete bytecode and associated hash table - */ -void -bc_remove_bytecode_data (const bytecode_data_header_t *bytecode_data_p) /**< byte-code scope data header */ -{ - bytecode_data_header_t *prev_header_p = NULL; - bytecode_data_header_t *cur_header_p = first_bytecode_header_p; - - while (cur_header_p != NULL) - { - if (cur_header_p == bytecode_data_p) - { - if (prev_header_p) - { - prev_header_p->next_header_cp = cur_header_p->next_header_cp; - } - else - { - first_bytecode_header_p = MEM_CP_GET_POINTER (bytecode_data_header_t, cur_header_p->next_header_cp); - } - - cur_header_p->next_header_cp = MEM_CP_NULL; - - bc_free_bytecode_data (cur_header_p); - - break; - } - - prev_header_p = cur_header_p; - cur_header_p = MEM_CP_GET_POINTER (bytecode_data_header_t, cur_header_p->next_header_cp); - } -} /* bc_remove_bytecode_data */ - -vm_instr_t bc_get_instr (const bytecode_data_header_t *bytecode_data_p, /**< byte-code scope data header */ - vm_instr_counter_t oc) /**< instruction position */ -{ - JERRY_ASSERT (oc < bytecode_data_p->instrs_count); - return bytecode_data_p->instrs_p[oc]; -} - -/** - * Print bytecode instructions - */ -void -bc_print_instrs (const bytecode_data_header_t *bytecode_data_p) /**< byte-code scope data header */ -{ -#ifdef JERRY_ENABLE_PRETTY_PRINTER - for (vm_instr_counter_t loc = 0; loc < bytecode_data_p->instrs_count; loc++) - { - op_meta opm; - - opm.op = bytecode_data_p->instrs_p[loc]; - for (int i = 0; i < 3; i++) - { - opm.lit_id[i] = NOT_A_LITERAL; - } - - pp_op_meta (bytecode_data_p, loc, opm, false); - } -#else - (void) bytecode_data_p; -#endif -} /* bc_print_instrs */ - -/** - * Dump single scopes tree into bytecode - * - * @return pointer to bytecode header of the outer most scope - */ -bytecode_data_header_t * -bc_dump_single_scope (scopes_tree scope_p) /**< a node of scopes tree */ -{ - const size_t entries_count = scope_p->max_uniq_literals_num; - const vm_instr_counter_t instrs_count = scopes_tree_instrs_num (scope_p); - const size_t blocks_count = JERRY_ALIGNUP (instrs_count, BLOCK_SIZE) / BLOCK_SIZE; - const size_t func_scopes_count = scopes_tree_child_scopes_num (scope_p); - const uint16_t var_decls_count = linked_list_get_length (scope_p->var_decls); - const size_t bytecode_size = JERRY_ALIGNUP (instrs_count * sizeof (vm_instr_t), MEM_ALIGNMENT); - const size_t hash_table_size = lit_id_hash_table_get_size_for_table (entries_count, blocks_count); - const size_t declarations_area_size = JERRY_ALIGNUP (func_scopes_count * sizeof (mem_cpointer_t) - + var_decls_count * sizeof (lit_cpointer_t), - MEM_ALIGNMENT); - const size_t header_and_tables_size = JERRY_ALIGNUP ((sizeof (bytecode_data_header_t) - + hash_table_size - + declarations_area_size), - MEM_ALIGNMENT); - - uint8_t *buffer_p = (uint8_t *) mem_heap_alloc_block (bytecode_size + header_and_tables_size, - MEM_HEAP_ALLOC_LONG_TERM); - - lit_id_hash_table *lit_id_hash_p = lit_id_hash_table_init (buffer_p + sizeof (bytecode_data_header_t), - hash_table_size, - entries_count, blocks_count); - - mem_cpointer_t *declarations_p = (mem_cpointer_t *) (buffer_p + sizeof (bytecode_data_header_t) + hash_table_size); - - for (size_t i = 0; i < func_scopes_count; i++) - { - declarations_p[i] = MEM_CP_NULL; - } - - scopes_tree_dump_var_decls (scope_p, (lit_cpointer_t *) (declarations_p + func_scopes_count)); - - vm_instr_t *bytecode_p = (vm_instr_t *) (buffer_p + header_and_tables_size); - - JERRY_ASSERT (scope_p->max_uniq_literals_num >= lit_id_hash_p->current_bucket_pos); - - bytecode_data_header_t *header_p = (bytecode_data_header_t *) buffer_p; - - if ((uint16_t) func_scopes_count != func_scopes_count) - { - jerry_fatal (ERR_OUT_OF_MEMORY); - } - - bc_fill_bytecode_data_header (header_p, - lit_id_hash_p, bytecode_p, - declarations_p, - (uint16_t) func_scopes_count, - var_decls_count, - scope_p->strict_mode, - scope_p->ref_arguments, - scope_p->ref_eval, - scope_p->is_vars_and_args_to_regs_possible, - false, - false); - - JERRY_ASSERT (scope_p->bc_header_cp == MEM_CP_NULL); - MEM_CP_SET_NON_NULL_POINTER (scope_p->bc_header_cp, header_p); - - return header_p; -} /* bc_dump_single_scope */ - -void -bc_register_root_bytecode_header (bytecode_data_header_t *bc_header_p) -{ - MEM_CP_SET_POINTER (bc_header_p->next_header_cp, first_bytecode_header_p); - first_bytecode_header_p = bc_header_p; -} /* bc_register_root_bytecode_header */ - -/** - * Free all bytecode data which was allocated - */ -void -bc_finalize (void) -{ - while (first_bytecode_header_p != NULL) - { - bytecode_data_header_t *header_p = first_bytecode_header_p; - first_bytecode_header_p = MEM_CP_GET_POINTER (bytecode_data_header_t, header_p->next_header_cp); - - header_p->next_header_cp = MEM_CP_NULL; - - bc_free_bytecode_data (header_p); - } -} /* bc_finalize */ - -/** - * Convert literal id (operand value of instruction) to compressed pointer to literal - * - * Bytecode is divided into blocks of fixed size and each block has independent encoding of variable names, - * which are represented by 8 bit numbers - ids. - * This function performs conversion from id to literal. - * - * @return compressed pointer to literal - */ -lit_cpointer_t -bc_get_literal_cp_by_uid (uint8_t id, /**< literal idx */ - const bytecode_data_header_t *bytecode_data_p, /**< pointer to bytecode */ - vm_instr_counter_t oc) /**< position in the bytecode */ -{ - JERRY_ASSERT (bytecode_data_p); - - lit_id_hash_table *lit_id_hash = MEM_CP_GET_POINTER (lit_id_hash_table, bytecode_data_p->lit_id_hash_cp); - - if (lit_id_hash == NULL) - { - return INVALID_LITERAL; - } - - return lit_id_hash_table_lookup (lit_id_hash, id, oc); -} /* bc_get_literal_cp_by_uid */ - -#ifdef JERRY_ENABLE_SNAPSHOT -/** - * Find literal offset in the table literal->offset - */ -uint32_t -bc_find_lit_offset (lit_cpointer_t lit_cp, /**< literal to find */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map from literal - * identifiers in - * literal storage - * to literal offsets - * in snapshot */ - uint32_t literals_num) /**< number of entries in the map */ -{ - uint32_t lit_index; - for (lit_index = 0; lit_index < literals_num; lit_index++) - { - if (lit_map_p[lit_index].literal_id.packed_value == lit_cp.packed_value) - { - break; - } - } - - JERRY_ASSERT (lit_index < literals_num); - - return lit_map_p[lit_index].literal_offset; -} /* bc_find_lit_offset */ - -/** - * Write alignment bytes to outptut buffer to align 'in_out_size' to MEM_ALIGNEMENT - * - * @return true if alignment bytes were written successfully - * else otherwise - */ -bool -bc_align_data_in_output_buffer (uint32_t *in_out_size, /**< in: unaligned size, out: aligned size */ - uint8_t *buffer_p, /**< buffer where to write */ - size_t buffer_size, /**< buffer size */ - size_t *in_out_buffer_offset_p) /**< current offset in buffer */ -{ - uint32_t aligned_size = JERRY_ALIGNUP (*in_out_size, MEM_ALIGNMENT); - - if (aligned_size != (*in_out_size)) - { - JERRY_ASSERT (aligned_size > (*in_out_size)); - - uint32_t padding_bytes_num = (uint32_t) (aligned_size - (*in_out_size)); - uint8_t padding = 0; - - for (uint32_t i = 0; i < padding_bytes_num; i++) - { - if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, &padding, sizeof (padding))) - { - return false; - } - } - - *in_out_size = aligned_size; - } - - return true; -} /* bc_align_data_in_output_buffer */ - -/** - * Dump byte-code and idx-to-literal map of a single scope to snapshot - * - * @return true, upon success (i.e. buffer size is enough), - * false - otherwise. - */ -static bool -bc_save_bytecode_with_idx_map (uint8_t *buffer_p, /**< buffer to dump to */ - size_t buffer_size, /**< buffer size */ - size_t *in_out_buffer_offset_p, /**< in-out: buffer write offset */ - const bytecode_data_header_t *bytecode_data_p, /**< byte-code data */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map from literal - * identifiers in - * literal storage - * to literal offsets - * in snapshot */ - uint32_t literals_num) /**< literals number */ -{ - JERRY_ASSERT (JERRY_ALIGNUP (*in_out_buffer_offset_p, MEM_ALIGNMENT) == *in_out_buffer_offset_p); - - jerry_snapshot_bytecode_header_t bytecode_header; - bytecode_header.func_scopes_count = bytecode_data_p->func_scopes_count; - bytecode_header.var_decls_count = bytecode_data_p->var_decls_count; - bytecode_header.is_strict = bytecode_data_p->is_strict; - bytecode_header.is_ref_arguments_identifier = bytecode_data_p->is_ref_arguments_identifier; - bytecode_header.is_ref_eval_identifier = bytecode_data_p->is_ref_eval_identifier; - bytecode_header.is_args_moved_to_regs = bytecode_data_p->is_args_moved_to_regs; - bytecode_header.is_no_lex_env = bytecode_data_p->is_no_lex_env; - size_t bytecode_header_offset = *in_out_buffer_offset_p; - - /* Dump instructions */ - *in_out_buffer_offset_p += JERRY_ALIGNUP (sizeof (jerry_snapshot_bytecode_header_t), MEM_ALIGNMENT); - - vm_instr_counter_t instrs_num = bytecode_data_p->instrs_count; - - const size_t instrs_array_size = sizeof (vm_instr_t) * instrs_num; - if (*in_out_buffer_offset_p + instrs_array_size > buffer_size) - { - return false; - } - memcpy (buffer_p + *in_out_buffer_offset_p, bytecode_data_p->instrs_p, instrs_array_size); - *in_out_buffer_offset_p += instrs_array_size; - - bytecode_header.instrs_size = (uint32_t) (sizeof (vm_instr_t) * instrs_num); - - /* Dump variable declarations */ - mem_cpointer_t *func_scopes_p = MEM_CP_GET_POINTER (mem_cpointer_t, bytecode_data_p->declarations_cp); - lit_cpointer_t *var_decls_p = (lit_cpointer_t *) (func_scopes_p + bytecode_data_p->func_scopes_count); - uint32_t null_var_decls_num = 0; - for (uint32_t i = 0; i < bytecode_header.var_decls_count; ++i) - { - lit_cpointer_t lit_cp = var_decls_p[i]; - - if (lit_cp.packed_value == MEM_CP_NULL) - { - null_var_decls_num++; - continue; - } - - uint32_t offset = bc_find_lit_offset (lit_cp, lit_map_p, literals_num); - if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, &offset, sizeof (offset))) - { - return false; - } - } - bytecode_header.var_decls_count -= null_var_decls_num; - - /* Dump uid->lit_cp hash table */ - lit_id_hash_table *lit_id_hash_p = MEM_CP_GET_POINTER (lit_id_hash_table, bytecode_data_p->lit_id_hash_cp); - uint32_t idx_to_lit_map_size = lit_id_hash_table_dump_for_snapshot (buffer_p, - buffer_size, - in_out_buffer_offset_p, - lit_id_hash_p, - lit_map_p, - literals_num, - instrs_num); - - if (idx_to_lit_map_size == 0) - { - return false; - } - - bytecode_header.idx_to_lit_map_size = idx_to_lit_map_size; - - /* Align to write next bytecode data at aligned address */ - bytecode_header.size = (uint32_t) (*in_out_buffer_offset_p - bytecode_header_offset); - JERRY_ASSERT (bytecode_header.size == JERRY_ALIGNUP (sizeof (jerry_snapshot_bytecode_header_t), MEM_ALIGNMENT) - + bytecode_header.instrs_size - + bytecode_header.var_decls_count * sizeof (uint32_t) - + idx_to_lit_map_size); - - if (!bc_align_data_in_output_buffer (&bytecode_header.size, - buffer_p, - buffer_size, - in_out_buffer_offset_p)) - { - return false; - } - - /* Dump header at the saved offset */ - if (!jrt_write_to_buffer_by_offset (buffer_p, - buffer_size, - &bytecode_header_offset, - &bytecode_header, - sizeof (bytecode_header))) - { - return false; - } - - return true; -} /* bc_save_bytecode_with_idx_map */ - - -/** - * Dump bytecode and summplementary data of all existing scopes to snapshot - * - * @return true if snapshot was dumped successfully - * false otherwise - */ -bool -bc_save_bytecode_data (uint8_t *buffer_p, /**< buffer to dump to */ - size_t buffer_size, /**< buffer size */ - size_t *in_out_buffer_offset_p, /**< in-out: buffer write offset */ - const bytecode_data_header_t *bytecode_data_p, /**< byte-code data */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map from literal - * identifiers in - * literal storage - * to literal offsets - * in snapshot */ - uint32_t literals_num, /**< literals number */ - uint32_t *out_scopes_num) /**< number of scopes written */ -{ - bytecode_data_header_t *next_to_handle_list_p = first_bytecode_header_p; - - while (next_to_handle_list_p != NULL) - { - if (next_to_handle_list_p == bytecode_data_p) - { - break; - } - next_to_handle_list_p = MEM_CP_GET_POINTER (bytecode_data_header_t, next_to_handle_list_p->next_header_cp); - } - - JERRY_ASSERT (next_to_handle_list_p); - JERRY_ASSERT (next_to_handle_list_p->next_header_cp == MEM_CP_NULL); - - *out_scopes_num = 0; - while (next_to_handle_list_p!= NULL) - { - bytecode_data_header_t *bc_header_list_iter_p = next_to_handle_list_p; - next_to_handle_list_p = NULL; - - - mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, bc_header_list_iter_p->declarations_cp); - - if (!bc_save_bytecode_with_idx_map (buffer_p, - buffer_size, - in_out_buffer_offset_p, - bc_header_list_iter_p, - lit_map_p, - literals_num)) - { - return false; - } - - (*out_scopes_num)++; - - next_to_handle_list_p = MEM_CP_GET_POINTER (bytecode_data_header_t, bc_header_list_iter_p->next_header_cp); - - for (uint32_t index = bc_header_list_iter_p->func_scopes_count; index > 0 ; index--) - { - bytecode_data_header_t *child_scope_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, - declarations_p[index-1]); - - JERRY_ASSERT (child_scope_header_p->next_header_cp == MEM_CP_NULL); - - MEM_CP_SET_POINTER (child_scope_header_p->next_header_cp, next_to_handle_list_p); - - next_to_handle_list_p = child_scope_header_p; - } - - bc_header_list_iter_p->next_header_cp = MEM_CP_NULL; - } - - return true; -} /* bc_save_bytecode_data */ - - -/** - * Register bytecode and supplementary data of a single scope from snapshot - * - * NOTE: - * If is_copy flag is set, bytecode is copied from snapshot, else bytecode is referenced directly - * from snapshot - * - * @return pointer to byte-code header, upon success, - * NULL - upon failure (i.e., in case snapshot format is not valid) - */ -static bytecode_data_header_t * -bc_load_bytecode_with_idx_map (const uint8_t *snapshot_data_p, /**< buffer with instructions array - * and idx to literals map from - * snapshot */ - size_t snapshot_size, /**< remaining size of snapshot */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map of in-snapshot - * literal offsets - * to literal identifiers, - * created in literal - * storage */ - uint32_t literals_num, /**< number of literals */ - bool is_copy, /** flag, indicating whether the passed in-snapshot data - * should be copied to engine's memory (true), - * or it can be referenced until engine is stopped - * (i.e. until call to jerry_cleanup) */ - uint32_t *out_bytecode_data_size) /**< out: size occupied by bytecode data - * in snapshot */ -{ - size_t buffer_offset = 0; - jerry_snapshot_bytecode_header_t bytecode_header; - if (!jrt_read_from_buffer_by_offset (snapshot_data_p, - snapshot_size, - &buffer_offset, - &bytecode_header, - sizeof (bytecode_header))) - { - return NULL; - } - - *out_bytecode_data_size = bytecode_header.size; - - buffer_offset += (JERRY_ALIGNUP (sizeof (jerry_snapshot_bytecode_header_t), MEM_ALIGNMENT) - - sizeof (jerry_snapshot_bytecode_header_t)); - - JERRY_ASSERT (bytecode_header.size <= snapshot_size); - - /* Read uid->lit_cp hash table size */ - const uint8_t *idx_to_lit_map_p = (snapshot_data_p - + buffer_offset + - + bytecode_header.instrs_size - + bytecode_header.var_decls_count * sizeof (uint32_t)); - - size_t instructions_number = bytecode_header.instrs_size / sizeof (vm_instr_t); - size_t blocks_count = JERRY_ALIGNUP (instructions_number, BLOCK_SIZE) / BLOCK_SIZE; - - uint32_t idx_num_total; - size_t idx_to_lit_map_offset = 0; - if (!jrt_read_from_buffer_by_offset (idx_to_lit_map_p, - bytecode_header.idx_to_lit_map_size, - &idx_to_lit_map_offset, - &idx_num_total, - sizeof (idx_num_total))) - { - return NULL; - } - - /* Alloc bytecode_header for runtime */ - const size_t bytecode_alloc_size = JERRY_ALIGNUP (bytecode_header.instrs_size, MEM_ALIGNMENT); - const size_t hash_table_size = lit_id_hash_table_get_size_for_table (idx_num_total, blocks_count); - const size_t declarations_area_size = JERRY_ALIGNUP (bytecode_header.func_scopes_count * sizeof (mem_cpointer_t) - + bytecode_header.var_decls_count * sizeof (lit_cpointer_t), - MEM_ALIGNMENT); - const size_t header_and_tables_size = JERRY_ALIGNUP ((sizeof (bytecode_data_header_t) - + hash_table_size - + declarations_area_size), - MEM_ALIGNMENT); - const size_t alloc_size = header_and_tables_size + (is_copy ? bytecode_alloc_size : 0); - - uint8_t *buffer_p = (uint8_t*) mem_heap_alloc_block (alloc_size, MEM_HEAP_ALLOC_LONG_TERM); - bytecode_data_header_t *header_p = (bytecode_data_header_t *) buffer_p; - - vm_instr_t *instrs_p; - vm_instr_t *snapshot_instrs_p = (vm_instr_t *) (snapshot_data_p + buffer_offset); - if (is_copy) - { - instrs_p = (vm_instr_t *) (buffer_p + header_and_tables_size); - memcpy (instrs_p, snapshot_instrs_p, bytecode_header.instrs_size); - } - else - { - instrs_p = snapshot_instrs_p; - } - - buffer_offset += bytecode_header.instrs_size; /* buffer_offset is now offset of variable declarations */ - - /* Read uid->lit_cp hash table */ - uint8_t *lit_id_hash_table_buffer_p = buffer_p + sizeof (bytecode_data_header_t); - if (!(lit_id_hash_table_load_from_snapshot (blocks_count, - idx_num_total, - idx_to_lit_map_p + idx_to_lit_map_offset, - bytecode_header.idx_to_lit_map_size - idx_to_lit_map_offset, - lit_map_p, - literals_num, - lit_id_hash_table_buffer_p, - hash_table_size) - && (vm_instr_counter_t) instructions_number == instructions_number)) - { - mem_heap_free_block (buffer_p); - return NULL; - } - - /* Fill with NULLs child scopes declarations for this scope */ - mem_cpointer_t *declarations_p = (mem_cpointer_t *) (buffer_p + sizeof (bytecode_data_header_t) + hash_table_size); - memset (declarations_p, 0, bytecode_header.func_scopes_count * sizeof (mem_cpointer_t)); - - /* Read variable declarations for this scope */ - lit_cpointer_t *var_decls_p = (lit_cpointer_t *) (declarations_p + bytecode_header.func_scopes_count); - for (uint32_t i = 0; i < bytecode_header.var_decls_count; i++) - { - uint32_t lit_offset_from_snapshot; - if (!jrt_read_from_buffer_by_offset (snapshot_data_p, - buffer_offset + bytecode_header.var_decls_count * sizeof (uint32_t), - &buffer_offset, - &lit_offset_from_snapshot, - sizeof (lit_offset_from_snapshot))) - { - mem_heap_free_block (buffer_p); - return NULL; - } - /** - * TODO: implement binary search here - */ - lit_cpointer_t lit_cp = NOT_A_LITERAL; - uint32_t j; - for (j = 0; j < literals_num; j++) - { - if (lit_map_p[j].literal_offset == lit_offset_from_snapshot) - { - lit_cp.packed_value = lit_map_p[j].literal_id.packed_value; - break; - } - } - - if (j == literals_num) - { - mem_heap_free_block (buffer_p); - return NULL; - } - - var_decls_p[i] = lit_cp; - } - - /* Fill bytecode_data_header */ - bc_fill_bytecode_data_header (header_p, - (lit_id_hash_table *) lit_id_hash_table_buffer_p, - instrs_p, - declarations_p, - (uint16_t) bytecode_header.func_scopes_count, - (uint16_t) bytecode_header.var_decls_count, - bytecode_header.is_strict, - bytecode_header.is_ref_arguments_identifier, - bytecode_header.is_ref_eval_identifier, - bytecode_header.is_args_moved_to_regs, - bytecode_header.is_args_moved_to_regs, - bytecode_header.is_no_lex_env); - - return header_p; -} /* bc_load_bytecode_with_idx_map */ - -/** - * Register bytecode and supplementary data of all scopes from snapshot - * - * NOTE: - * If is_copy flag is set, bytecode is copied from snapshot, else bytecode is referenced directly - * from snapshot - * - * @return pointer to byte-code header, upon success, - * NULL - upon failure (i.e., in case snapshot format is not valid) - */ -const bytecode_data_header_t * -bc_load_bytecode_data (const uint8_t *snapshot_data_p, /**< buffer with instructions array - * and idx to literals map from - * snapshot */ - size_t snapshot_size, /**< remaining size of snapshot */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map of in-snapshot - * literal offsets - * to literal identifiers, - * created in literal - * storage */ - uint32_t literals_num, /**< number of literals */ - bool is_copy, /** flag, indicating whether the passed in-snapshot data - * should be copied to engine's memory (true), - * or it can be referenced until engine is stopped - * (i.e. until call to jerry_cleanup) */ - uint32_t expected_scopes_num) /**< scopes number read from snapshot header */ -{ - uint32_t snapshot_offset = 0; - uint32_t out_bytecode_data_size = 0; - uint32_t scopes_num = 0; - - bytecode_data_header_t *bc_header_p = bc_load_bytecode_with_idx_map (snapshot_data_p, - snapshot_size, - lit_map_p, - literals_num, - is_copy, - &out_bytecode_data_size); - - scopes_num++; - snapshot_offset += out_bytecode_data_size; - JERRY_ASSERT (snapshot_offset <= snapshot_size); - - bytecode_data_header_t* next_to_handle_list_p = bc_header_p; - - while (next_to_handle_list_p != NULL) - { - mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, next_to_handle_list_p->declarations_cp); - uint32_t child_scope_index = 0; - while (child_scope_index < next_to_handle_list_p->func_scopes_count - && declarations_p[child_scope_index] != MEM_CP_NULL) - { - child_scope_index++; - } - - if (child_scope_index == next_to_handle_list_p->func_scopes_count) - { - bytecode_data_header_t *bc_header_list_iter_p = MEM_CP_GET_POINTER (bytecode_data_header_t, - next_to_handle_list_p->next_header_cp); - - next_to_handle_list_p->next_header_cp = MEM_CP_NULL; - next_to_handle_list_p = bc_header_list_iter_p; - - if (next_to_handle_list_p == NULL) - { - break; - } - else - { - continue; - } - } - - JERRY_ASSERT (snapshot_offset < snapshot_size); - bytecode_data_header_t *next_header_p = bc_load_bytecode_with_idx_map (snapshot_data_p + snapshot_offset, - snapshot_size - snapshot_offset, - lit_map_p, - literals_num, - is_copy, - &out_bytecode_data_size); - - scopes_num++; - - snapshot_offset += out_bytecode_data_size; - JERRY_ASSERT (snapshot_offset <= snapshot_size); - - MEM_CP_SET_NON_NULL_POINTER (declarations_p[child_scope_index], next_header_p); - - if (next_header_p->func_scopes_count > 0) - { - JERRY_ASSERT (next_header_p->next_header_cp == MEM_CP_NULL); - - MEM_CP_SET_POINTER (next_header_p->next_header_cp, next_to_handle_list_p); - next_to_handle_list_p = next_header_p; - } - } - - if (expected_scopes_num != scopes_num) - { - return NULL; - } - - MEM_CP_SET_POINTER (bc_header_p->next_header_cp, first_bytecode_header_p); - - first_bytecode_header_p = bc_header_p; - - return bc_header_p; -} /* bc_load_bytecode_data */ - -#endif /* JERRY_ENABLE_SNAPSHOT */ diff --git a/jerry-core/parser/js/bc/bytecode-data.h b/jerry-core/parser/js/bc/bytecode-data.h deleted file mode 100644 index 90e2b8fff8..0000000000 --- a/jerry-core/parser/js/bc/bytecode-data.h +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BYTECODE_DATA_H -#define BYTECODE_DATA_H - -#include "opcodes.h" -#include "mem-allocator.h" -#include "lit-id-hash-table.h" -#include "scopes-tree.h" - -/* - * All literals are kept in the 'literals' array. - * Literal structure doesn't hold real string. All program-specific strings - * are kept in the 'strings_buffer' and literal has pointer to this buffer. - * - * Literal id is its index in 'literals' array of bytecode_data_t structure. - * - * Bytecode, which is kept in the 'instrs' field, is divided into blocks - * of 'BLOCK_SIZE' operands. Every block has its own numbering of literals. - * Literal uid could be in range [0, 127] in every block. - * - * To map uid to literal id 'lit_id_hash' table is used. - */ -#define BLOCK_SIZE 32u - -/** - * Header of byte-code memory region, containing byte-code array and literal identifiers hash table - */ -typedef struct __attribute__ ((aligned (MEM_ALIGNMENT))) bytecode_data_header_t -{ - vm_instr_t *instrs_p; /**< pointer to the bytecode */ - vm_instr_counter_t instrs_count; /**< number of instructions in the byte-code array */ - mem_cpointer_t lit_id_hash_cp; /**< pointer to literal identifiers hash table - * See also: lit_id_hash_table_init */ - - mem_cpointer_t declarations_cp; /**< function scopes and variable declarations inside current scope */ - uint16_t func_scopes_count; /**< count of function scopes inside current scope */ - uint16_t var_decls_count; /**< count of variable declrations inside current scope */ - - mem_cpointer_t next_header_cp; /**< pointer to next instructions data header */ - - uint8_t is_strict : 1; /**< code is strict mode code */ - uint8_t is_ref_arguments_identifier : 1; /**< code doesn't reference 'arguments' identifier */ - uint8_t is_ref_eval_identifier : 1; /**< code doesn't reference 'eval' identifier */ - uint8_t is_vars_and_args_to_regs_possible : 1; /**< flag, indicating whether it is possible - * to safely perform var-to-reg - * optimization on the scope - * - * TODO: remove the flag when var-to-reg optimization - * would be moved from post-parse to dump stage */ - uint8_t is_args_moved_to_regs : 1; /**< the function's arguments are moved to registers, - * so should be initialized in vm registers, - * and not in lexical environment */ - uint8_t is_no_lex_env : 1; /**< no lex. env. is necessary for the scope */ -} bytecode_data_header_t; - -JERRY_STATIC_ASSERT (sizeof (bytecode_data_header_t) % MEM_ALIGNMENT == 0); - -void bc_remove_bytecode_data (const bytecode_data_header_t *); - -vm_instr_t bc_get_instr (const bytecode_data_header_t *, - vm_instr_counter_t); - -void bc_print_instrs (const bytecode_data_header_t *); - -bytecode_data_header_t *bc_dump_single_scope (scopes_tree); -void bc_register_root_bytecode_header (bytecode_data_header_t *); - -void bc_finalize (); - -lit_cpointer_t -bc_get_literal_cp_by_uid (uint8_t, - const bytecode_data_header_t *, - vm_instr_counter_t); - - -#ifdef JERRY_ENABLE_SNAPSHOT -/* - * Snapshot-related - */ -uint32_t -bc_find_lit_offset (lit_cpointer_t, const lit_mem_to_snapshot_id_map_entry_t *, uint32_t); - -bool -bc_align_data_in_output_buffer (uint32_t *, uint8_t *, size_t, size_t *); - -bool -bc_save_bytecode_data (uint8_t *, size_t, size_t *, const bytecode_data_header_t *, - const lit_mem_to_snapshot_id_map_entry_t *, uint32_t, uint32_t *); - -const bytecode_data_header_t * -bc_load_bytecode_data (const uint8_t *, size_t, - const lit_mem_to_snapshot_id_map_entry_t *, uint32_t, bool, uint32_t); -#endif /* JERRY_ENABLE_SNAPSHOT */ - -#endif /* BYTECODE_DATA_H */ diff --git a/jerry-core/parser/js/byte-code.cpp b/jerry-core/parser/js/byte-code.cpp new file mode 100644 index 0000000000..2ec6b3176f --- /dev/null +++ b/jerry-core/parser/js/byte-code.cpp @@ -0,0 +1,78 @@ +/* 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 "js-parser-internal.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_bytecode Bytecode + * @{ + */ + +#define CBC_OPCODE(arg1, arg2, arg3, arg4) \ + ((arg2) | (((arg3) + CBC_STACK_ADJUST_BASE) << CBC_STACK_ADJUST_SHIFT)), + +/** + * Flags of the opcodes. + */ +const uint8_t cbc_flags[] = +{ + CBC_OPCODE_LIST +}; + +/** + * Flags of the extended opcodes. + */ +const uint8_t cbc_ext_flags[] = +{ + CBC_EXT_OPCODE_LIST +}; + +#undef CBC_OPCODE + +#ifdef PARSER_DUMP_BYTE_CODE + +#define CBC_OPCODE(arg1, arg2, arg3, arg4) #arg1, + +/** + * Names of the opcodes. + */ +const char *cbc_names[] = +{ + CBC_OPCODE_LIST +}; + +/** + * Names of the extended opcodes. + */ +const char *cbc_ext_names[] = +{ + CBC_EXT_OPCODE_LIST +}; + +#undef CBC_OPCODE + +#endif /* PARSER_DUMP_BYTE_CODE */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h new file mode 100644 index 0000000000..df7d246d7d --- /dev/null +++ b/jerry-core/parser/js/byte-code.h @@ -0,0 +1,694 @@ +/* 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. + */ + +#ifndef BYTE_CODE_H +#define BYTE_CODE_H + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_bytecode Bytecode + * @{ + */ + +/** + * Compact byte code (CBC) is a byte code representation + * of EcmaScript which is designed for low memory + * environments. Most opcodes are only one or sometimes + * two byte long so the CBC provides a small binary size. + * + * The execution engine of CBC is a stack machine, where + * the maximum stack size is known in advance for each + * function. + */ + +/** + * Byte code flags. Only the lower 5 bit can be used + * since the stack change is encoded in the upper + * three bits for each instruction between -4 and 3 + * (except for call / construct opcodes). + */ +#define CBC_STACK_ADJUST_BASE 4 +#define CBC_STACK_ADJUST_SHIFT 5 +#define CBC_STACK_ADJUST_VALUE(value) \ + (((value) >> CBC_STACK_ADJUST_SHIFT) - CBC_STACK_ADJUST_BASE) + +#define CBC_NO_FLAG 0x00u +#define CBC_HAS_LITERAL_ARG 0x01u +#define CBC_HAS_LITERAL_ARG2 0x02u +#define CBC_HAS_BYTE_ARG 0x04u +#define CBC_HAS_BRANCH_ARG 0x08u + +/* These flags are shared */ +#define CBC_FORWARD_BRANCH_ARG 0x10u +#define CBC_POP_STACK_BYTE_ARG 0x10u + +#define CBC_ARG_TYPES (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2 | CBC_HAS_BYTE_ARG | CBC_HAS_BRANCH_ARG) + +#define CBC_HAS_POP_STACK_BYTE_ARG (CBC_HAS_BYTE_ARG | CBC_POP_STACK_BYTE_ARG) + +/* Debug macro. */ +#define CBC_ARGS_EQ(op, types) \ + ((cbc_flags[op] & CBC_ARG_TYPES) == (types)) + +/* Debug macro. */ +#define CBC_SAME_ARGS(op1, op2) \ + ((cbc_flags[op1] & CBC_ARG_TYPES) == (cbc_flags[op2] & CBC_ARG_TYPES)) + +#define CBC_UNARY_OPERATION(name, group) \ + CBC_OPCODE (name, CBC_NO_FLAG, 0, \ + (VM_OC_ ## group) | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (name ## _LITERAL, CBC_HAS_LITERAL_ARG, 1, \ + (VM_OC_ ## group) | VM_OC_GET_LITERAL | VM_OC_PUT_STACK) + +#define CBC_BINARY_OPERATION(name, group) \ + CBC_OPCODE (name, CBC_NO_FLAG, -1, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (name ## _RIGHT_LITERAL, CBC_HAS_LITERAL_ARG, 0, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (name ## _TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \ + (VM_OC_ ## group) | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) + +#define CBC_UNARY_LVALUE_OPERATION(name, group) \ + CBC_OPCODE (name, CBC_NO_FLAG, -2, \ + (VM_OC_PROP_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE) \ + CBC_OPCODE (name ## _PUSH_RESULT, CBC_NO_FLAG, -1, \ + (VM_OC_PROP_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (name ## _BLOCK, CBC_NO_FLAG, -2, \ + (VM_OC_PROP_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (name ## _IDENT, CBC_HAS_LITERAL_ARG, 0, \ + (VM_OC_ ## group) | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ + CBC_OPCODE (name ## _IDENT_PUSH_RESULT, CBC_HAS_LITERAL_ARG, 1, \ + (VM_OC_ ## group) | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_STACK) \ + CBC_OPCODE (name ## _IDENT_BLOCK, CBC_HAS_LITERAL_ARG, 0, \ + (VM_OC_ ## group) | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_BLOCK) + +#define CBC_BINARY_LVALUE_OPERATION(name, group) \ + CBC_OPCODE (name, CBC_NO_FLAG, -4, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE) \ + CBC_OPCODE (name ## _LITERAL, CBC_HAS_LITERAL_ARG, -3, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_REFERENCE) \ + +#define CBC_EXT_BINARY_LVALUE_OPERATION(name, group) \ + CBC_OPCODE (name ## _PUSH_RESULT, CBC_NO_FLAG, -3, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (name ## _LITERAL_PUSH_RESULT, CBC_HAS_LITERAL_ARG, -2, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ + +#define CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION(name, group) \ + CBC_OPCODE (name ## _BLOCK, CBC_NO_FLAG, -4, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (name ## _LITERAL_BLOCK, CBC_HAS_LITERAL_ARG, -3, \ + (VM_OC_ ## group) | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ + +#define CBC_UNARY_LVALUE_WITH_IDENT 3 + +#define CBC_BINARY_LVALUE_WITH_LITERAL 1 + +#define CBC_BINARY_WITH_LITERAL 1 +#define CBC_BINARY_WITH_TWO_LITERALS 2 + +/** + * Several opcodes (mostly call and assignment opcodes) have + * two forms: one which does not push a return value onto + * the stack, and another which does. The reasion is that + * the return value of these opcodes are often not used + * and the first form provides smaller byte code. + * + * The following rules must be kept by the code generator: + * - only the opcode without return value can be emitted + * by the code generator + * - the first form can be converted to the second form + * by adding 1 to the opcode + * - after the conversion the opcode must be immediately + * flushed, so no further changes are possible + * + * Hence CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode) + * cannot be true for an opcode which has a result + */ + +#define CBC_NO_RESULT_OPERATION(opcode) \ + ((opcode) >= CBC_DELETE && (opcode) < CBC_END) + +#define CBC_NO_RESULT_BLOCK(opcode) \ + ((opcode) >= CBC_DELETE && (opcode) < CBC_ASSIGN_ADD) + +#define CBC_NO_RESULT_COMPOUND_ASSIGMENT(opcode) \ + ((opcode) >= CBC_ASSIGN_ADD && (opcode) < CBC_END) + +/** + * Branch instructions are organized in group of 8 opcodes. + * - 1st opcode: unused, can be used for other purpose + * - 2nd opcode: forward branch with 1 byte offset + * - 3rd opcode: forward branch with 2 byte offset + * - 4th opcode: forward branch with 3 byte offset + * - 5th opcode: unused, can be used for other purpose + * - 6th opcode: backward branch with 1 byte offset + * - 7th opcode: backward branch with 2 byte offset + * - 8th opcode: backward branch with 3 byte offset + * + * Reasons: + * The branch_opcode & 0x3 tells the length in bytes of the offset + * If branch offset & 0x4 == 0, it is a forward branch. Otherwise + * it is backward. + * + * The offset bytes are encoded in higher to lower order. + */ + +#define CBC_FORWARD_BRANCH(name, stack, vm_oc) \ + CBC_OPCODE (name, CBC_HAS_BRANCH_ARG | CBC_FORWARD_BRANCH_ARG, stack, \ + (vm_oc)) \ + CBC_OPCODE (name ## _2, CBC_HAS_BRANCH_ARG | CBC_FORWARD_BRANCH_ARG, stack, \ + (vm_oc)) \ + CBC_OPCODE (name ## _3, CBC_HAS_BRANCH_ARG | CBC_FORWARD_BRANCH_ARG, stack, \ + (vm_oc)) + +#define CBC_BACKWARD_BRANCH(name, stack, vm_oc) \ + CBC_OPCODE (name, CBC_HAS_BRANCH_ARG, stack, \ + (vm_oc)) \ + CBC_OPCODE (name ## _2, CBC_HAS_BRANCH_ARG, stack, \ + (vm_oc)) \ + CBC_OPCODE (name ## _3, CBC_HAS_BRANCH_ARG, stack, \ + (vm_oc)) + +#define CBC_BRANCH_OFFSET_LENGTH(opcode) \ + ((opcode) & 0x3) + +#define CBC_BRANCH_IS_BACKWARD(flags) \ + (!((flags) & CBC_FORWARD_BRANCH_ARG)) + +#define CBC_BRANCH_IS_FORWARD(flags) \ + ((flags) & CBC_FORWARD_BRANCH_ARG) + +/* Stack consumption of opcodes with context. */ + +/* PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 3 +/* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_WITH_CONTEXT_STACK_ALLOCATION 2 +/* PARSER_TRY_CONTEXT_STACK_ALLOCATION must be <= 3 */ +#define PARSER_TRY_CONTEXT_STACK_ALLOCATION 2 + +/** + * Opcode definitions. + */ +#define CBC_OPCODE_LIST \ + /* Branch opcodes first. Some other opcodes are mixed. */ \ + CBC_OPCODE (CBC_EXT_OPCODE, CBC_NO_FLAG, 0, \ + VM_OC_NONE) \ + CBC_FORWARD_BRANCH (CBC_JUMP_FORWARD, 0, \ + VM_OC_JUMP) \ + CBC_OPCODE (CBC_POP, CBC_NO_FLAG, -1, \ + VM_OC_POP) \ + CBC_BACKWARD_BRANCH (CBC_JUMP_BACKWARD, 0, \ + VM_OC_JUMP) \ + CBC_OPCODE (CBC_POP_BLOCK, CBC_NO_FLAG, -1, \ + VM_OC_POP_BLOCK | VM_OC_PUT_BLOCK) \ + CBC_FORWARD_BRANCH (CBC_BRANCH_IF_TRUE_FORWARD, -1, \ + VM_OC_BRANCH_IF_TRUE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_THROW, CBC_NO_FLAG, -1, \ + VM_OC_THROW | VM_OC_GET_STACK) \ + CBC_BACKWARD_BRANCH (CBC_BRANCH_IF_TRUE_BACKWARD, -1, \ + VM_OC_BRANCH_IF_TRUE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_CONTEXT_END, CBC_NO_FLAG, 0, \ + VM_OC_CONTEXT_END) \ + CBC_FORWARD_BRANCH (CBC_BRANCH_IF_FALSE_FORWARD, -1, \ + VM_OC_BRANCH_IF_FALSE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_CREATE_OBJECT, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_OBJECT | VM_OC_PUT_STACK) \ + CBC_BACKWARD_BRANCH (CBC_BRANCH_IF_FALSE_BACKWARD, -1, \ + VM_OC_BRANCH_IF_FALSE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_SET_PROPERTY, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_SET_PROPERTY | VM_OC_GET_STACK_LITERAL) \ + CBC_FORWARD_BRANCH (CBC_JUMP_FORWARD_EXIT_CONTEXT, 0, \ + VM_OC_JUMP_AND_EXIT_CONTEXT) \ + CBC_OPCODE (CBC_CREATE_ARRAY, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_ARRAY | VM_OC_PUT_STACK) \ + CBC_FORWARD_BRANCH (CBC_BRANCH_IF_LOGICAL_TRUE, -1, \ + VM_OC_BRANCH_IF_LOGICAL_TRUE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_ARRAY_APPEND, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_APPEND_ARRAY | VM_OC_GET_BYTE) \ + CBC_FORWARD_BRANCH (CBC_BRANCH_IF_LOGICAL_FALSE, -1, \ + VM_OC_BRANCH_IF_LOGICAL_FALSE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_PUSH_ELISION, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_ELISON | VM_OC_PUT_STACK) \ + CBC_FORWARD_BRANCH (CBC_BRANCH_IF_STRICT_EQUAL, -1, \ + VM_OC_BRANCH_IF_STRICT_EQUAL | VM_OC_GET_STACK) \ + \ + /* Basic opcodes. */ \ + CBC_OPCODE (CBC_PUSH_LITERAL, CBC_HAS_LITERAL_ARG, 1, \ + VM_OC_PUSH | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_PUSH_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 2, \ + VM_OC_PUSH_TWO | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_PUSH_THREE_LITERALS, CBC_HAS_LITERAL_ARG2, 3, \ + VM_OC_PUSH_THREE | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_PUSH_UNDEFINED, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_UNDEFINED | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_TRUE, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_TRUE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_FALSE, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_FALSE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_NULL, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_NULL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_THIS, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_THIS | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_THIS_LITERAL, CBC_HAS_LITERAL_ARG, 2, \ + VM_OC_PUSH_TWO | VM_OC_GET_THIS_LITERAL) \ + CBC_OPCODE (CBC_PUSH_NUMBER_0, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_NUMBER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_NUMBER_1, CBC_HAS_BYTE_ARG, 1, \ + VM_OC_PUSH_NUMBER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP, CBC_NO_FLAG, -1, \ + VM_OC_PROP_GET | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_LITERAL, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_PROP_GET | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \ + VM_OC_PROP_GET | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_THIS_LITERAL, CBC_HAS_LITERAL_ARG, 1, \ + VM_OC_PROP_GET | VM_OC_GET_THIS_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_IDENT_REFERENCE, CBC_HAS_LITERAL_ARG, 3, \ + VM_OC_IDENT_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_REFERENCE, CBC_NO_FLAG, 1, \ + VM_OC_PROP_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_LITERAL_REFERENCE, CBC_HAS_LITERAL_ARG, 2, \ + VM_OC_PROP_REFERENCE | VM_OC_GET_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 3, \ + VM_OC_PROP_REFERENCE | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_PUSH_PROP_THIS_LITERAL_REFERENCE, CBC_HAS_LITERAL_ARG, 3, \ + VM_OC_PROP_REFERENCE | VM_OC_GET_THIS_LITERAL | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_NEW, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_NEW | VM_OC_GET_BYTE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_NEW0, CBC_NO_FLAG, 0, \ + VM_OC_NEW_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_NEW1, CBC_NO_FLAG, -1, \ + VM_OC_NEW_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EVAL, CBC_NO_FLAG, 0, \ + VM_OC_EVAL) \ + CBC_OPCODE (CBC_DEFINE_VARS, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_INITIALIZE_VAR, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_INITIALIZE_VARS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_SET_BYTECODE_PTR, CBC_NO_FLAG, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_RETURN, CBC_NO_FLAG, -1, \ + VM_OC_RET | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_RETURN_WITH_BLOCK, CBC_NO_FLAG, 0, \ + VM_OC_RET) \ + CBC_OPCODE (CBC_RETURN_WITH_LITERAL, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_RET | VM_OC_GET_LITERAL) \ + \ + /* Unary opcodes. */ \ + CBC_UNARY_OPERATION (CBC_PLUS, \ + PLUS) \ + CBC_UNARY_OPERATION (CBC_NEGATE, \ + MINUS) \ + CBC_UNARY_OPERATION (CBC_LOGICAL_NOT, \ + NOT) \ + CBC_UNARY_OPERATION (CBC_BIT_NOT, \ + BIT_NOT) \ + CBC_UNARY_OPERATION (CBC_VOID, \ + VOID) \ + CBC_OPCODE (CBC_TYPEOF, CBC_NO_FLAG, 0, \ + VM_OC_TYPEOF | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_TYPEOF_IDENT, CBC_HAS_LITERAL_ARG, 1, \ + VM_OC_TYPEOF_IDENT | VM_OC_PUT_STACK) \ + \ + /* Binary opcodes. */ \ + CBC_BINARY_OPERATION (CBC_BIT_OR, \ + BIT_OR) \ + CBC_BINARY_OPERATION (CBC_BIT_XOR, \ + BIT_XOR) \ + CBC_BINARY_OPERATION (CBC_BIT_AND, \ + BIT_AND) \ + CBC_BINARY_OPERATION (CBC_EQUAL, \ + EQUAL) \ + CBC_BINARY_OPERATION (CBC_NOT_EQUAL, \ + NOT_EQUAL) \ + CBC_BINARY_OPERATION (CBC_STRICT_EQUAL, \ + STRICT_EQUAL) \ + CBC_BINARY_OPERATION (CBC_STRICT_NOT_EQUAL, \ + STRICT_NOT_EQUAL) \ + CBC_BINARY_OPERATION (CBC_LESS, \ + LESS) \ + CBC_BINARY_OPERATION (CBC_GREATER, \ + GREATER) \ + CBC_BINARY_OPERATION (CBC_LESS_EQUAL, \ + LESS_EQUAL) \ + CBC_BINARY_OPERATION (CBC_GREATER_EQUAL, \ + GREATER_EQUAL) \ + CBC_BINARY_OPERATION (CBC_IN, \ + IN) \ + CBC_BINARY_OPERATION (CBC_INSTANCEOF, \ + INSTANCEOF) \ + CBC_BINARY_OPERATION (CBC_LEFT_SHIFT, \ + LEFT_SHIFT) \ + CBC_BINARY_OPERATION (CBC_RIGHT_SHIFT, \ + RIGHT_SHIFT) \ + CBC_BINARY_OPERATION (CBC_UNS_RIGHT_SHIFT, \ + UNS_RIGHT_SHIFT) \ + CBC_BINARY_OPERATION (CBC_ADD, \ + ADD) \ + CBC_BINARY_OPERATION (CBC_SUBTRACT, \ + SUB) \ + CBC_BINARY_OPERATION (CBC_MULTIPLY, \ + MUL) \ + CBC_BINARY_OPERATION (CBC_DIVIDE, \ + DIV) \ + CBC_BINARY_OPERATION (CBC_MODULO, \ + MOD) \ + \ + /* Unary lvalue opcodes. */ \ + CBC_OPCODE (CBC_DELETE, CBC_NO_FLAG, -2, \ + VM_OC_PROP_DELETE | VM_OC_GET_STACK_STACK) \ + CBC_OPCODE (CBC_DELETE_PUSH_RESULT, CBC_NO_FLAG, -1, \ + VM_OC_PROP_DELETE | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_DELETE_BLOCK, CBC_NO_FLAG, -2, \ + VM_OC_PROP_DELETE | VM_OC_GET_STACK_STACK | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_DELETE_IDENT, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_DELETE) \ + CBC_OPCODE (CBC_DELETE_IDENT_PUSH_RESULT, CBC_HAS_LITERAL_ARG, 1, \ + VM_OC_DELETE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_DELETE_IDENT_BLOCK, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_DELETE | VM_OC_PUT_BLOCK) \ + CBC_UNARY_LVALUE_OPERATION (CBC_PRE_INCR, \ + PRE_INCR) \ + CBC_UNARY_LVALUE_OPERATION (CBC_PRE_DECR, \ + PRE_DECR) \ + CBC_UNARY_LVALUE_OPERATION (CBC_POST_INCR, \ + POST_INCR) \ + CBC_UNARY_LVALUE_OPERATION (CBC_POST_DECR, \ + POST_DECR) \ + \ + /* Call opcodes. */ \ + CBC_OPCODE (CBC_CALL, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ + VM_OC_CALL | VM_OC_GET_BYTE) \ + CBC_OPCODE (CBC_CALL_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ + VM_OC_CALL | VM_OC_GET_BYTE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -1, \ + VM_OC_CALL | VM_OC_GET_BYTE | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL_PROP, CBC_HAS_POP_STACK_BYTE_ARG, -3, \ + VM_OC_CALL_PROP | VM_OC_GET_BYTE) \ + CBC_OPCODE (CBC_CALL_PROP_PUSH_RESULT, CBC_HAS_POP_STACK_BYTE_ARG, -2, \ + VM_OC_CALL_PROP | VM_OC_GET_BYTE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL_PROP_BLOCK, CBC_HAS_POP_STACK_BYTE_ARG, -3, \ + VM_OC_CALL_PROP | VM_OC_GET_BYTE | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL0, CBC_NO_FLAG, -1, \ + VM_OC_CALL_N) \ + CBC_OPCODE (CBC_CALL0_PUSH_RESULT, CBC_NO_FLAG, 0, \ + VM_OC_CALL_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL0_BLOCK, CBC_NO_FLAG, -1, \ + VM_OC_CALL_N | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL0_PROP, CBC_NO_FLAG, -3, \ + VM_OC_CALL_PROP_N) \ + CBC_OPCODE (CBC_CALL0_PROP_PUSH_RESULT, CBC_NO_FLAG, -2, \ + VM_OC_CALL_PROP_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL0_PROP_BLOCK, CBC_NO_FLAG, -3, \ + VM_OC_CALL_PROP_N | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL1, CBC_NO_FLAG, -2, \ + VM_OC_CALL_N) \ + CBC_OPCODE (CBC_CALL1_PUSH_RESULT, CBC_NO_FLAG, -1, \ + VM_OC_CALL_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL1_BLOCK, CBC_NO_FLAG, -2, \ + VM_OC_CALL_N | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL1_PROP, CBC_NO_FLAG, -4, \ + VM_OC_CALL_PROP_N) \ + CBC_OPCODE (CBC_CALL1_PROP_PUSH_RESULT, CBC_NO_FLAG, -3, \ + VM_OC_CALL_PROP_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL1_PROP_BLOCK, CBC_NO_FLAG, -4, \ + VM_OC_CALL_PROP_N | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL2, CBC_NO_FLAG, -3, \ + VM_OC_CALL_N) \ + CBC_OPCODE (CBC_CALL2_PUSH_RESULT, CBC_NO_FLAG, -2, \ + VM_OC_CALL_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL2_BLOCK, CBC_NO_FLAG, -3, \ + VM_OC_CALL_N | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_CALL2_PROP, CBC_NO_FLAG, -4, \ + VM_OC_CALL_PROP_N) \ + CBC_OPCODE (CBC_CALL2_PROP_PUSH_RESULT, CBC_NO_FLAG, -3, \ + VM_OC_CALL_PROP_N | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_CALL2_PROP_BLOCK, CBC_NO_FLAG, -4, \ + VM_OC_CALL_PROP_N | VM_OC_PUT_BLOCK) \ + \ + /* Binary assignment opcodes. */ \ + CBC_OPCODE (CBC_ASSIGN, CBC_NO_FLAG, -3, \ + VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_REFERENCE) \ + CBC_OPCODE (CBC_ASSIGN_PUSH_RESULT, CBC_NO_FLAG, -2, \ + VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_ASSIGN_BLOCK, CBC_NO_FLAG, -3, \ + VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_ASSIGN_SET_IDENT, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_IDENT) \ + CBC_OPCODE (CBC_ASSIGN_SET_IDENT_PUSH_RESULT, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_IDENT | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_ASSIGN_SET_IDENT_BLOCK, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_ASSIGN | VM_OC_GET_STACK | VM_OC_PUT_IDENT | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_ASSIGN_LITERAL_SET_IDENT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ + CBC_OPCODE (CBC_ASSIGN_LITERAL_SET_IDENT_PUSH_RESULT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \ + VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_ASSIGN_LITERAL_SET_IDENT_BLOCK, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_ASSIGN_PROP_LITERAL, CBC_HAS_LITERAL_ARG, -2, \ + VM_OC_ASSIGN_PROP | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE) \ + CBC_OPCODE (CBC_ASSIGN_PROP_LITERAL_PUSH_RESULT, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_ASSIGN_PROP | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_ASSIGN_PROP_LITERAL_BLOCK, CBC_HAS_LITERAL_ARG, -2, \ + VM_OC_ASSIGN_PROP | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ + CBC_OPCODE (CBC_ASSIGN_PROP_THIS_LITERAL, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE) \ + CBC_OPCODE (CBC_ASSIGN_PROP_THIS_LITERAL_PUSH_RESULT, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_ASSIGN_PROP_THIS_LITERAL_BLOCK, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ + \ + /* Binary compound assignment opcodes. */ \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_ADD, \ + ADD) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_SUBTRACT, \ + SUB) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_MULTIPLY, \ + MUL) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_DIVIDE, \ + DIV) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_MODULO, \ + MOD) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_LEFT_SHIFT, \ + LEFT_SHIFT) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_RIGHT_SHIFT, \ + RIGHT_SHIFT) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_UNS_RIGHT_SHIFT, \ + UNS_RIGHT_SHIFT) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_BIT_AND, \ + BIT_AND) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_BIT_OR, \ + BIT_OR) \ + CBC_BINARY_LVALUE_OPERATION (CBC_ASSIGN_BIT_XOR, \ + BIT_XOR) \ + \ + /* Last opcode (not a real opcode). */ \ + CBC_OPCODE (CBC_END, CBC_NO_FLAG, 0, \ + VM_OC_NONE) + +/* All EXT branches are statement block end + * marks, so they are always forward branches. */ + +#define CBC_EXT_OPCODE_LIST \ + /* Branch opcodes first. Some other opcodes are mixed. */ \ + CBC_OPCODE (CBC_EXT_NOP, CBC_NO_FLAG, 0, \ + VM_OC_NONE) \ + CBC_FORWARD_BRANCH (CBC_EXT_WITH_CREATE_CONTEXT, \ + -1 + PARSER_WITH_CONTEXT_STACK_ALLOCATION, VM_OC_WITH | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_EXT_FOR_IN_GET_NEXT, CBC_NO_FLAG, 1, \ + VM_OC_FOR_IN_GET_NEXT | VM_OC_PUT_STACK) \ + CBC_FORWARD_BRANCH (CBC_EXT_FOR_IN_CREATE_CONTEXT, \ + -1 + PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION, VM_OC_FOR_IN_CREATE_CONTEXT | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_EXT_SET_GETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_SET_GETTER | VM_OC_GET_LITERAL_LITERAL) \ + CBC_BACKWARD_BRANCH (CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT, 0, \ + VM_OC_FOR_IN_HAS_NEXT) \ + CBC_OPCODE (CBC_EXT_SET_SETTER, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_SET_SETTER | VM_OC_GET_LITERAL_LITERAL) \ + CBC_FORWARD_BRANCH (CBC_EXT_TRY_CREATE_CONTEXT, PARSER_TRY_CONTEXT_STACK_ALLOCATION, \ + VM_OC_TRY) \ + CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \ + VM_OC_THROW_REFERENCE_ERROR) \ + CBC_FORWARD_BRANCH (CBC_EXT_CATCH, 1, \ + VM_OC_CATCH) \ + CBC_OPCODE (CBC_EXT_PUSH_UNDEFINED_BASE, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_UNDEFINED_BASE | VM_OC_PUT_STACK) \ + CBC_FORWARD_BRANCH (CBC_EXT_FINALLY, 0, \ + VM_OC_FINALLY) \ + \ + /* Basic opcodes. */ \ + CBC_OPCODE (CBC_EXT_DEBUGGER, CBC_NO_FLAG, 0, \ + VM_OC_NONE) \ + \ + /* Binary compound assignment opcodes with pushing the result. */ \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_ADD, \ + ADD) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_SUBTRACT, \ + SUB) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_MULTIPLY, \ + MUL) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_DIVIDE, \ + DIV) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_MODULO, \ + MOD) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_LEFT_SHIFT, \ + LEFT_SHIFT) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_RIGHT_SHIFT, \ + RIGHT_SHIFT) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_UNS_RIGHT_SHIFT, \ + UNS_RIGHT_SHIFT) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_BIT_AND, \ + BIT_AND) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_BIT_OR, \ + BIT_OR) \ + CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_BIT_XOR, \ + BIT_XOR) \ + \ + /* Binary compound assignment opcodes with saving the result. */ \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_ADD, \ + ADD) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_SUBTRACT, \ + SUB) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_MULTIPLY, \ + MUL) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_DIVIDE, \ + DIV) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_MODULO, \ + MOD) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_LEFT_SHIFT, \ + LEFT_SHIFT) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_RIGHT_SHIFT, \ + RIGHT_SHIFT) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_UNS_RIGHT_SHIFT, \ + UNS_RIGHT_SHIFT) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_BIT_AND, \ + BIT_AND) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_BIT_OR, \ + BIT_OR) \ + CBC_EXT_BINARY_LVALUE_BLOCK_OPERATION (CBC_EXT_ASSIGN_BIT_XOR, \ + BIT_XOR) \ + \ + /* Last opcode (not a real opcode). */ \ + CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \ + VM_OC_NONE) + +#define CBC_MAXIMUM_BYTE_VALUE 255 +#define CBC_MAXIMUM_SMALL_VALUE 510 +#define CBC_MAXIMUM_FULL_VALUE 32767 + +#define CBC_PUSH_NUMBER_1_RANGE_END 128 + +#define CBC_HIGHEST_BIT_MASK 0x80 +#define CBC_LOWER_SEVEN_BIT_MASK 0x7f + +/** + * Literal indicies belong to one of the following groups: + * + * 0 <= index < argument_end : arguments + * argument_end <= index < register_end : registers + * register_end <= index < ident_end : identifiers + * ident_end <= index < const_literal_end : constant literals + * const_literal_end <= index < literal_end : template literals + */ + +/** + * Compiled byte code arguments. + */ +typedef struct +{ + uint16_t status_flags; /**< various status flags */ + uint8_t stack_limit; /**< maximum number of values stored on the stack */ + uint8_t argument_end; /**< number of arguments expected by the function */ + uint8_t register_end; /**< end position of the register group */ + uint8_t ident_end; /**< end position of the identifier group */ + uint8_t const_literal_end; /**< end position of the const literal group */ + uint8_t literal_end; /**< end position of the literal group */ +} cbc_uint8_arguments_t; + +/** + * Compiled byte code arguments. + */ +typedef struct +{ + uint16_t status_flags; /**< various status flags */ + uint16_t stack_limit; /**< maximum number of values stored on the stack */ + uint16_t argument_end; /**< number of arguments expected by the function */ + uint16_t register_end; /**< end position of the register group */ + uint16_t ident_end; /**< end position of the identifier group */ + uint16_t const_literal_end; /**< end position of the const literal group */ + uint16_t literal_end; /**< end position of the literal group */ +} cbc_uint16_arguments_t; + +/* When CBC_CODE_FLAGS_FULL_LITERAL_ENCODING + * is not set the small encoding is used. */ +#define CBC_CODE_FLAGS_FUNCTION 0x01 +#define CBC_CODE_FLAGS_FULL_LITERAL_ENCODING 0x02 +#define CBC_CODE_FLAGS_UINT16_ARGUMENTS 0x04 +#define CBC_CODE_FLAGS_STRICT_MODE 0x08 +#define CBC_CODE_FLAGS_ARGUMENTS_NEEDED 0x10 +#define CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED 0x20 + +#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg1, + +/** + * Opcode list. + */ +typedef enum +{ + CBC_OPCODE_LIST /**< list of opcodes */ +} cbc_opcode_t; + +/** + * Extended opcode list. + */ +typedef enum +{ + CBC_EXT_OPCODE_LIST /**< list extended opcodes */ +} cbc_ext_opcode_t; + +#undef CBC_OPCODE + +/** + * Opcode flags. + */ +extern const uint8_t cbc_flags[]; +extern const uint8_t cbc_ext_flags[]; + +#ifdef PARSER_DUMP_BYTE_CODE + +/** + * Opcode names for debugging. + */ +extern const char *cbc_names[]; +extern const char *cbc_ext_names[]; + +#endif /* PARSER_DUMP_BYTE_CODE */ + +/** + * @} + * @} + * @} + */ + +#endif /* !BYTE_CODE_H */ diff --git a/jerry-core/parser/js/collections/array-list.cpp b/jerry-core/parser/js/collections/array-list.cpp deleted file mode 100644 index 51422bb4c3..0000000000 --- a/jerry-core/parser/js/collections/array-list.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "array-list.h" -#include "jrt-libc-includes.h" -#include "jsp-mm.h" - -typedef struct -{ - uint8_t element_size; - size_t len; - size_t size; -} array_list_header; - -static array_list_header * -extract_header (array_list al) -{ - JERRY_ASSERT (al != null_list); - array_list_header *header = (array_list_header *) al; - return header; -} - -static uint8_t * -data (array_list al) -{ - return (uint8_t *) al + sizeof (array_list_header); -} - -array_list -array_list_append (array_list al, void *element) -{ - array_list_header *h = extract_header (al); - if ((h->len + 1) * h->element_size + sizeof (array_list_header) > h->size) - { - size_t size = jsp_mm_recommend_size (h->size + h->element_size); - JERRY_ASSERT (size > h->size); - - uint8_t *new_block_p = (uint8_t *) jsp_mm_alloc (size); - memcpy (new_block_p, h, h->size); - memset (new_block_p + h->size, 0, size - h->size); - - jsp_mm_free (h); - - h = (array_list_header *) new_block_p; - h->size = size; - al = (array_list) h; - } - memcpy (data (al) + (h->len * h->element_size), element, h->element_size); - h->len++; - return al; -} - -void -array_list_drop_last (array_list al) -{ - array_list_header *h = extract_header (al); - JERRY_ASSERT (h->len > 0); - h->len--; -} - -void * -array_list_element (array_list al, size_t index) -{ - array_list_header *h = extract_header (al); - if (h->len <= index) - { - return NULL; - } - return data (al) + (index * h->element_size); -} - -void -array_list_set_element (array_list al, size_t index, void *elem) -{ - array_list_header *h = extract_header (al); - JERRY_ASSERT (index < h->len); - memcpy (data (al) + (index * h->element_size), elem, h->element_size); -} - -void * -array_list_last_element (array_list al, size_t index) -{ - array_list_header *h = extract_header (al); - if (index == 0 || index > h->len) - { - return NULL; - } - return array_list_element (al, (size_t) (h->len - index)); -} - -void -array_list_set_last_element (array_list al, size_t index, void *elem) -{ - array_list_header *h = extract_header (al); - JERRY_ASSERT (index != 0 && index <= h->len); - array_list_set_element (al, (size_t) (h->len - index), elem); -} - -array_list -array_list_init (uint8_t element_size) -{ - size_t size = jsp_mm_recommend_size (sizeof (array_list_header)); - array_list_header *header = (array_list_header *) jsp_mm_alloc (size); - memset (header, 0, size); - header->element_size = element_size; - header->len = 0; - header->size = size; - return (array_list) header; -} - -size_t -array_list_len (array_list al) -{ - array_list_header *h = extract_header (al); - return h->len; -} - -void -array_list_free (array_list al) -{ - array_list_header *h = extract_header (al); - jsp_mm_free (h); -} diff --git a/jerry-core/parser/js/collections/array-list.h b/jerry-core/parser/js/collections/array-list.h deleted file mode 100644 index 5774ef03ab..0000000000 --- a/jerry-core/parser/js/collections/array-list.h +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ARRAY_LIST_H -#define ARRAY_LIST_H - -#include "jrt.h" - -typedef uint8_t *array_list; -#define null_list NULL - -array_list array_list_init (uint8_t); -void array_list_free (array_list); -array_list array_list_append (array_list, void *); -void array_list_drop_last (array_list); -void *array_list_element (array_list, size_t); -void array_list_set_element (array_list, size_t, void *); -void *array_list_last_element (array_list, size_t); -void array_list_set_last_element (array_list, size_t, void *); -size_t array_list_len (array_list); - -#endif /* ARRAY_LIST_H */ diff --git a/jerry-core/parser/js/collections/linked-list.cpp b/jerry-core/parser/js/collections/linked-list.cpp deleted file mode 100644 index 22cd43dec2..0000000000 --- a/jerry-core/parser/js/collections/linked-list.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jrt-libc-includes.h" -#include "jsp-mm.h" -#include "linked-list.h" - -/** - * Header of a linked list's chunk - */ -typedef struct linked_list_chunk_header -{ - struct linked_list_chunk_header *next_p; /**< pointer to next chunk of the list */ -} linked_list_chunk_header; - -/** - * Header of a linked list - */ -typedef struct -{ - uint16_t list_length; /**< number of elements */ - uint16_t element_size; /**< size of an element */ -} linked_list_header; - -#define ASSERT_LIST(list) \ -do { \ - linked_list_header *header = (linked_list_header *) list; \ - JERRY_ASSERT (header); \ -} while (0); - -/** - * Calculate size of a linked list's chunk - * - * @return size of the chunk's data space - */ -static size_t -linked_list_block_size (bool is_first_chunk) /**< is it first chunk (chunk, containing header)? */ -{ - if (is_first_chunk) - { - return (jsp_mm_recommend_size (sizeof (linked_list_header) + sizeof (linked_list_chunk_header) + 1u) - - sizeof (linked_list_header) - sizeof (linked_list_chunk_header)); - } - else - { - return (jsp_mm_recommend_size (sizeof (linked_list_chunk_header) + 1u) - sizeof (linked_list_chunk_header)); - } -} /* linked_list_block_size */ - -/** - * Initialize linked list - * - * @return linked list's identifier - */ -linked_list -linked_list_init (size_t element_size) /**< size of a linked list's element */ -{ - JERRY_ASSERT (element_size <= linked_list_block_size (true)); - size_t size = sizeof (linked_list_header) + sizeof (linked_list_chunk_header) + linked_list_block_size (true); - - linked_list list = (linked_list) jsp_mm_alloc (size); - JERRY_ASSERT (list != null_list); - - linked_list_header *header_p = (linked_list_header *) list; - - header_p->element_size = (uint16_t) element_size; - JERRY_ASSERT (header_p->element_size == element_size); - - header_p->list_length = 0; - - linked_list_chunk_header* chunk_header_p = (linked_list_chunk_header *) (header_p + 1u); - - chunk_header_p->next_p = NULL; - - return list; -} /* linked_list_init */ - -/** - * Create and append new chunk to list - * - * @return pointer to the new chunk - */ -static linked_list_chunk_header * -linked_list_append_new_chunk (linked_list_header *header_p, /**< linked list's header */ - linked_list_chunk_header *last_chunk_header_p) /**< last chunk of the list */ -{ - JERRY_ASSERT (header_p != NULL && last_chunk_header_p != NULL); - - JERRY_ASSERT (header_p->element_size <= linked_list_block_size (false)); - size_t size = sizeof (linked_list_chunk_header) + linked_list_block_size (false); - - linked_list_chunk_header *new_chunk_header_p = (linked_list_chunk_header *) jsp_mm_alloc (size); - JERRY_ASSERT (new_chunk_header_p != NULL); - - new_chunk_header_p->next_p = NULL; - - JERRY_ASSERT (last_chunk_header_p->next_p == NULL); - last_chunk_header_p->next_p = new_chunk_header_p; - - return new_chunk_header_p; -} /* linked_list_append_new_chunk */ - -/** - * Free the linked list - */ -void -linked_list_free (linked_list list) /**< linked list's identifier */ -{ - ASSERT_LIST (list); - - linked_list_header *header_p = (linked_list_header *) list; - linked_list_chunk_header *first_chunk_header_p = (linked_list_chunk_header *) (header_p + 1u); - - linked_list_chunk_header *iter_p = first_chunk_header_p->next_p; - while (iter_p != NULL) - { - linked_list_chunk_header *iter_next_p = iter_p->next_p; - jsp_mm_free (iter_p); - - iter_p = iter_next_p; - } - - jsp_mm_free (header_p); -} /* linked_list_free */ - -/** - * Get pointer to next element of the list - * - * @return pointer to the next element's area, - * or NULL - in case end of list was reached. - */ -static uint8_t* -linked_list_switch_to_next_elem (linked_list_header *header_p, /**< list header */ - linked_list_chunk_header **in_out_chunk_header_p, /**< list iterator (in case end - * of list was reached, - * the iterator points - * to last chunk of the list) */ - uint8_t *raw_elem_ptr_p) /**< element to get the next element for */ -{ - linked_list_chunk_header *chunk_header_p = *in_out_chunk_header_p; - - const size_t element_size = header_p->element_size; - const bool is_first_chunk = ((linked_list_chunk_header *) (header_p + 1u) == chunk_header_p); - - JERRY_ASSERT (raw_elem_ptr_p + element_size - <= (uint8_t *) (chunk_header_p + 1u) + linked_list_block_size (is_first_chunk)); - - const size_t elements_in_chunk = linked_list_block_size (is_first_chunk) / element_size; - - uint8_t *raw_start_p = (uint8_t *) (chunk_header_p + 1u); - - JERRY_ASSERT (raw_elem_ptr_p >= raw_start_p); - size_t element_offset = (size_t) (raw_elem_ptr_p - raw_start_p) / element_size; - - if (element_offset == elements_in_chunk - 1) - { - linked_list_chunk_header *next_chunk_header_p = chunk_header_p->next_p; - - if (next_chunk_header_p == NULL) - { - return NULL; - } - else - { - *in_out_chunk_header_p = next_chunk_header_p; - return (uint8_t *) (next_chunk_header_p + 1u); - } - } - else - { - JERRY_ASSERT (element_offset < elements_in_chunk - 1u); - - return (raw_elem_ptr_p + element_size); - } -} /* linked_list_switch_to_next_elem */ - -/** - * Get pointer to the linked list's element - */ -void * -linked_list_element (linked_list list, /**< linked list's identifier */ - size_t element_num) /**< index of the element */ -{ - ASSERT_LIST (list); - - linked_list_header *header_p = (linked_list_header *) list; - - if (element_num >= header_p->list_length) - { - return NULL; - } - - linked_list_chunk_header *list_chunk_iter_p = (linked_list_chunk_header *) (header_p + 1u); - - uint8_t *element_iter_p = (uint8_t *) (list_chunk_iter_p + 1); - - for (size_t i = 0; i < element_num; i++) - { - element_iter_p = linked_list_switch_to_next_elem (header_p, &list_chunk_iter_p, element_iter_p); - - if (element_iter_p == NULL) - { - return NULL; - } - } - - return element_iter_p; -} /* linked_list_element */ - -/** - * Set linked list's element - */ -void -linked_list_set_element (linked_list list, /**< linked list's identifier */ - size_t element_num, /**< index of element to set */ - void *element_p) /**< pointer to new value of the element */ -{ - if (element_p == NULL) - { - return; - } - - ASSERT_LIST (list); - - linked_list_header *header_p = (linked_list_header *) list; - linked_list_chunk_header *list_chunk_iter_p = (linked_list_chunk_header *) (header_p + 1u); - - uint8_t *element_iter_p = (uint8_t *) (list_chunk_iter_p + 1u); - - for (size_t i = 0; i < element_num; i++) - { - element_iter_p = linked_list_switch_to_next_elem (header_p, &list_chunk_iter_p, element_iter_p); - - if (element_iter_p == NULL) - { - JERRY_ASSERT (element_num >= header_p->list_length); - - linked_list_append_new_chunk (header_p, list_chunk_iter_p); - list_chunk_iter_p = list_chunk_iter_p->next_p; - - element_iter_p = (uint8_t *) (list_chunk_iter_p + 1u); - } - } - - if (element_num + 1 > header_p->list_length) - { - header_p->list_length = (uint16_t) (element_num + 1u); - JERRY_ASSERT (header_p->list_length == element_num + 1u); - } - - JERRY_ASSERT (element_iter_p != NULL); - memcpy (element_iter_p, element_p, header_p->element_size); -} /* linked_list_set_element */ - -/** - * Remove specified element from the linked list - */ -void -linked_list_remove_element (linked_list list, /**< linked list's identifier */ - size_t element_num) /**< position of the element */ -{ - ASSERT_LIST (list); - - linked_list_header *header_p = (linked_list_header *) list; - - linked_list_chunk_header *list_chunk_iter_p = (linked_list_chunk_header *) (header_p + 1u); - linked_list_chunk_header *chunk_prev_to_chunk_with_last_elem_p = list_chunk_iter_p; - - const size_t list_length = header_p->list_length; - const size_t element_size = header_p->element_size; - - JERRY_ASSERT (element_num < list_length); - - uint8_t *element_iter_p = (uint8_t *) (list_chunk_iter_p + 1u); - - for (size_t i = 0; i < element_num; i++) - { - chunk_prev_to_chunk_with_last_elem_p = list_chunk_iter_p; - element_iter_p = linked_list_switch_to_next_elem (header_p, &list_chunk_iter_p, element_iter_p); - JERRY_ASSERT (element_iter_p != NULL); - } - - uint8_t *next_elem_iter_p = linked_list_switch_to_next_elem (header_p, &list_chunk_iter_p, element_iter_p); - - JERRY_ASSERT ((element_num + 1 == list_length && next_elem_iter_p == NULL) - || (next_elem_iter_p != NULL)); - - for (size_t i = element_num + 1; i < list_length; i++) - { - JERRY_ASSERT (next_elem_iter_p != NULL); - memcpy (element_iter_p, next_elem_iter_p, element_size); - - chunk_prev_to_chunk_with_last_elem_p = list_chunk_iter_p; - - element_iter_p = next_elem_iter_p; - next_elem_iter_p = linked_list_switch_to_next_elem (header_p, &list_chunk_iter_p, next_elem_iter_p); - } - - if (list_chunk_iter_p != chunk_prev_to_chunk_with_last_elem_p) - { - JERRY_ASSERT (chunk_prev_to_chunk_with_last_elem_p->next_p == list_chunk_iter_p); - - jsp_mm_free (list_chunk_iter_p); - chunk_prev_to_chunk_with_last_elem_p->next_p = NULL; - } - - header_p->list_length--; -} /* linked_list_remove_element */ - -/** - * Get length of the linked list - * - * @return length - */ -uint16_t -linked_list_get_length (linked_list list) /**< linked list's identifier */ -{ - ASSERT_LIST (list); - - linked_list_header *header_p = (linked_list_header *) list; - return header_p->list_length; -} /* linked_list_get_length */ diff --git a/jerry-core/parser/js/collections/linked-list.h b/jerry-core/parser/js/collections/linked-list.h deleted file mode 100644 index fb3da80b7c..0000000000 --- a/jerry-core/parser/js/collections/linked-list.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LINKED_LIST_H -#define LINKED_LIST_H - -#include "jrt.h" - -typedef uint8_t *linked_list; -#define null_list NULL - -linked_list linked_list_init (size_t); -void linked_list_free (linked_list); -void *linked_list_element (linked_list, size_t); -void linked_list_set_element (linked_list, size_t, void *); -void linked_list_remove_element (linked_list, size_t); -uint16_t linked_list_get_length (linked_list); - -#endif /* LINKED_LIST_H */ diff --git a/jerry-core/parser/js/collections/lit-id-hash-table.cpp b/jerry-core/parser/js/collections/lit-id-hash-table.cpp deleted file mode 100644 index d456fd6976..0000000000 --- a/jerry-core/parser/js/collections/lit-id-hash-table.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "lit-id-hash-table.h" -#include "bytecode-data.h" - -/** \addtogroup jsparser ECMAScript parser - * @{ - * - * \addtogroup collections Collections - * @{ - * - * \addtogroup lit_id_hash_table Literal identifiers hash table - * The hash table connects pairs (instruction block, vm_idx_t value) with literal identifiers. - * @{ - */ - -/** - * Initialize literal identifiers hash table - * - * @return pointer to header of the table - */ -lit_id_hash_table * -lit_id_hash_table_init (uint8_t *table_buffer_p, /**< buffer to initialize hash table in */ - size_t buffer_size, /**< size of the buffer */ - size_t buckets_count, /**< number of pairs */ - size_t blocks_count) /**< number of instruction blocks */ -{ - const size_t header_size = JERRY_ALIGNUP (sizeof (lit_id_hash_table), MEM_ALIGNMENT); - const size_t raw_buckets_size = JERRY_ALIGNUP (sizeof (lit_cpointer_t) * buckets_count, MEM_ALIGNMENT); - const size_t buckets_size = JERRY_ALIGNUP (sizeof (lit_cpointer_t*) * blocks_count, MEM_ALIGNMENT); - - JERRY_ASSERT (header_size + raw_buckets_size + buckets_size <= buffer_size); - - lit_id_hash_table *table_p = (lit_id_hash_table *) table_buffer_p; - - table_p->current_bucket_pos = 0; - table_p->raw_buckets = (lit_cpointer_t*) (table_buffer_p + header_size); - table_p->buckets = (lit_cpointer_t **) (table_buffer_p + header_size + raw_buckets_size); - - memset (table_p->buckets, 0, buckets_size); - - return table_p; -} /* lit_id_hash_table_init */ - -/** - * Get size of buffer, necessary to hold hash table with specified parameters - * - * @return size of buffer - */ -size_t -lit_id_hash_table_get_size_for_table (size_t buckets_count, /**< number of pairs */ - size_t blocks_count) /**< number of instructions blocks */ -{ - const size_t header_size = JERRY_ALIGNUP (sizeof (lit_id_hash_table), MEM_ALIGNMENT); - const size_t raw_buckets_size = JERRY_ALIGNUP (sizeof (lit_cpointer_t) * buckets_count, MEM_ALIGNMENT); - const size_t buckets_size = JERRY_ALIGNUP (sizeof (lit_cpointer_t*) * blocks_count, MEM_ALIGNMENT); - - return header_size + raw_buckets_size + buckets_size; -} /* lit_id_hash_table_get_size_for_table */ - -/** - * Free literal identifiers hash table - */ -void -lit_id_hash_table_free (lit_id_hash_table *table_p) /**< table's header */ -{ - JERRY_ASSERT (table_p != NULL); - - mem_heap_free_block ((uint8_t *) table_p); -} /* lit_id_hash_table_free */ - -/** - * Register literal in the hash table - * - * @return corresponding idx - */ -vm_idx_t -lit_id_hash_table_insert (lit_id_hash_table *table_p, /**< table's header */ - vm_instr_counter_t oc, /**< instruction counter of the instruction */ - lit_cpointer_t lit_cp) /**< literal identifier */ -{ - JERRY_ASSERT (table_p != NULL); - - size_t block_id = oc / BLOCK_SIZE; - - if (table_p->buckets[block_id] == NULL) - { - table_p->buckets[block_id] = table_p->raw_buckets + table_p->current_bucket_pos; - } - - lit_cpointer_t *raw_bucket_iter_p = table_p->raw_buckets + table_p->current_bucket_pos; - - JERRY_ASSERT (raw_bucket_iter_p >= table_p->buckets[block_id]); - ssize_t bucket_size = (raw_bucket_iter_p - table_p->buckets[block_id]); - - int32_t index; - for (index = 0; index < bucket_size; index++) - { - if (table_p->buckets[block_id][index].packed_value == lit_cp.packed_value) - { - break; - } - } - - if (index == bucket_size) - { - JERRY_ASSERT ((uint8_t *) (table_p->raw_buckets + table_p->current_bucket_pos) < (uint8_t *) (table_p->buckets)); - - table_p->buckets[block_id][index] = lit_cp; - table_p->current_bucket_pos++; - } - - JERRY_ASSERT (index <= VM_IDX_LITERAL_LAST); - - return (vm_idx_t) index; -} /* lit_id_hash_table_insert */ - -/** - * Lookup literal identifier by pair - * - * @return literal identifier - */ -lit_cpointer_t -lit_id_hash_table_lookup (lit_id_hash_table *table_p, /**< table's header */ - vm_idx_t uid, /**< value of byte-code instruction's argument */ - vm_instr_counter_t oc) /**< instruction counter of the instruction */ -{ - JERRY_ASSERT (table_p != NULL); - - size_t block_id = oc / BLOCK_SIZE; - JERRY_ASSERT (table_p->buckets[block_id] != NULL); - - return table_p->buckets[block_id][uid]; -} /* lit_id_hash_table_lookup */ - -/** - * Dump literal identifiers hash table to snapshot buffer - * - * @return number of bytes dumper - upon success, - * 0 - upon failure - */ -uint32_t -lit_id_hash_table_dump_for_snapshot (uint8_t *buffer_p, /**< buffer to dump to */ - size_t buffer_size, /**< buffer size */ - size_t *in_out_buffer_offset_p, /**< in-out: buffer write offset */ - lit_id_hash_table *table_p, /**< hash table to dump */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map from literal - * identifiers in - * literal storage - * to literal offsets - * in snapshot */ - uint32_t literals_num, /**< number of literals */ - vm_instr_counter_t instrs_num) /**< number of instructions in corresponding - * byte-code array */ -{ - size_t begin_offset = *in_out_buffer_offset_p; - - uint32_t idx_num_total = (uint32_t) table_p->current_bucket_pos; - JERRY_ASSERT (idx_num_total == table_p->current_bucket_pos); - - if (!jrt_write_to_buffer_by_offset (buffer_p, - buffer_size, - in_out_buffer_offset_p, - &idx_num_total, - sizeof (idx_num_total))) - { - return 0; - } - - size_t blocks_num = JERRY_ALIGNUP (instrs_num, BLOCK_SIZE) / BLOCK_SIZE; - - for (size_t block_index = 0, next_block_index; - block_index < blocks_num; - ) - { - uint32_t idx_num_in_block; - - next_block_index = block_index + 1u; - - while (next_block_index < blocks_num - && table_p->buckets[next_block_index] == NULL) - { - next_block_index++; - } - - if (next_block_index != blocks_num) - { - idx_num_in_block = (uint32_t) (table_p->buckets[next_block_index] - table_p->buckets[block_index]); - } - else if (table_p->buckets[block_index] != NULL) - { - idx_num_in_block = (uint32_t) (table_p->current_bucket_pos - - (size_t) (table_p->buckets[block_index] - table_p->buckets[0])); - } - else - { - idx_num_in_block = 0; - } - - if (!jrt_write_to_buffer_by_offset (buffer_p, - buffer_size, - in_out_buffer_offset_p, - &idx_num_in_block, - sizeof (idx_num_in_block))) - { - return 0; - } - - for (size_t block_idx_pair_index = 0; - block_idx_pair_index < idx_num_in_block; - block_idx_pair_index++) - { - lit_cpointer_t lit_cp = table_p->buckets[block_index][block_idx_pair_index]; - - uint32_t offset = bc_find_lit_offset (lit_cp, lit_map_p, literals_num); - - if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, &offset, sizeof (offset))) - { - return 0; - } - } - - while (++block_index < next_block_index) - { - idx_num_in_block = 0; - - if (!jrt_write_to_buffer_by_offset (buffer_p, - buffer_size, - in_out_buffer_offset_p, - &idx_num_in_block, - sizeof (idx_num_in_block))) - { - return 0; - } - } - } - - size_t bytes_written = (*in_out_buffer_offset_p - begin_offset); - - JERRY_ASSERT (bytes_written == (uint32_t) bytes_written); - return (uint32_t) bytes_written; -} /* lit_id_hash_table_dump_for_snapshot */ - -/** - * Load literal identifiers hash table from specified snapshot buffer - * - * @return true - upon successful load (i.e. data in snapshot is consistent), - * false - upon failure (in case, snapshot is incorrect) - */ -bool -lit_id_hash_table_load_from_snapshot (size_t blocks_count, /**< number of byte-code blocks - * in corresponding byte-code array */ - uint32_t idx_num_total, /**< total number of (byte-code block, idx) - * pairs in snapshot */ - const uint8_t *idx_to_lit_map_p, /**< idx-to-lit map in snapshot */ - size_t idx_to_lit_map_size, /**< size of the map */ - const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map of in-snapshot - * literal offsets - * to literal identifiers, - * created in literal - * storage */ - uint32_t literals_num, /**< number of literals in snapshot */ - uint8_t *buffer_for_hash_table_p, /**< buffer to initialize hash table in */ - size_t buffer_for_hash_table_size) /**< size of the buffer */ -{ - lit_id_hash_table *hash_table_p = lit_id_hash_table_init (buffer_for_hash_table_p, - buffer_for_hash_table_size, - idx_num_total, - blocks_count); - - size_t idx_to_lit_map_offset = 0; - uint32_t idx_num_counter = 0; - for (size_t block_idx = 0; block_idx < blocks_count; block_idx++) - { - uint32_t idx_num_in_block; - if (!jrt_read_from_buffer_by_offset (idx_to_lit_map_p, - idx_to_lit_map_size, - &idx_to_lit_map_offset, - &idx_num_in_block, - sizeof (idx_num_in_block))) - { - return false; - } - - hash_table_p->buckets[block_idx] = hash_table_p->raw_buckets + hash_table_p->current_bucket_pos; - - if (idx_num_counter + idx_num_in_block < idx_num_counter) - { - return false; - } - idx_num_counter += idx_num_in_block; - - if (idx_num_counter > idx_num_total) - { - return false; - } - - for (uint32_t idx_in_block = 0; idx_in_block < idx_num_in_block; idx_in_block++) - { - uint32_t lit_offset_from_snapshot; - if (!jrt_read_from_buffer_by_offset (idx_to_lit_map_p, - idx_to_lit_map_size, - &idx_to_lit_map_offset, - &lit_offset_from_snapshot, - sizeof (lit_offset_from_snapshot))) - { - return false; - } - - /** - * TODO: implement binary search here - */ - lit_cpointer_t lit_cp = rcs_cpointer_null_cp (); - uint32_t i; - for (i = 0; i < literals_num; i++) - { - if (lit_map_p[i].literal_offset == lit_offset_from_snapshot) - { - lit_cp.packed_value = lit_map_p[i].literal_id.packed_value; - break; - } - } - - if (i == literals_num) - { - return false; - } - - JERRY_ASSERT (hash_table_p->current_bucket_pos < idx_num_total); - hash_table_p->raw_buckets[hash_table_p->current_bucket_pos++] = lit_cp; - } - } - - return true; -} /* lit_id_hash_table_load_from_snapshot */ - - -/** - * @} - * @} - * @} - */ diff --git a/jerry-core/parser/js/collections/lit-id-hash-table.h b/jerry-core/parser/js/collections/lit-id-hash-table.h deleted file mode 100644 index 75cc674863..0000000000 --- a/jerry-core/parser/js/collections/lit-id-hash-table.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LIT_ID_HASH_TABLE -#define LIT_ID_HASH_TABLE - -#include "jrt.h" -#include "ecma-globals.h" -#include "opcodes.h" -#include "lit-literal.h" -#include "lit-snapshot.h" - -typedef struct -{ - size_t current_bucket_pos; - lit_cpointer_t *raw_buckets; - lit_cpointer_t **buckets; -} lit_id_hash_table; - -lit_id_hash_table *lit_id_hash_table_init (uint8_t *, size_t, size_t, size_t); -size_t lit_id_hash_table_get_size_for_table (size_t, size_t); -void lit_id_hash_table_free (lit_id_hash_table *); -vm_idx_t lit_id_hash_table_insert (lit_id_hash_table *,vm_instr_counter_t, lit_cpointer_t); -lit_cpointer_t lit_id_hash_table_lookup (lit_id_hash_table *, vm_idx_t, vm_instr_counter_t); -uint32_t lit_id_hash_table_dump_for_snapshot (uint8_t *, size_t, size_t *, lit_id_hash_table *, - const lit_mem_to_snapshot_id_map_entry_t *, uint32_t, vm_instr_counter_t); -bool lit_id_hash_table_load_from_snapshot (size_t, uint32_t, const uint8_t *, size_t, - const lit_mem_to_snapshot_id_map_entry_t *, uint32_t, uint8_t *, size_t); -#endif /* LIT_ID_HASH_TABLE */ diff --git a/jerry-core/parser/js/collections/stack.h b/jerry-core/parser/js/collections/stack.h deleted file mode 100644 index 77b4dd9e33..0000000000 --- a/jerry-core/parser/js/collections/stack.h +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - This file contains macros to define and use stacks. - Use macro STACK or STATIC_STACK to create stack variable and define all necessaty routines. - Also, define variable with name NAME##_global_size. If the variable more than 0, - first NAME##_global_size element will remain untouched during STACK_PUSH and STACK_POP operations. - Before using the stack, init it by calling STACK_INIT macro. - Use macros STACK_PUSH, STACK_POP, STACK_DROP, STACK_CLEAN and STACK_HEAD to manipulate the stack. - DO NOT FORGET to free stack memory by calling STACK_FREE macro. - For check usage of stack during a function, use STACK_DECLARE_USAGE and STACK_CHECK_USAGE macros. - - For the purpose of memory fragmentation reduction, the memory is allocated by chunks and them are - used to store data. The chunks are connected to each other in manner of double-linked list. - - Macro STACK_CONVERT_TO_RAW_DATA allocates memory, so use it after finishing working with the stack. - - Example (parser.c): - - enum - { - temp_name, - min_temp_name, - max_temp_name, - temp_names_global_size - }; - STACK (temp_names, uint8_t, uint8_t) - - #define GLOBAL(NAME, VAR) \ - STACK_ELEMENT (NAME, VAR) - - #define MAX_TEMP_NAME() \ - GLOBAL (temp_names, max_temp_name) - - #define MIN_TEMP_NAME() \ - GLOBAL (temp_names, min_temp_name) - - #define TEMP_NAME() \ - GLOBAL (temp_names, temp_name) - - void - parser_init (void) - { - STACK_INIT (temp_names) - } - - void - parser_free (void) - { - STACK_FREE (temp_names) - } -*/ -#ifndef STACK_H -#define STACK_H - -#include "array-list.h" - -#define DEFINE_STACK_TYPE(NAME, TYPE) \ -typedef TYPE NAME##_stack_value_type; \ -typedef struct \ -{ \ - array_list data; \ -} \ -NAME##_stack; - -#define STACK_INIT(NAME) \ -do { \ - NAME.data = array_list_init (sizeof (NAME##_stack_value_type)); \ -} while (0) - -#define STACK_FREE(NAME) \ -do { \ - array_list_free (NAME.data); \ - NAME.data = null_list; \ -} while (0) - -#define DEFINE_STACK_ELEMENT(NAME, TYPE) \ -static TYPE NAME##_stack_element (size_t) __attr_unused___; \ -static TYPE NAME##_stack_element (size_t elem) { \ - return *((TYPE *) array_list_element (NAME.data, elem)); \ -} - -#define DEFINE_SET_STACK_ELEMENT(NAME, TYPE) \ -static void set_##NAME##_stack_element (size_t, TYPE) __attr_unused___; \ -static void set_##NAME##_stack_element (size_t elem, TYPE value) { \ - array_list_set_element (NAME.data, elem, &value); \ -} - -#define DEFINE_STACK_HEAD(NAME, TYPE) \ -static TYPE NAME##_stack_head (size_t) __attr_unused___; \ -static TYPE NAME##_stack_head (size_t elem) { \ - return *((TYPE *) array_list_last_element (NAME.data, elem)); \ -} - -#define DEFINE_SET_STACK_HEAD(NAME, TYPE) \ -static void set_##NAME##_stack_head (size_t, TYPE) __attr_unused___; \ -static void set_##NAME##_stack_head (size_t elem, TYPE value) { \ - array_list_set_last_element (NAME.data, elem, &value); \ -} - -#define DEFINE_STACK_PUSH(NAME, TYPE) \ -static void NAME##_stack_push (TYPE) __attr_unused___; \ -static void NAME##_stack_push (TYPE value) { \ - NAME.data = array_list_append (NAME.data, &value); \ -} - -#define STACK_PUSH(NAME, VALUE) \ -do { NAME##_stack_push (VALUE); } while (0) - -#define STACK_DROP(NAME, I) \ -do { \ - for (size_t i = 0, till = (size_t) (I); i < till; i++) { \ - array_list_drop_last (NAME.data); } } while (0) - -#define STACK_CLEAN(NAME) \ -STACK_DROP (NAME, NAME.current - NAME##_global_size); - -#define STACK_HEAD(NAME, I) \ -NAME##_stack_head ((size_t) (I)) - -#define STACK_SET_HEAD(NAME, I, VALUE) \ -do { set_##NAME##_stack_head ((size_t) (I), VALUE); } while (0) - -#define STACK_INCR_HEAD(NAME, I) \ -do { STACK_SET_HEAD (NAME, I, (NAME##_stack_value_type) (STACK_HEAD (NAME, I) + 1)); } while (0) - -#define STACK_DECR_HEAD(NAME, I) \ -do { STACK_SET_HEAD (NAME, I, (NAME##_stack_value_type) (STACK_HEAD (NAME, I) - 1)); } while (0) - -#define STACK_TOP(NAME) \ -STACK_HEAD (NAME, 1) - -#define STACK_SWAP(NAME) \ -do { \ - NAME##_stack_value_type temp = STACK_TOP (NAME); \ - STACK_SET_HEAD (NAME, 1, STACK_HEAD (NAME, 2)); \ - STACK_SET_HEAD (NAME, 2, temp); \ -} while (0) - -#define STACK_SIZE(NAME) \ -array_list_len (NAME.data) - -#define STACK_ELEMENT(NAME, I) \ -NAME##_stack_element ((size_t) (I)) - -#define STACK_SET_ELEMENT(NAME, I, VALUE) \ -do { set_##NAME##_stack_element ((size_t) I, VALUE); } while (0) - -#define STACK_CONVERT_TO_RAW_DATA(NAME, DATA) \ -do { DATA = convert_##NAME##_to_raw_data (); } while (0) - -#define STACK_INCR_ELEMENT(NAME, I) \ -do { STACK_SET_ELEMENT (NAME, I, (NAME##_stack_value_type) (STACK_ELEMENT (NAME, I) + 1)); } while (0) - -#define STACK_DECR_ELEMENT(NAME, I) \ -do { STACK_SET_ELEMENT (NAME, I, (NAME##_stack_value_type) (STACK_ELEMENT (NAME, I) - 1)); } while (0) - -#define STACK_ITERATE(NAME, VAL, FROM) \ -for (size_t NAME##_i = FROM; \ - NAME##_i < array_list_len (NAME.data); \ - NAME##_i++) \ -{ \ - NAME##_stack_value_type VAL = STACK_ELEMENT (NAME, NAME##_i); - -#define STACK_ITERATE_END() \ -} - -#define STACK_ITERATE_VARG_SET(NAME, FUNC, FROM, ...) \ -do { for (size_t i = FROM; i < array_list_len (NAME.data); i++) { \ - STACK_SET_ELEMENT (NAME, i, FUNC (STACK_ELEMENT (NAME, i), __VA_ARGS__)); \ -} } while (0) - -#define STACK(NAME, TYPE) \ -DEFINE_STACK_TYPE (NAME, TYPE) \ -NAME##_stack NAME; \ -DEFINE_STACK_ELEMENT (NAME, TYPE) \ -DEFINE_SET_STACK_ELEMENT (NAME, TYPE) \ -DEFINE_STACK_HEAD (NAME, TYPE) \ -DEFINE_SET_STACK_HEAD (NAME, TYPE) \ -DEFINE_STACK_PUSH (NAME, TYPE) - -#define STATIC_STACK(NAME, TYPE) \ -DEFINE_STACK_TYPE (NAME, TYPE) \ -static NAME##_stack NAME; \ -DEFINE_STACK_ELEMENT (NAME, TYPE) \ -DEFINE_SET_STACK_ELEMENT (NAME, TYPE) \ -DEFINE_STACK_HEAD (NAME, TYPE) \ -DEFINE_SET_STACK_HEAD (NAME, TYPE) \ -DEFINE_STACK_PUSH (NAME, TYPE) - -#ifndef JERRY_NDEBUG -#define STACK_DECLARE_USAGE(NAME) \ -size_t NAME##_current = array_list_len (NAME.data); -#define STACK_CHECK_USAGE(NAME) \ -do { \ - JERRY_ASSERT (array_list_len (NAME.data) == NAME##_current); \ -} while (0) -#else -#define STACK_DECLARE_USAGE(NAME) ; -#define STACK_CHECK_USAGE(NAME) ; -#endif /* JERRY_NDEBUG */ - -#endif /* STACK_H */ diff --git a/jerry-core/parser/js/common.cpp b/jerry-core/parser/js/common.cpp new file mode 100644 index 0000000000..1931bac098 --- /dev/null +++ b/jerry-core/parser/js/common.cpp @@ -0,0 +1,262 @@ +/* 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-helpers.h" +#include "lit-char-helpers.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_utils Utility + * @{ + */ + +/** + * Checks whether the next UTF8 character is a valid identifier start. + * + * @return non-zero if it is. + */ +int +util_is_identifier_start (const uint8_t *src_p) /* pointer to a vaild UTF8 character */ +{ + if (*src_p <= 127) + { + return util_is_identifier_start_character (*src_p); + } + + return util_is_identifier_start_character (lit_utf8_peek_next (src_p)); +} /* util_is_identifier_start */ + +/** + * Checks whether the next UTF8 character is a valid identifier part. + * + * @return non-zero if it is. + */ +int +util_is_identifier_part (const uint8_t *src_p) /* pointer to a vaild UTF8 character */ +{ + if (*src_p <= 127) + { + return util_is_identifier_part_character (*src_p); + } + + return util_is_identifier_part_character (lit_utf8_peek_next (src_p)); +} /* util_is_identifier_part */ + +/** + * Checks whether the character is a valid identifier start. + * + * @return non-zero if it is. + */ +int +util_is_identifier_start_character (uint16_t chr) /**< EcmaScript character */ +{ + if (chr <= 127) + { + return (((chr | 0x20) >= LIT_CHAR_LOWERCASE_A && (chr | 0x20) <= LIT_CHAR_LOWERCASE_Z) + || chr == LIT_CHAR_DOLLAR_SIGN + || chr == LIT_CHAR_UNDERSCORE); + } + + return lit_char_is_unicode_letter (chr); + +} /* util_is_identifier_start_character */ + +/** + * Checks whether the character is a valid identifier part. + * + * @return non-zero if it is. + */ +int +util_is_identifier_part_character (uint16_t chr) /**< EcmaScript character */ +{ + if (chr <= 127) + { + return (((chr | 0x20) >= LIT_CHAR_LOWERCASE_A && (chr | 0x20) <= LIT_CHAR_LOWERCASE_Z) + || (chr >= LIT_CHAR_0 && chr <= LIT_CHAR_9) + || chr == LIT_CHAR_DOLLAR_SIGN + || chr == LIT_CHAR_UNDERSCORE); + } + + return (lit_char_is_unicode_letter (chr) + || lit_char_is_unicode_combining_mark (chr) + || lit_char_is_unicode_digit (chr) + || lit_char_is_unicode_connector_punctuation (chr)); +} /* util_is_identifier_part_character */ + +/** + * Converts a character to UTF8 bytes. + * + * @return length of the UTF8 representation. + */ +size_t +util_to_utf8_bytes (uint8_t *dst_p, /**< destination buffer */ + uint16_t chr) /**< EcmaScript character */ +{ + if (!(chr & ~0x007f)) + { + /* 00000000 0xxxxxxx -> 0xxxxxxx */ + *dst_p = (uint8_t) chr; + return 1; + } + + if (!(chr & ~0x07ff)) + { + /* 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx */ + *(dst_p++) = (uint8_t) (0xc0 | ((chr >> 6) & 0x1f)); + *dst_p = (uint8_t) (0x80 | (chr & 0x3f)); + return 2; + } + + /* zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx */ + *(dst_p++) = (uint8_t) (0xe0 | ((chr >> 12) & 0x0f)); + *(dst_p++) = (uint8_t) (0x80 | ((chr >> 6) & 0x3f)); + *dst_p = (uint8_t) (0x80 | (chr & 0x3f)); + return 3; +} /* util_to_utf8_bytes */ + +/** + * Returns the length of the UTF8 representation of a character. + * + * @return length of the UTF8 representation. + */ +size_t +util_get_utf8_length (uint16_t chr) /**< EcmaScript character */ +{ + if (!(chr & ~0x007f)) + { + /* 00000000 0xxxxxxx */ + return 1; + } + + if (!(chr & ~0x07ff)) + { + /* 00000yyy yyxxxxxx */ + return 2; + } + + /* zzzzyyyy yyxxxxxx */ + return 3; +} /* util_get_utf8_length */ + +/** + * Free literal. + */ +void +util_free_literal (lexer_literal_t *literal_p) /**< literal */ +{ + if (literal_p->type == LEXER_IDENT_LITERAL + || literal_p->type == LEXER_STRING_LITERAL) + { + if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) + { + PARSER_FREE ((uint8_t *) literal_p->u.char_p); + } + } + else if ((literal_p->type == LEXER_FUNCTION_LITERAL) + || (literal_p->type == LEXER_REGEXP_LITERAL)) + { + ecma_bytecode_deref (literal_p->u.bytecode_p); + } +} /* util_free_literal */ + +#ifdef PARSER_DUMP_BYTE_CODE + +/** + * Debug utility to print a character sequence. + */ +static void +util_print_chars (const uint8_t *char_p, /**< character pointer */ + size_t size) /**< size */ +{ + while (size > 0) + { + printf ("%c", *char_p++); + size--; + } +} /* util_print_chars */ + +/** + * Debug utility to print a number. + */ +static void +util_print_number (ecma_number_t num_p) /**< number to print */ +{ + lit_utf8_byte_t str_buf[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; + lit_utf8_size_t str_size = ecma_number_to_utf8_string (num_p, str_buf, sizeof (str_buf)); + str_buf[str_size] = 0; + printf ("%s", str_buf); +} /* util_print_number */ + +/** + * Print literal. + */ +void +util_print_literal (lexer_literal_t *literal_p) /**< literal */ +{ + if (literal_p->type == LEXER_IDENT_LITERAL) + { + if (literal_p->status_flags & LEXER_FLAG_VAR) + { + printf ("var_ident("); + } + else + { + printf ("ident("); + } + util_print_chars (literal_p->u.char_p, literal_p->prop.length); + } + else if (literal_p->type == LEXER_FUNCTION_LITERAL) + { + printf ("function"); + return; + } + else if (literal_p->type == LEXER_STRING_LITERAL) + { + printf ("string("); + util_print_chars (literal_p->u.char_p, literal_p->prop.length); + } + else if (literal_p->type == LEXER_NUMBER_LITERAL) + { + lit_literal_t literal = lit_get_literal_by_cp (literal_p->u.value); + printf ("number("); + util_print_number (lit_number_literal_get_number (literal)); + } + else if (literal_p->type == LEXER_REGEXP_LITERAL) + { + printf ("regexp"); + return; + } + else + { + printf ("unknown"); + return; + } + + printf (")"); +} /* util_print_literal */ + +#endif /* PARSER_DUMP_BYTE_CODE */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/common.h b/jerry-core/parser/js/common.h new file mode 100644 index 0000000000..6e55cfc50b --- /dev/null +++ b/jerry-core/parser/js/common.h @@ -0,0 +1,167 @@ +/* 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. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_utils Utility + * @{ + */ + +#ifndef JERRY_NDEBUG +#define PARSER_DEBUG +#endif + +#ifndef JERRY_NDEBUG +/* Note: This flag is independent from debug mode. */ +#define PARSER_DUMP_BYTE_CODE +#endif + +#include "ecma-globals.h" +#include "ecma-regexp-object.h" +#include "lit-literal.h" +#include "mem-heap.h" + +/* The utilites here are just for compiling purposes, JS + * engines should have an optimized version for them. */ + +/* Malloc functions. */ + +#define PARSER_MALLOC(size) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM) +#define PARSER_FREE(ptr) mem_heap_free_block ((void *) ptr) + +#define PARSER_MALLOC_LOCAL(size) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_SHORT_TERM) +#define PARSER_FREE_LOCAL(ptr) mem_heap_free_block (ptr) + +/* UTF character management. Only ASCII characters are + * supported for simplicity. */ + +int util_is_identifier_start (const uint8_t *); +int util_is_identifier_part (const uint8_t *); +int util_is_identifier_start_character (uint16_t); +int util_is_identifier_part_character (uint16_t); +size_t util_to_utf8_bytes (uint8_t *, uint16_t); +size_t util_get_utf8_length (uint16_t); + +/* Immediate management. */ + +/** + * Literal types. + * + * The LEXER_UNUSED_LITERAL type is internal and + * used for various purposes. + */ +typedef enum +{ + LEXER_IDENT_LITERAL = 0, /**< identifier literal */ + LEXER_STRING_LITERAL = 1, /**< string literal */ + LEXER_NUMBER_LITERAL = 2, /**< number literal */ + LEXER_FUNCTION_LITERAL = 3, /**< function literal */ + LEXER_REGEXP_LITERAL = 4, /**< regexp literal */ + LEXER_UNUSED_LITERAL = 5, /**< unused literal, can only be + used by the byte code generator. */ +} lexer_literal_type_t; + +/* Flags for status_flags. */ + +/* Local identifier (var, function arg). */ +#define LEXER_FLAG_VAR 0x01 +/* This local identifier cannot be stored in register. */ +#define LEXER_FLAG_NO_REG_STORE 0x02 +/* This local identifier is initialized with a value. */ +#define LEXER_FLAG_INITIALIZED 0x04 +/* This local identifier has a reference to the function itself. */ +#define LEXER_FLAG_FUNCTION_NAME 0x08 +/* This local identifier is a function argument. */ +#define LEXER_FLAG_FUNCTION_ARGUMENT 0x10 +/* No space is allocated for this character literal. */ +#define LEXER_FLAG_SOURCE_PTR 0x20 +/* Initialize this variable after the byte code is freed. */ +#define LEXER_FLAG_LATE_INIT 0x40 + +/** + * Literal data. + */ +typedef struct +{ + union + { + lit_cpointer_t value; /**< literal value (not processed by the parser) */ + const uint8_t *char_p; /**< character value */ + ecma_compiled_code_t *bytecode_p; /**< compiled function or regexp pointer */ + uint32_t source_data; /**< encoded source literal */ + } u; + +#ifdef PARSER_DUMP_BYTE_CODE + struct +#else + union +#endif + { + uint16_t length; /**< length of ident / string literal */ + uint16_t index; /**< real index during post processing */ + } prop; + + uint8_t type; /**< type of the literal */ + uint8_t status_flags; /**< status flags */ +} lexer_literal_t; + +void util_free_literal (lexer_literal_t *); + +#ifdef PARSER_DUMP_BYTE_CODE +void util_print_literal (lexer_literal_t *); +#endif /* PARSER_DUMP_BYTE_CODE */ + +/* TRY/CATCH block */ + +#define PARSER_TRY_CONTEXT(context_name) \ + jmp_buf context_name + +#define PARSER_THROW(context_name) \ + longjmp (context_name, 1); + +#define PARSER_TRY(context_name) \ + { \ + if (!setjmp (context_name)) \ + { \ + +#define PARSER_CATCH \ + } \ + else \ + { + +#define PARSER_TRY_END \ + } \ + } + +/* Other */ + +#define PARSER_INLINE inline +#define PARSER_NOINLINE __attribute__ ((noinline)) + +#endif /* !COMMON_H */ diff --git a/jerry-core/parser/js/js-lexer.cpp b/jerry-core/parser/js/js-lexer.cpp new file mode 100644 index 0000000000..d855e0f1e8 --- /dev/null +++ b/jerry-core/parser/js/js-lexer.cpp @@ -0,0 +1,2160 @@ +/* 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 "ecma-alloc.h" +#include "ecma-helpers.h" +#include "ecma-function-object.h" +#include "jerry-snapshot.h" +#include "js-parser-internal.h" +#include "lit-char-helpers.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_lexer Lexer + * @{ + */ + +#define IS_UTF8_INTERMEDIATE_OCTET(byte) (((byte) & 0xc0) == 0x80) + +/** + * Align column to the next tab position. + * + * @return aligned position + */ +static parser_line_counter_t +align_column_to_tab (parser_line_counter_t column) /**< current column */ +{ + /* Tab aligns to zero column start position. */ + return (parser_line_counter_t) (((column + (8u - 1u)) & ~0x7u) + 1u); +} /* align_column_to_tab */ + +/** + * Parse hexadecimal character sequence + * + * @return character value + */ +static ecma_char_t +lexer_hex_to_character (parser_context_t *context_p, /**< context */ + const uint8_t *source_p, /**< current source position */ + int length) /**< source length */ +{ + uint32_t result = 0; + + do + { + uint32_t byte = *source_p++; + + result <<= 4; + + if (byte >= '0' && byte <= '9') + { + result += byte - '0'; + } + else + { + byte |= 0x20; + if (byte >= 'a' && byte <= 'f') + { + result += byte - ('a' - 10); + } + else + { + parser_raise_error (context_p, PARSER_ERR_INVALID_ESCAPE_SEQUENCE); + } + } + } + while (--length > 0); + + return (ecma_char_t) result; +} /* lexer_hex_to_character */ + +/** + * Skip space mode + */ +typedef enum +{ + LEXER_SKIP_SPACES, /**< skip spaces mode */ + LEXER_SKIP_SINGLE_LINE_COMMENT, /**< parse single line comment */ + LEXER_SKIP_MULTI_LINE_COMMENT, /**< parse multi line comment */ +} skip_mode_t; + +/** + * Skip spaces. + */ +static void +skip_spaces (parser_context_t *context_p) /**< context */ +{ + skip_mode_t mode = LEXER_SKIP_SPACES; + const uint8_t *source_end_p = context_p->source_end_p; + + context_p->token.was_newline = 0; + + while (PARSER_TRUE) + { + if (context_p->source_p >= source_end_p) + { + if (mode == LEXER_SKIP_MULTI_LINE_COMMENT) + { + parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_MULTILINE_COMMENT); + } + return; + } + + switch (context_p->source_p[0]) + { + case LIT_CHAR_CR: + { + if (context_p->source_p + 1 < source_end_p + && context_p->source_p[1] == LIT_CHAR_LF) + { + context_p->source_p++; + } + /* FALLTHRU */ + } + + case LIT_CHAR_LF: + { + context_p->line++; + context_p->column = 0; + context_p->token.was_newline = 1; + + if (mode == LEXER_SKIP_SINGLE_LINE_COMMENT) + { + mode = LEXER_SKIP_SPACES; + } + /* FALLTHRU */ + } + + case 0x0b: + case 0x0c: + case 0x20: + { + context_p->source_p++; + context_p->column++; + continue; + /* FALLTHRU */ + } + + case LIT_CHAR_TAB: + { + context_p->column = align_column_to_tab (context_p->column); + context_p->source_p++; + continue; + /* FALLTHRU */ + } + + case '/': + { + if (mode == LEXER_SKIP_SPACES + && context_p->source_p + 1 < source_end_p) + { + if (context_p->source_p[1] == '/') + { + mode = LEXER_SKIP_SINGLE_LINE_COMMENT; + } + else if (context_p->source_p[1] == '*') + { + mode = LEXER_SKIP_MULTI_LINE_COMMENT; + context_p->token.line = context_p->line; + context_p->token.column = context_p->column; + } + + if (mode != LEXER_SKIP_SPACES) + { + context_p->source_p += 2; + PARSER_PLUS_EQUAL_LC (context_p->column, 2); + continue; + } + } + break; + } + + case '*': + { + if (mode == LEXER_SKIP_MULTI_LINE_COMMENT + && context_p->source_p + 1 < source_end_p + && context_p->source_p[1] == '/') + { + mode = LEXER_SKIP_SPACES; + context_p->source_p += 2; + PARSER_PLUS_EQUAL_LC (context_p->column, 2); + continue; + } + break; + } + + case 0xc2: + { + if (context_p->source_p + 1 < source_end_p + && context_p->source_p[1] == 0xa0) + { + /* Codepoint \u00A0 */ + context_p->source_p += 2; + context_p->column++; + continue; + } + break; + } + + case LEXER_NEWLINE_LS_PS_BYTE_1: + { + JERRY_ASSERT (context_p->source_p + 2 < source_end_p); + if (LEXER_NEWLINE_LS_PS_BYTE_23 (context_p->source_p)) + { + /* Codepoint \u2028 and \u2029 */ + context_p->source_p += 3; + context_p->line++; + context_p->column = 1; + context_p->token.was_newline = 1; + + if (mode == LEXER_SKIP_SINGLE_LINE_COMMENT) + { + mode = LEXER_SKIP_SPACES; + } + continue; + } + break; + } + + case 0xef: + { + if (context_p->source_p + 2 < source_end_p + && context_p->source_p[1] == 0xbb + && context_p->source_p[2] == 0xbf) + { + /* Codepoint \uFEFF */ + context_p->source_p += 2; + context_p->column++; + continue; + } + break; + } + + default: + { + break; + } + } + + if (mode == LEXER_SKIP_SPACES) + { + return; + } + + context_p->source_p++; + + if (context_p->source_p < source_end_p + && IS_UTF8_INTERMEDIATE_OCTET (context_p->source_p[0])) + { + context_p->column++; + } + } +} /* skip_spaces */ + +/** + * Keyword data. + */ +typedef struct +{ + const uint8_t *keyword_p; /**< keyword string */ + lexer_token_type_t type; /**< keyword token type */ +} keyword_string_t; + +#define LEXER_KEYWORD(name, type) { (const uint8_t *) (name), (type) } +#define LEXER_KEYWORD_END() { (const uint8_t *) NULL, LEXER_EOS } + +/** + * Keywords with 2 characters. + */ +static const keyword_string_t keyword_length_2[4] = +{ + LEXER_KEYWORD ("do", LEXER_KEYW_DO), + LEXER_KEYWORD ("if", LEXER_KEYW_IF), + LEXER_KEYWORD ("in", LEXER_KEYW_IN), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 3 characters. + */ +static const keyword_string_t keyword_length_3[6] = +{ + LEXER_KEYWORD ("for", LEXER_KEYW_FOR), + LEXER_KEYWORD ("let", LEXER_KEYW_LET), + LEXER_KEYWORD ("new", LEXER_KEYW_NEW), + LEXER_KEYWORD ("try", LEXER_KEYW_TRY), + LEXER_KEYWORD ("var", LEXER_KEYW_VAR), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 4 characters. + */ +static const keyword_string_t keyword_length_4[9] = +{ + LEXER_KEYWORD ("case", LEXER_KEYW_CASE), + LEXER_KEYWORD ("else", LEXER_KEYW_ELSE), + LEXER_KEYWORD ("enum", LEXER_KEYW_ENUM), + LEXER_KEYWORD ("null", LEXER_LIT_NULL), + LEXER_KEYWORD ("this", LEXER_KEYW_THIS), + LEXER_KEYWORD ("true", LEXER_LIT_TRUE), + LEXER_KEYWORD ("void", LEXER_KEYW_VOID), + LEXER_KEYWORD ("with", LEXER_KEYW_WITH), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 5 characters. + */ +static const keyword_string_t keyword_length_5[10] = +{ + LEXER_KEYWORD ("break", LEXER_KEYW_BREAK), + LEXER_KEYWORD ("catch", LEXER_KEYW_CATCH), + LEXER_KEYWORD ("class", LEXER_KEYW_CLASS), + LEXER_KEYWORD ("const", LEXER_KEYW_CONST), + LEXER_KEYWORD ("false", LEXER_LIT_FALSE), + LEXER_KEYWORD ("super", LEXER_KEYW_SUPER), + LEXER_KEYWORD ("throw", LEXER_KEYW_THROW), + LEXER_KEYWORD ("while", LEXER_KEYW_WHILE), + LEXER_KEYWORD ("yield", LEXER_KEYW_YIELD), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 6 characters. + */ +static const keyword_string_t keyword_length_6[9] = +{ + LEXER_KEYWORD ("delete", LEXER_KEYW_DELETE), + LEXER_KEYWORD ("export", LEXER_KEYW_EXPORT), + LEXER_KEYWORD ("import", LEXER_KEYW_IMPORT), + LEXER_KEYWORD ("public", LEXER_KEYW_PUBLIC), + LEXER_KEYWORD ("return", LEXER_KEYW_RETURN), + LEXER_KEYWORD ("static", LEXER_KEYW_STATIC), + LEXER_KEYWORD ("switch", LEXER_KEYW_SWITCH), + LEXER_KEYWORD ("typeof", LEXER_KEYW_TYPEOF), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 7 characters. + */ +static const keyword_string_t keyword_length_7[6] = +{ + LEXER_KEYWORD ("default", LEXER_KEYW_DEFAULT), + LEXER_KEYWORD ("extends", LEXER_KEYW_EXTENDS), + LEXER_KEYWORD ("finally", LEXER_KEYW_FINALLY), + LEXER_KEYWORD ("private", LEXER_KEYW_PRIVATE), + LEXER_KEYWORD ("package", LEXER_KEYW_PACKAGE), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 8 characters. + */ +static const keyword_string_t keyword_length_8[4] = +{ + LEXER_KEYWORD ("continue", LEXER_KEYW_CONTINUE), + LEXER_KEYWORD ("debugger", LEXER_KEYW_DEBUGGER), + LEXER_KEYWORD ("function", LEXER_KEYW_FUNCTION), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 9 characters. + */ +static const keyword_string_t keyword_length_9[3] = +{ + LEXER_KEYWORD ("interface", LEXER_KEYW_INTERFACE), + LEXER_KEYWORD ("protected", LEXER_KEYW_PROTECTED), + LEXER_KEYWORD_END () +}; + +/** + * Keywords with 10 characters. + */ +static const keyword_string_t keyword_length_10[3] = +{ + LEXER_KEYWORD ("instanceof", LEXER_KEYW_INSTANCEOF), + LEXER_KEYWORD ("implements", LEXER_KEYW_IMPLEMENTS), + LEXER_KEYWORD_END () +}; + +/** + * List to the keywords. + */ +static const keyword_string_t * const keyword_string_list[9] = +{ + keyword_length_2, + keyword_length_3, + keyword_length_4, + keyword_length_5, + keyword_length_6, + keyword_length_7, + keyword_length_8, + keyword_length_9, + keyword_length_10 +}; + +#undef LEXER_KEYWORD +#undef LEXER_KEYWORD_END + +/** + * Parse identifier. + */ +static void +lexer_parse_identifier (parser_context_t *context_p, /**< context */ + int check_keywords) /**< check keywords */ +{ + /* Only very few identifiers contains \u escape sequences. */ + const uint8_t *source_p = context_p->source_p; + const uint8_t *ident_start_p = context_p->source_p; + /* Note: newline or tab cannot be part of an identifier. */ + parser_line_counter_t column = context_p->column; + const uint8_t *source_end_p = context_p->source_end_p; + size_t length = 0; + + context_p->token.type = LEXER_LITERAL; + context_p->token.literal_is_reserved = PARSER_FALSE; + context_p->token.lit_location.type = LEXER_IDENT_LITERAL; + context_p->token.lit_location.has_escape = PARSER_FALSE; + + do + { + if (*source_p == '\\') + { + uint16_t character; + + context_p->token.lit_location.has_escape = PARSER_TRUE; + context_p->source_p = source_p; + context_p->token.column = column; + + if ((source_p + 6 > source_end_p) || (source_p[1] != 'u')) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE); + } + + character = lexer_hex_to_character (context_p, source_p + 2, 4); + + if (length == 0) + { + if (!util_is_identifier_start_character (character)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_IDENTIFIER_START); + } + } + else + { + if (!util_is_identifier_part_character (character)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_IDENTIFIER_PART); + } + } + + length += util_get_utf8_length (character); + source_p += 6; + PARSER_PLUS_EQUAL_LC (column, 6); + continue; + } + + /* Valid identifiers cannot contain 4 byte long utf-8 + * characters, since those characters are represented + * by 2 ecmascript (UTF-16) characters, and those + * characters cannot be literal characters. */ + JERRY_ASSERT (source_p[0] < LEXER_UTF8_4BYTE_START); + + source_p++; + length++; + column++; + + while (source_p < source_end_p + && IS_UTF8_INTERMEDIATE_OCTET (source_p[0])) + { + source_p++; + length++; + } + } + while (source_p < source_end_p + && (util_is_identifier_part (source_p) || *source_p == '\\')); + + context_p->source_p = ident_start_p; + context_p->token.column = context_p->column; + + if (length > PARSER_MAXIMUM_IDENT_LENGTH) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_TOO_LONG); + } + + /* Check keywords (Only if there is no \u escape sequence in the pattern). */ + if (check_keywords + && !context_p->token.lit_location.has_escape + && (length >= 2 && length <= 10)) + { + const keyword_string_t *keyword_p = keyword_string_list[length - 2]; + + do + { + if (ident_start_p[0] == keyword_p->keyword_p[0] + && ident_start_p[1] == keyword_p->keyword_p[1] + && memcmp (ident_start_p, keyword_p->keyword_p, length) == 0) + { + if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD) + { + if (context_p->status_flags & PARSER_IS_STRICT) + { + parser_raise_error (context_p, PARSER_ERR_STRICT_IDENT_NOT_ALLOWED); + } + + context_p->token.literal_is_reserved = PARSER_TRUE; + break; + } + + context_p->token.type = keyword_p->type; + break; + } + keyword_p++; + } + while (keyword_p->type != LEXER_EOS); + } + + if (context_p->token.type == LEXER_LITERAL) + { + /* Fill literal data. */ + context_p->token.lit_location.char_p = ident_start_p; + context_p->token.lit_location.length = (uint16_t) length; + } + + context_p->source_p = source_p; + context_p->column = column; +} /* lexer_parse_identifier */ + +/** + * Parse string. + */ +static void +lexer_parse_string (parser_context_t *context_p) /**< context */ +{ + uint8_t str_end_character = context_p->source_p[0]; + const uint8_t *source_p = context_p->source_p + 1; + const uint8_t *string_start_p = source_p; + const uint8_t *source_end_p = context_p->source_end_p; + parser_line_counter_t line = context_p->line; + parser_line_counter_t column = (parser_line_counter_t) (context_p->column + 1); + parser_line_counter_t original_line = line; + parser_line_counter_t original_column = column; + size_t length = 0; + uint8_t has_escape = PARSER_FALSE; + + while (PARSER_TRUE) + { + if (source_p >= source_end_p) + { + context_p->token.line = original_line; + context_p->token.column = (parser_line_counter_t) (original_column - 1); + parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_STRING); + } + + if (*source_p == str_end_character) + { + break; + } + + if (*source_p == '\\') + { + source_p++; + column++; + if (source_p >= source_end_p) + { + /* Will throw an unterminated string error. */ + continue; + } + + has_escape = PARSER_TRUE; + + /* Newline is ignored. */ + if (*source_p == LIT_CHAR_CR + || *source_p == LIT_CHAR_LF + || (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))) + { + if (*source_p == LIT_CHAR_CR) + { + source_p++; + if (source_p < source_end_p + && *source_p == LIT_CHAR_LF) + { + source_p++; + } + } + else if (*source_p == LIT_CHAR_LF) + { + source_p++; + } + else + { + source_p += 3; + } + + line++; + column = 1; + continue; + } + + /* Except \x, \u, and octal numbers, everything is + * converted to a character which has the same byte length. */ + if (*source_p >= '0' && *source_p <= '3') + { + if (context_p->status_flags & PARSER_IS_STRICT) + { + parser_raise_error (context_p, PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED); + } + + source_p++; + column++; + + if (source_p < source_end_p && *source_p >= '0' && *source_p <= '7') + { + source_p++; + column++; + + if (source_p < source_end_p && *source_p >= '0' && *source_p <= '7') + { + /* Numbers >= 0x200 (0x80) requires + * two bytes for encoding in UTF-8. */ + if (source_p[-2] >= '2') + { + length++; + } + + source_p++; + column++; + } + } + + length++; + continue; + } + + if (*source_p >= '4' && *source_p <= '7') + { + if (context_p->status_flags & PARSER_IS_STRICT) + { + parser_raise_error (context_p, PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED); + } + + source_p++; + column++; + + if (source_p < source_end_p && *source_p >= '0' && *source_p <= '7') + { + source_p++; + column++; + } + + /* The maximum number is 0x4d so the UTF-8 + * representation is always one byte. */ + length++; + continue; + } + + if (*source_p == 'x' || *source_p == 'u') + { + uint8_t hex_part_length = (*source_p == 'x') ? 2 : 4; + + context_p->token.line = line; + context_p->token.column = (parser_line_counter_t) (column - 1); + if (source_p + 1 + hex_part_length > source_end_p) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_ESCAPE_SEQUENCE); + } + + length += util_get_utf8_length (lexer_hex_to_character (context_p, + source_p + 1, + hex_part_length)); + source_p += hex_part_length + 1; + PARSER_PLUS_EQUAL_LC (column, hex_part_length + 1u); + continue; + } + } + + if (*source_p >= LEXER_UTF8_4BYTE_START) + { + /* Processing 4 byte unicode sequence (even if it is + * after a backslash). Always converted to two 3 byte + * long sequence. */ + length += 2 * 3; + has_escape = PARSER_TRUE; + source_p += 4; + column++; + continue; + } + else if (*source_p == LIT_CHAR_CR + || *source_p == LIT_CHAR_LF + || (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))) + { + context_p->token.line = line; + context_p->token.column = column; + parser_raise_error (context_p, PARSER_ERR_NEWLINE_NOT_ALLOWED); + } + else if (*source_p == LIT_CHAR_TAB) + { + column = align_column_to_tab (column); + /* Subtract -1 because column is increased below. */ + column--; + } + + source_p++; + column++; + length++; + + while (source_p < source_end_p + && IS_UTF8_INTERMEDIATE_OCTET (*source_p)) + { + source_p++; + length++; + } + } + + if (length > PARSER_MAXIMUM_STRING_LENGTH) + { + parser_raise_error (context_p, PARSER_ERR_STRING_TOO_LONG); + } + + context_p->token.type = LEXER_LITERAL; + + /* Fill literal data. */ + context_p->token.lit_location.char_p = string_start_p; + context_p->token.lit_location.length = (uint16_t) length; + context_p->token.lit_location.type = LEXER_STRING_LITERAL; + context_p->token.lit_location.has_escape = has_escape; + + context_p->source_p = source_p + 1; + context_p->line = line; + context_p->column = (parser_line_counter_t) (column + 1); +} /* lexer_parse_string */ + +/** + * Checks whether the character is hex digit. + * + * @return non-zero if the character is hex digit. + */ +static int +lexer_is_hex_digit (uint8_t character) /**< input character */ +{ + return (character >= '0' && character <= '9') || ((character | 0x20) >= 'a' && (character | 0x20) <= 'f'); +} /* lexer_is_hex_digit */ + +/** + * Parse number. + */ +static void +lexer_parse_number (parser_context_t *context_p) /**< context */ +{ + const uint8_t *source_p = context_p->source_p; + const uint8_t *source_end_p = context_p->source_end_p; + int can_be_float = PARSER_FALSE; + size_t length; + + context_p->token.type = LEXER_LITERAL; + context_p->token.literal_is_reserved = PARSER_FALSE; + context_p->token.extra_value = LEXER_NUMBER_DECIMAL; + context_p->token.lit_location.char_p = source_p; + context_p->token.lit_location.type = LEXER_NUMBER_LITERAL; + context_p->token.lit_location.has_escape = PARSER_FALSE; + + if (source_p[0] == '0' + && source_p + 1 < source_end_p) + { + if ((source_p[1] | 0x20) == 'x') + { + context_p->token.extra_value = LEXER_NUMBER_HEXADECIMAL; + source_p += 2; + + if (source_p >= source_end_p + || !lexer_is_hex_digit (source_p[0])) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_HEX_DIGIT); + } + + do + { + source_p ++; + } + while (source_p < source_end_p + && lexer_is_hex_digit (source_p[0])); + } + else if (source_p[1] >= '0' + && source_p[1] <= '7') + { + context_p->token.extra_value = LEXER_NUMBER_OCTAL; + + if (context_p->status_flags & PARSER_IS_STRICT) + { + parser_raise_error (context_p, PARSER_ERR_OCTAL_NUMBER_NOT_ALLOWED); + } + + do + { + source_p ++; + } + while (source_p < source_end_p + && source_p[0] >= '0' + && source_p[0] <= '7'); + + if (source_p < source_end_p + && source_p[0] >= '8' + && source_p[0] <= '9') + { + parser_raise_error (context_p, PARSER_ERR_INVALID_NUMBER); + } + } + else if (source_p[1] >= '8' + && source_p[1] <= '9') + { + parser_raise_error (context_p, PARSER_ERR_INVALID_NUMBER); + } + else + { + can_be_float = PARSER_TRUE; + source_p++; + } + } + else + { + do + { + source_p++; + } + while (source_p < source_end_p + && source_p[0] >= '0' + && source_p[0] <= '9'); + + can_be_float = PARSER_TRUE; + } + + if (can_be_float) + { + if (source_p < source_end_p + && source_p[0] == '.') + { + source_p++; + while (source_p < source_end_p + && source_p[0] >= '0' + && source_p[0] <= '9') + { + source_p++; + } + } + + if (source_p < source_end_p + && (source_p[0] | 0x20) == 'e') + { + source_p++; + + if (source_p < source_end_p + && (source_p[0] == '+' || source_p[0] == '-')) + { + source_p++; + } + + if (source_p >= source_end_p + || source_p[0] < '0' + || source_p[0] > '9') + { + parser_raise_error (context_p, PARSER_ERR_MISSING_EXPONENT); + } + + do + { + source_p++; + } + while (source_p < source_end_p + && source_p[0] >= '0' + && source_p[0] <= '9'); + } + } + + if (source_p < source_end_p + && (util_is_identifier_start (source_p) || source_p[0] == '\\')) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_AFTER_NUMBER); + } + + length = (size_t) (source_p - context_p->source_p); + if (length > PARSER_MAXIMUM_IDENT_LENGTH) + { + parser_raise_error (context_p, PARSER_ERR_NUMBER_TOO_LONG); + } + + context_p->token.lit_location.length = (uint16_t) length; + PARSER_PLUS_EQUAL_LC (context_p->column, length); + context_p->source_p = source_p; +} /* lexer_parse_number */ + +#define LEXER_TYPE_A_TOKEN(char1, type1) \ + case (uint8_t) (char1) : \ + { \ + context_p->token.type = (type1); \ + length = 1; \ + break; \ + } + +#define LEXER_TYPE_B_TOKEN(char1, type1, char2, type2) \ + case (uint8_t) (char1) : \ + { \ + if (length >= 2 && context_p->source_p[1] == (uint8_t) (char2)) \ + { \ + context_p->token.type = (type2); \ + length = 2; \ + break; \ + } \ + \ + context_p->token.type = (type1); \ + length = 1; \ + break; \ + } + +#define LEXER_TYPE_C_TOKEN(char1, type1, char2, type2, char3, type3) \ + case (uint8_t) (char1) : \ + { \ + if (length >= 2) \ + { \ + if (context_p->source_p[1] == (uint8_t) (char2)) \ + { \ + context_p->token.type = (type2); \ + length = 2; \ + break; \ + } \ + \ + if (context_p->source_p[1] == (uint8_t) (char3)) \ + { \ + context_p->token.type = (type3); \ + length = 2; \ + break; \ + } \ + } \ + \ + context_p->token.type = (type1); \ + length = 1; \ + break; \ + } + +#define LEXER_TYPE_D_TOKEN(char1, type1, char2, type2, char3, type3) \ + case (uint8_t) (char1) : \ + { \ + if (length >= 2 && context_p->source_p[1] == (uint8_t) (char2)) \ + { \ + if (length >= 3 && context_p->source_p[2] == (uint8_t) (char3)) \ + { \ + context_p->token.type = (type3); \ + length = 3; \ + break; \ + } \ + \ + context_p->token.type = (type2); \ + length = 2; \ + break; \ + } \ + \ + context_p->token.type = (type1); \ + length = 1; \ + break; \ + } + +/** + * Get next token. + */ +void +lexer_next_token (parser_context_t *context_p) /**< context */ +{ + size_t length; + + skip_spaces (context_p); + + context_p->token.line = context_p->line; + context_p->token.column = context_p->column; + + length = (size_t) (context_p->source_end_p - context_p->source_p); + if (length == 0) + { + context_p->token.type = LEXER_EOS; + return; + } + + if (util_is_identifier_start (context_p->source_p) + || context_p->source_p[0] == '\\') + { + lexer_parse_identifier (context_p, PARSER_TRUE); + return; + } + + if (context_p->source_p[0] >= '0' && context_p->source_p[0] <= '9') + { + lexer_parse_number (context_p); + return; + } + + switch (context_p->source_p[0]) + { + LEXER_TYPE_A_TOKEN ('{', LEXER_LEFT_BRACE); + LEXER_TYPE_A_TOKEN ('(', LEXER_LEFT_PAREN); + LEXER_TYPE_A_TOKEN ('[', LEXER_LEFT_SQUARE); + LEXER_TYPE_A_TOKEN ('}', LEXER_RIGHT_BRACE); + LEXER_TYPE_A_TOKEN (')', LEXER_RIGHT_PAREN); + LEXER_TYPE_A_TOKEN (']', LEXER_RIGHT_SQUARE); + LEXER_TYPE_A_TOKEN (';', LEXER_SEMICOLON); + LEXER_TYPE_A_TOKEN (',', LEXER_COMMA); + + case (uint8_t) '.' : + { + if (length >= 2 + && (context_p->source_p[1] >= '0' && context_p->source_p[1] <= '9')) + { + lexer_parse_number (context_p); + return; + } + + context_p->token.type = LEXER_DOT; + length = 1; + break; + } + + case (uint8_t) '<': + { + if (length >= 2) + { + if (context_p->source_p[1] == (uint8_t) '=') + { + context_p->token.type = LEXER_LESS_EQUAL; + length = 2; + break; + } + + if (context_p->source_p[1] == (uint8_t) '<') + { + if (length >= 3 && context_p->source_p[2] == (uint8_t) '=') + { + context_p->token.type = LEXER_ASSIGN_LEFT_SHIFT; + length = 3; + break; + } + + context_p->token.type = LEXER_LEFT_SHIFT; + length = 2; + break; + } + } + + context_p->token.type = LEXER_LESS; + length = 1; + break; + } + + case '>': + { + if (length >= 2) + { + if (context_p->source_p[1] == (uint8_t) '=') + { + context_p->token.type = LEXER_GREATER_EQUAL; + length = 2; + break; + } + + if (context_p->source_p[1] == (uint8_t) '>') + { + if (length >= 3) + { + if (context_p->source_p[2] == (uint8_t) '=') + { + context_p->token.type = LEXER_ASSIGN_RIGHT_SHIFT; + length = 3; + break; + } + + if (context_p->source_p[2] == (uint8_t) '>') + { + if (length >= 4 && context_p->source_p[3] == (uint8_t) '=') + { + context_p->token.type = LEXER_ASSIGN_UNS_RIGHT_SHIFT; + length = 4; + break; + } + + context_p->token.type = LEXER_UNS_RIGHT_SHIFT; + length = 3; + break; + } + } + + context_p->token.type = LEXER_RIGHT_SHIFT; + length = 2; + break; + } + } + + context_p->token.type = LEXER_GREATER; + length = 1; + break; + } + + LEXER_TYPE_D_TOKEN ('=', LEXER_ASSIGN, '=', LEXER_EQUAL, '=', LEXER_STRICT_EQUAL) + LEXER_TYPE_D_TOKEN ('!', LEXER_LOGICAL_NOT, '=', LEXER_NOT_EQUAL, '=', LEXER_STRICT_NOT_EQUAL) + + LEXER_TYPE_C_TOKEN ('+', LEXER_ADD, '=', LEXER_ASSIGN_ADD, '+', LEXER_INCREASE) + LEXER_TYPE_C_TOKEN ('-', LEXER_SUBTRACT, '=', LEXER_ASSIGN_SUBTRACT, '-', LEXER_DECREASE) + + LEXER_TYPE_B_TOKEN ('*', LEXER_MULTIPLY, '=', LEXER_ASSIGN_MULTIPLY) + LEXER_TYPE_B_TOKEN ('/', LEXER_DIVIDE, '=', LEXER_ASSIGN_DIVIDE) + LEXER_TYPE_B_TOKEN ('%', LEXER_MODULO, '=', LEXER_ASSIGN_MODULO) + + LEXER_TYPE_C_TOKEN ('&', LEXER_BIT_AND, '=', LEXER_ASSIGN_BIT_AND, '&', LEXER_LOGICAL_AND) + LEXER_TYPE_C_TOKEN ('|', LEXER_BIT_OR, '=', LEXER_ASSIGN_BIT_OR, '|', LEXER_LOGICAL_OR) + + LEXER_TYPE_B_TOKEN ('^', LEXER_BIT_XOR, '=', LEXER_ASSIGN_BIT_XOR) + + LEXER_TYPE_A_TOKEN ('~', LEXER_BIT_NOT); + LEXER_TYPE_A_TOKEN ('?', LEXER_QUESTION_MARK); + LEXER_TYPE_A_TOKEN (':', LEXER_COLON); + + case '\'': + case '"': + { + lexer_parse_string (context_p); + return; + } + + default: + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + } + + context_p->source_p += length; + PARSER_PLUS_EQUAL_LC (context_p->column, length); +} /* lexer_next_token */ + +#undef LEXER_TYPE_A_TOKEN +#undef LEXER_TYPE_B_TOKEN +#undef LEXER_TYPE_C_TOKEN +#undef LEXER_TYPE_D_TOKEN + +/** + * Search or append the string to the literal pool. + */ +static void +lexer_process_char_literal (parser_context_t *context_p, /**< context */ + const uint8_t *char_p, /**< characters */ + size_t length, /**< length of string */ + uint8_t literal_type, /**< final literal type */ + uint8_t has_escape) /**< has escape sequences */ +{ + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + uint32_t literal_index = 0; + + JERRY_ASSERT (literal_type == LEXER_IDENT_LITERAL + || literal_type == LEXER_STRING_LITERAL); + + JERRY_ASSERT (literal_type != LEXER_IDENT_LITERAL || length <= PARSER_MAXIMUM_IDENT_LENGTH); + JERRY_ASSERT (literal_type != LEXER_STRING_LITERAL || length <= PARSER_MAXIMUM_STRING_LENGTH); + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (literal_p->type == literal_type + && literal_p->prop.length == length + && memcmp (literal_p->u.char_p, char_p, length) == 0) + { + context_p->lit_object.literal_p = literal_p; + context_p->lit_object.index = (uint16_t) literal_index; + return; + } + + literal_index++; + } + + JERRY_ASSERT (literal_index == context_p->literal_count); + + if (literal_index >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->prop.length = (uint16_t) length; + literal_p->type = literal_type; + literal_p->status_flags = has_escape ? 0 : LEXER_FLAG_SOURCE_PTR; + + if (has_escape) + { + literal_p->u.char_p = (uint8_t *) PARSER_MALLOC (length); + memcpy ((uint8_t *) literal_p->u.char_p, char_p, length); + } + else + { + literal_p->u.char_p = char_p; + } + + context_p->lit_object.literal_p = literal_p; + context_p->lit_object.index = (uint16_t) literal_index; + context_p->literal_count++; +} /* lexer_process_char_literal */ + +/* Maximum buffer size for identifiers which contains escape sequences. */ +#define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48 + +/** + * Construct a literal object from an identifier. + */ +void +lexer_construct_literal_object (parser_context_t *context_p, /**< context */ + lexer_lit_location_t *literal_p, /**< literal location */ + uint8_t literal_type) /**< final literal type */ +{ + uint8_t *destination_start_p; + uint8_t *destination_p; + const uint8_t *source_p; + uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE]; + + JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL + || literal_p->type == LEXER_STRING_LITERAL); + JERRY_ASSERT (context_p->allocated_buffer_p == NULL); + + destination_start_p = local_byte_array; + source_p = literal_p->char_p; + + if (literal_p->has_escape) + { + if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE) + { + destination_start_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length); + context_p->allocated_buffer_p = destination_start_p; + } + + destination_p = destination_start_p; + + if (literal_p->type == LEXER_IDENT_LITERAL) + { + const uint8_t *source_end_p = context_p->source_end_p; + + JERRY_ASSERT (literal_p->length <= PARSER_MAXIMUM_IDENT_LENGTH); + + do + { + if (*source_p == '\\') + { + destination_p += util_to_utf8_bytes (destination_p, + lexer_hex_to_character (context_p, source_p + 2, 4)); + source_p += 6; + continue; + } + + *destination_p++ = *source_p++; + + while (source_p < source_end_p + && IS_UTF8_INTERMEDIATE_OCTET (*source_p)) + { + *destination_p++ = *source_p++; + } + } + while (source_p < source_end_p + && (util_is_identifier_part (source_p) || *source_p == '\\')); + + JERRY_ASSERT (destination_p == destination_start_p + literal_p->length); + } + else + { + uint8_t str_end_character = source_p[-1]; + + while (PARSER_TRUE) + { + if (*source_p == str_end_character) + { + break; + } + + if (*source_p == '\\') + { + uint8_t conv_character; + + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + + /* Newline is ignored. */ + if (*source_p == LIT_CHAR_CR + || *source_p == LIT_CHAR_LF + || (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))) + { + if (*source_p == LIT_CHAR_CR) + { + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + + if (*source_p == LIT_CHAR_LF) + { + source_p++; + } + } + else if (*source_p == LIT_CHAR_LF) + { + source_p++; + } + else + { + source_p += 3; + } + continue; + } + + if (*source_p >= '0' && *source_p <= '3') + { + uint32_t octal_number = (uint32_t) (*source_p - '0'); + + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + + if (*source_p >= '0' && *source_p <= '7') + { + octal_number = octal_number * 8 + (uint32_t) (*source_p - '0'); + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + + if (*source_p >= '0' && *source_p <= '7') + { + octal_number = octal_number * 8 + (uint32_t) (*source_p - '0'); + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + } + } + + destination_p += util_to_utf8_bytes (destination_p, (uint16_t) octal_number); + continue; + } + + if (*source_p >= '4' && *source_p <= '7') + { + uint32_t octal_number = (uint32_t) (*source_p - '0'); + + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + + if (*source_p >= '0' && *source_p <= '7') + { + octal_number = octal_number * 8 + (uint32_t) (*source_p - '0'); + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); + } + + *destination_p++ = (uint8_t) octal_number; + continue; + } + + if (*source_p == 'x' || *source_p == 'u') + { + int hex_part_length = (*source_p == 'x') ? 2 : 4; + JERRY_ASSERT (source_p + 1 + hex_part_length <= context_p->source_end_p); + + destination_p += util_to_utf8_bytes (destination_p, + lexer_hex_to_character (context_p, + source_p + 1, + hex_part_length)); + source_p += hex_part_length + 1; + continue; + } + + conv_character = *source_p; + switch (*source_p) + { + case 'b': + { + conv_character = 0x08; + break; + } + case 't': + { + conv_character = 0x09; + break; + } + case 'n': + { + conv_character = 0x0a; + break; + } + case 'v': + { + conv_character = 0x0b; + break; + } + case 'f': + { + conv_character = 0x0c; + break; + } + case 'r': + { + conv_character = 0x0d; + break; + } + } + + if (conv_character != *source_p) + { + *destination_p++ = conv_character; + source_p++; + continue; + } + } + + if (*source_p >= LEXER_UTF8_4BYTE_START) + { + /* Processing 4 byte unicode sequence (even if it is + * after a backslash). Always converted to two 3 byte + * long sequence. */ + + uint32_t character = ((((uint32_t) source_p[0]) & 0x7) << 18); + character |= ((((uint32_t) source_p[1]) & 0x3f) << 12); + character |= ((((uint32_t) source_p[2]) & 0x3f) << 6); + character |= (((uint32_t) source_p[3]) & 0x3f); + + JERRY_ASSERT (character >= 0x10000); + character -= 0x10000; + destination_p += util_to_utf8_bytes (destination_p, + (ecma_char_t) (0xd800 | (character >> 10))); + destination_p += util_to_utf8_bytes (destination_p, + (ecma_char_t) (0xdc00 | (character & 0x3ff))); + source_p += 4; + continue; + } + + *destination_p++ = *source_p++; + + /* There is no need to check the source_end_p + * since the string is terminated by a quotation mark. */ + while (IS_UTF8_INTERMEDIATE_OCTET (*source_p)) + { + *destination_p++ = *source_p++; + } + } + + JERRY_ASSERT (destination_p == destination_start_p + literal_p->length); + } + + source_p = destination_start_p; + } + + lexer_process_char_literal (context_p, + source_p, + literal_p->length, + literal_type, + literal_p->has_escape); + + context_p->lit_object.type = LEXER_LITERAL_OBJECT_ANY; + + if (literal_type == LEXER_IDENT_LITERAL) + { + if ((context_p->status_flags & PARSER_INSIDE_WITH) + && context_p->lit_object.literal_p->type == LEXER_IDENT_LITERAL) + { + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + } + + if (literal_p->length == 4 + && source_p[0] == 'e' + && source_p[3] == 'l' + && source_p[1] == 'v' + && source_p[2] == 'a') + { + context_p->lit_object.type = LEXER_LITERAL_OBJECT_EVAL; + } + + if (literal_p->length == 9 + && source_p[0] == 'a' + && source_p[8] == 's' + && memcmp (source_p + 1, "rgument", 7) == 0) + { + context_p->lit_object.type = LEXER_LITERAL_OBJECT_ARGUMENTS; + if (!(context_p->status_flags & PARSER_ARGUMENTS_NOT_NEEDED)) + { + context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED; + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + } + } + } + + if (destination_start_p != local_byte_array) + { + JERRY_ASSERT (context_p->allocated_buffer_p == destination_start_p); + + context_p->allocated_buffer_p = NULL; + parser_free_local (destination_start_p); + } + + JERRY_ASSERT (context_p->allocated_buffer_p == NULL); +} /* lexer_construct_literal_object */ + +#undef LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE + +/** + * Construct a number object. + * + * @return PARSER_TRUE if number is small number + */ +int +lexer_construct_number_object (parser_context_t *context_p, /**< context */ + int push_number_allowed, /**< push number support is allowed */ + int is_negative_number) /**< sign is negative */ +{ + lexer_literal_t *literal_p; + ecma_number_t num; + uint16_t literal_count = context_p->literal_count; + + if (context_p->token.extra_value != LEXER_NUMBER_OCTAL) + { + num = ecma_utf8_string_to_number (context_p->token.lit_location.char_p, + context_p->token.lit_location.length); + } + else + { + const uint8_t *src_p = context_p->token.lit_location.char_p; + const uint8_t *src_end_p = src_p + context_p->token.lit_location.length - 1; + + num = 0; + do + { + src_p++; + num = num * 8 + (ecma_number_t) (*src_p - '0'); + } + while (src_p < src_end_p); + } + + if (push_number_allowed) + { + int32_t int_num = (int32_t) num; + + if (int_num == num) + { + if (int_num < CBC_PUSH_NUMBER_1_RANGE_END + && (int_num != 0 || !is_negative_number)) + { + context_p->lit_object.index = (uint16_t) int_num; + return PARSER_TRUE; + } + } + } + + if (is_negative_number) + { + num = -num; + } + + if (literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->prop.length = context_p->token.lit_location.length; + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = 0; + + context_p->literal_count++; + + literal_p->u.value = rcs_cpointer_compress (lit_find_or_create_literal_from_num (num)); + literal_p->type = LEXER_NUMBER_LITERAL; + + context_p->lit_object.literal_p = literal_p; + context_p->lit_object.index = literal_count; + context_p->lit_object.type = LEXER_LITERAL_OBJECT_ANY; + + return PARSER_FALSE; +} /* lexer_construct_number_object */ + +/** + * Construct a function literal object. + */ +void +lexer_construct_function_object (parser_context_t *context_p, /**< context */ + uint32_t extra_status_flags) /**< extra status flags */ +{ + ecma_compiled_code_t *compiled_code_p; + lexer_literal_t *literal_p; + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + if (context_p->status_flags & PARSER_RESOLVE_THIS_FOR_CALLS) + { + extra_status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; + } + + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = 0; + + context_p->literal_count++; + + compiled_code_p = parser_parse_function (context_p, extra_status_flags); + + literal_p->u.bytecode_p = compiled_code_p; + + literal_p->type = LEXER_FUNCTION_LITERAL; + context_p->status_flags |= PARSER_NO_REG_STORE; +} /* lexer_construct_function_object */ + +/** + * Construct a regular expression object. + */ +void +lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ + int parse_only) /**< parse only */ +{ +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN + const uint8_t *source_p = context_p->source_p; + const uint8_t *regex_start_p = context_p->source_p; + const uint8_t *regex_end_p = regex_start_p; + const uint8_t *source_end_p = context_p->source_end_p; + parser_line_counter_t column = context_p->column; + lexer_literal_t *literal_p; + int in_class = PARSER_FALSE; + uint16_t current_flags; + lit_utf8_size_t length; + + JERRY_ASSERT (context_p->token.type == LEXER_DIVIDE + || context_p->token.type == LEXER_ASSIGN_DIVIDE); + + if (context_p->token.type == LEXER_ASSIGN_DIVIDE) + { + regex_start_p--; + } + + while (PARSER_TRUE) + { + if (source_p >= source_end_p) + { + parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_REGEXP); + } + + if (!in_class && source_p[0] == '/') + { + regex_end_p = source_p; + source_p++; + column++; + break; + } + + switch (source_p[0]) + { + case LIT_CHAR_CR: + case LIT_CHAR_LF: + case LEXER_NEWLINE_LS_PS_BYTE_1: + { + if (source_p[0] != LEXER_NEWLINE_LS_PS_BYTE_1 + || LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) + { + parser_raise_error (context_p, PARSER_ERR_NEWLINE_NOT_ALLOWED); + } + break; + } + case LIT_CHAR_TAB: + { + column = align_column_to_tab (column); + /* Subtract -1 because column is increased below. */ + column--; + break; + } + case '[': + { + in_class = PARSER_TRUE; + break; + } + case ']': + { + in_class = PARSER_FALSE; + break; + } + case '\\': + { + if (source_p + 1 >= source_end_p) + { + parser_raise_error (context_p, PARSER_ERR_UNTERMINATED_REGEXP); + } + + if (source_p[1] >= 0x20 && source_p[1] <= 0x7f) + { + source_p++; + column++; + } + } + } + + source_p++; + column++; + + while (source_p < source_end_p + && IS_UTF8_INTERMEDIATE_OCTET (source_p[0])) + { + source_p++; + } + } + + /* FIXME: This is duplicate of 're_parse_regexp_flags'. Move this to a helper function. */ + current_flags = 0; + while (source_p < source_end_p) + { + uint32_t flag = 0; + + if (source_p[0] == 'g') + { + flag = RE_FLAG_GLOBAL; + } + else if (source_p[0] == 'i') + { + flag = RE_FLAG_IGNORE_CASE; + } + else if (source_p[0] == 'm') + { + flag = RE_FLAG_MULTILINE; + } + + if (flag == 0) + { + break; + } + + if (current_flags & flag) + { + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_REGEXP_FLAG); + } + + current_flags |= (uint16_t) flag; + source_p++; + column++; + } + + if (source_p < source_end_p + && util_is_identifier_part (source_p)) + { + parser_raise_error (context_p, PARSER_ERR_UNKNOWN_REGEXP_FLAG); + } + + context_p->source_p = source_p; + context_p->column = column; + + length = (lit_utf8_size_t) (regex_end_p - regex_start_p); + if (length > PARSER_MAXIMUM_STRING_LENGTH) + { + parser_raise_error (context_p, PARSER_ERR_REGEXP_TOO_LONG); + } + + context_p->column = column; + context_p->source_p = source_p; + + if (parse_only) + { + return; + } + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + literal_p->prop.length = (uint16_t) length; + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = 0; + + context_p->literal_count++; + + /* Compile the RegExp literal and store the RegExp bytecode pointer */ + re_compiled_code_t *re_bytecode_p = NULL; + ecma_completion_value_t completion_value; + + ecma_string_t *pattern_str_p = ecma_new_ecma_string_from_utf8 (regex_start_p, length); + // FIXME: check return value of 're_compile_bytecode' and throw an error + completion_value = re_compile_bytecode (&re_bytecode_p, + pattern_str_p, + current_flags); + ecma_deref_ecma_string (pattern_str_p); + + bool is_throw = ecma_is_completion_value_throw (completion_value); + + ecma_free_completion_value (completion_value); + + if (is_throw) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_REGEXP); + } + +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + if (snapshot_report_byte_code_compilation) + { + snapshot_add_compiled_code ((ecma_compiled_code_t *) re_bytecode_p, + regex_start_p, + length); + } +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ + + literal_p->type = LEXER_REGEXP_LITERAL; + literal_p->u.bytecode_p = (ecma_compiled_code_t *) re_bytecode_p; + + context_p->token.type = LEXER_LITERAL; + context_p->token.literal_is_reserved = PARSER_FALSE; + context_p->token.lit_location.type = LEXER_REGEXP_LITERAL; + + context_p->lit_object.literal_p = literal_p; + context_p->lit_object.index = (uint16_t) (context_p->literal_count - 1); + context_p->lit_object.type = LEXER_LITERAL_OBJECT_ANY; +#else + (void) parse_only; + parser_raise_error (context_p, PARSER_ERR_UNSUPPORTED_REGEXP); +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ +} /* lexer_construct_regexp_object */ + +/** + * Next token must be an identifier. + */ +void +lexer_expect_identifier (parser_context_t *context_p, /**< context */ + uint8_t literal_type) /**< literal type */ +{ + JERRY_ASSERT (literal_type == LEXER_STRING_LITERAL + || literal_type == LEXER_IDENT_LITERAL); + + skip_spaces (context_p); + context_p->token.line = context_p->line; + context_p->token.column = context_p->column; + + if (context_p->source_p < context_p->source_end_p + && (util_is_identifier_start (context_p->source_p) || context_p->source_p[0] == '\\')) + { + lexer_parse_identifier (context_p, literal_type != LEXER_STRING_LITERAL); + + if (context_p->token.type == LEXER_LITERAL) + { + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + literal_type); + + if (literal_type == LEXER_IDENT_LITERAL + && (context_p->status_flags & PARSER_IS_STRICT) + && context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) + { + parser_error_t error; + + if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_EVAL) + { + error = PARSER_ERR_EVAL_NOT_ALLOWED; + } + else + { + JERRY_ASSERT (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS); + error = PARSER_ERR_ARGUMENTS_NOT_ALLOWED; + } + + parser_raise_error (context_p, error); + } + + context_p->token.lit_location.type = literal_type; + return; + } + } + + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); +} /* lexer_expect_identifier */ + +static lexer_lit_location_t lexer_get_literal = +{ + (const uint8_t *) "get", 3, LEXER_IDENT_LITERAL, PARSER_FALSE +}; + +static lexer_lit_location_t lexer_set_literal = +{ + (const uint8_t *) "set", 3, LEXER_IDENT_LITERAL, PARSER_FALSE +}; + +/** + * Next token must be an identifier. + */ +void +lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ + int must_be_identifier) /**< only identifiers are accepted */ +{ + skip_spaces (context_p); + + context_p->token.line = context_p->line; + context_p->token.column = context_p->column; + + if (context_p->source_p < context_p->source_end_p) + { + int create_literal_object = PARSER_FALSE; + + if (util_is_identifier_start (context_p->source_p) || context_p->source_p[0] == '\\') + { + lexer_parse_identifier (context_p, PARSER_FALSE); + + if (!must_be_identifier + && context_p->token.lit_location.length == 3) + { + skip_spaces (context_p); + + if (context_p->source_p < context_p->source_end_p + && context_p->source_p[0] != ':') + { + if (lexer_same_identifiers (&context_p->token.lit_location, &lexer_get_literal)) + { + context_p->token.type = LEXER_PROPERTY_GETTER; + return; + } + else if (lexer_same_identifiers (&context_p->token.lit_location, &lexer_set_literal)) + { + context_p->token.type = LEXER_PROPERTY_SETTER; + return; + } + } + } + + create_literal_object = PARSER_TRUE; + } + else if (context_p->source_p[0] == '"' + || context_p->source_p[0] == '\'') + { + lexer_parse_string (context_p); + create_literal_object = PARSER_TRUE; + } + else if (!must_be_identifier && context_p->source_p[0] == '}') + { + context_p->token.type = LEXER_RIGHT_BRACE; + context_p->source_p += 1; + context_p->column++; + return; + } + else + { + const uint8_t *char_p = context_p->source_p; + + if (char_p[0] == '.') + { + char_p++; + } + + if (char_p < context_p->source_end_p + && char_p[0] >= '0' + && char_p[0] <= '9') + { + lexer_parse_number (context_p); + lexer_construct_number_object (context_p, PARSER_FALSE, PARSER_FALSE); + return; + } + } + + if (create_literal_object) + { + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + LEXER_STRING_LITERAL); + return; + } + } + + parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED); +} /* lexer_expect_object_literal_id */ + +/** + * Next token must be an identifier. + */ +void +lexer_scan_identifier (parser_context_t *context_p, /**< context */ + int propety_name) /**< property name */ +{ + skip_spaces (context_p); + context_p->token.line = context_p->line; + context_p->token.column = context_p->column; + + if (context_p->source_p < context_p->source_end_p + && (util_is_identifier_start (context_p->source_p) || context_p->source_p[0] == '\\')) + { + lexer_parse_identifier (context_p, PARSER_FALSE); + + if (propety_name && context_p->token.lit_location.length == 3) + { + skip_spaces (context_p); + + if (context_p->source_p < context_p->source_end_p + && context_p->source_p[0] != ':') + { + if (lexer_same_identifiers (&context_p->token.lit_location, &lexer_get_literal)) + { + context_p->token.type = LEXER_PROPERTY_GETTER; + } + else if (lexer_same_identifiers (&context_p->token.lit_location, &lexer_set_literal)) + { + context_p->token.type = LEXER_PROPERTY_SETTER; + } + } + } + return; + } + + if (propety_name) + { + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_LITERAL + || context_p->token.type == LEXER_RIGHT_BRACE) + { + return; + } + } + + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); +} /* lexer_scan_identifier */ + +/** + * Converts a "\uxxxx" sequence into a unicode character + * + * @return the decoded 16 bit unicode character + */ +static ecma_char_t +lexer_decode_unicode_sequence (const uint8_t *source_p) /**< source pointer */ +{ + ecma_char_t chr = 0; + const uint8_t *source_end_p = source_p + 6; + + source_p += 2; + do + { + uint8_t byte = *source_p++; + chr = (ecma_char_t) (chr << 4); + if (byte <= '9') + { + chr = (ecma_char_t) (chr + byte - '0'); + } + else + { + chr = (ecma_char_t) (chr + LEXER_TO_ASCII_LOWERCASE (byte) - ('a' - 10)); + } + } + while (source_p < source_end_p); + + return chr; +} /* lexer_decode_unicode_sequence */ + +/** + * Compares two identifiers. + * + * @return non-zero if the input identifiers are the same + */ +int +lexer_same_identifiers (lexer_lit_location_t *left, /**< left identifier */ + lexer_lit_location_t *right) /**< right identifier */ +{ + const uint8_t *left_p; + const uint8_t *right_p; + size_t count; + + JERRY_ASSERT (left->length > 0 && right->length > 0); + + if (left->length != right->length) + { + return 0; + } + + if (!left->has_escape && !right->has_escape) + { + return memcmp (left->char_p, right->char_p, left->length) == 0; + } + + left_p = left->char_p; + right_p = right->char_p; + count = left->length; + + do + { + uint8_t utf8_buf[3]; + size_t utf8_len, offset; + + /* Backslash cannot be part of a multibyte UTF-8 character. */ + if (*left_p != '\\' && *right_p != '\\') + { + if (*left_p++ != *right_p++) + { + return PARSER_FALSE; + } + count--; + continue; + } + + if (*left_p == '\\' && *right_p == '\\') + { + uint16_t left_chr = lexer_decode_unicode_sequence (left_p); + + if (left_chr != lexer_decode_unicode_sequence (right_p)) + { + return PARSER_FALSE; + } + + left_p += 6; + right_p += 6; + count += util_get_utf8_length (left_chr); + continue; + } + + /* One character is encoded as unicode sequence. */ + if (*right_p == '\\') + { + /* The pointers can be swapped. */ + const uint8_t *swap_p = left_p; + left_p = right_p; + right_p = swap_p; + } + + utf8_len = util_to_utf8_bytes (utf8_buf, lexer_decode_unicode_sequence (left_p)); + JERRY_ASSERT (utf8_len > 0); + count -= utf8_len; + offset = 0; + + do + { + if (utf8_buf[offset] != *right_p++) + { + return PARSER_FALSE; + } + offset++; + } + while (offset < utf8_len); + + left_p += 6; + } + while (count > 0); + + return PARSER_TRUE; +} /* lexer_same_identifiers */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h new file mode 100644 index 0000000000..06373beee5 --- /dev/null +++ b/jerry-core/parser/js/js-lexer.h @@ -0,0 +1,270 @@ +/* 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. + */ + +#ifndef JS_LEXER_H +#define JS_LEXER_H + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_lexer Lexer + * @{ + */ + +/** + * Lexer token types. + */ +typedef enum +{ + LEXER_EOS, /**< end of source */ + + /* Primary expressions */ + LEXER_LITERAL, /**< literal token */ + LEXER_KEYW_THIS, /**< this */ + LEXER_LIT_TRUE, /**< true (not a keyword!) */ + LEXER_LIT_FALSE, /**< false (not a keyword!) */ + LEXER_LIT_NULL, /**< null (not a keyword!) */ + + /* Unary operators + * IMPORTANT: update CBC_UNARY_OP_TOKEN_TO_OPCODE and + * CBC_UNARY_LVALUE_OP_TOKEN_TO_OPCODE after changes. */ +#define LEXER_IS_UNARY_OP_TOKEN(token_type) \ + ((token_type) >= LEXER_PLUS && (token_type) <= LEXER_DECREASE) +#define LEXER_IS_UNARY_LVALUE_OP_TOKEN(token_type) \ + ((token_type) >= LEXER_KEYW_DELETE && (token_type) <= LEXER_DECREASE) + + LEXER_PLUS, /**< + */ + LEXER_NEGATE, /**< - */ + LEXER_LOGICAL_NOT, /**< ! */ + LEXER_BIT_NOT, /**< ~ */ + LEXER_KEYW_VOID, /**< void */ + LEXER_KEYW_TYPEOF, /**< typeof */ + LEXER_KEYW_DELETE, /**< delete */ + LEXER_INCREASE, /**< ++ */ + LEXER_DECREASE, /**< -- */ + + /* Binary operators + * IMPORTANT: update CBC_BINARY_OP_TOKEN_TO_OPCODE, + * CBC_BINARY_LVALUE_OP_TOKEN_TO_OPCODE and + * parser_binary_precedence_table after changes. */ +#define LEXER_IS_BINARY_OP_TOKEN(token_type) \ + ((token_type) >= LEXER_ASSIGN && (token_type) <= LEXER_MODULO) +#define LEXER_IS_BINARY_LVALUE_TOKEN(token_type) \ + ((token_type) >= LEXER_ASSIGN && (token_type) <= LEXER_ASSIGN_BIT_XOR) +#define LEXER_FIRST_BINARY_OP LEXER_ASSIGN + + LEXER_ASSIGN, /**< = (prec: 3) */ + LEXER_ASSIGN_ADD, /**< += (prec: 3) */ + LEXER_ASSIGN_SUBTRACT, /**< -= (prec: 3) */ + LEXER_ASSIGN_MULTIPLY, /**< *= (prec: 3) */ + LEXER_ASSIGN_DIVIDE, /**< /= (prec: 3) */ + LEXER_ASSIGN_MODULO, /**< %= (prec: 3) */ + LEXER_ASSIGN_LEFT_SHIFT, /**< <<= (prec: 3) */ + LEXER_ASSIGN_RIGHT_SHIFT, /**< >>= (prec: 3) */ + LEXER_ASSIGN_UNS_RIGHT_SHIFT, /**< >>>= (prec: 3) */ + LEXER_ASSIGN_BIT_AND, /**< &= (prec: 3) */ + LEXER_ASSIGN_BIT_OR, /**< |= (prec: 3) */ + LEXER_ASSIGN_BIT_XOR, /**< ^= (prec: 3) */ + LEXER_QUESTION_MARK, /**< ? (prec: 4) */ + LEXER_LOGICAL_OR, /**< || (prec: 5) */ + LEXER_LOGICAL_AND, /**< && (prec: 6) */ + LEXER_BIT_OR, /**< | (prec: 7) */ + LEXER_BIT_XOR, /**< ^ (prec: 8) */ + LEXER_BIT_AND, /**< & (prec: 9) */ + LEXER_EQUAL, /**< == (prec: 10) */ + LEXER_NOT_EQUAL, /**< != (prec: 10) */ + LEXER_STRICT_EQUAL, /**< === (prec: 10) */ + LEXER_STRICT_NOT_EQUAL, /**< !== (prec: 10) */ + LEXER_LESS, /**< < (prec: 11) */ + LEXER_GREATER, /**< > (prec: 11) */ + LEXER_LESS_EQUAL, /**< <= (prec: 11) */ + LEXER_GREATER_EQUAL, /**< >= (prec: 11) */ + LEXER_KEYW_IN, /**< in (prec: 11) */ + LEXER_KEYW_INSTANCEOF, /**< instanceof (prec: 11) */ + LEXER_LEFT_SHIFT, /**< << (prec: 12) */ + LEXER_RIGHT_SHIFT, /**< >> (prec: 12) */ + LEXER_UNS_RIGHT_SHIFT, /**< >>> (prec: 12) */ + LEXER_ADD, /**< + (prec: 13) */ + LEXER_SUBTRACT, /**< - (prec: 13) */ + LEXER_MULTIPLY, /**< * (prec: 14) */ + LEXER_DIVIDE, /**< / (prec: 14) */ + LEXER_MODULO, /**< % (prec: 14) */ + + LEXER_LEFT_BRACE, /**< { */ + LEXER_LEFT_PAREN, /**< ( */ + LEXER_LEFT_SQUARE, /**< [ */ + LEXER_RIGHT_BRACE, /**< } */ + LEXER_RIGHT_PAREN, /**<_) */ + LEXER_RIGHT_SQUARE, /**< ] */ + LEXER_DOT, /**< . */ + LEXER_SEMICOLON, /**< ; */ + LEXER_COLON, /**< : */ + LEXER_COMMA, /**< , */ + + LEXER_KEYW_BREAK, /**< break */ + LEXER_KEYW_DO, /**< do */ + LEXER_KEYW_CASE, /**< case */ + LEXER_KEYW_ELSE, /**< else */ + LEXER_KEYW_NEW, /**< new */ + LEXER_KEYW_VAR, /**< var */ + LEXER_KEYW_CATCH, /**< catch */ + LEXER_KEYW_FINALLY, /**< finally */ + LEXER_KEYW_RETURN, /**< return */ + LEXER_KEYW_CONTINUE, /**< continue */ + LEXER_KEYW_FOR, /**< for */ + LEXER_KEYW_SWITCH, /**< switch */ + LEXER_KEYW_WHILE, /**< while */ + LEXER_KEYW_DEBUGGER, /**< debugger */ + LEXER_KEYW_FUNCTION, /**< function */ + LEXER_KEYW_WITH, /**< with */ + LEXER_KEYW_DEFAULT, /**< default */ + LEXER_KEYW_IF, /**< if */ + LEXER_KEYW_THROW, /**< throw */ + LEXER_KEYW_TRY, /**< try */ + + /* These are virtual tokens. */ + LEXER_EXPRESSION_START, /**< expression start */ + LEXER_PROPERTY_GETTER, /**< property getter function */ + LEXER_PROPERTY_SETTER, /**< property setter function */ + LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */ + LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */ + + /* Future reserved words: these keywords + * must form a group after all other keywords. */ +#define LEXER_FIRST_FUTURE_RESERVED_WORD LEXER_KEYW_CLASS + LEXER_KEYW_CLASS, /**< class */ + LEXER_KEYW_ENUM, /**< enum */ + LEXER_KEYW_EXTENDS, /**< extends */ + LEXER_KEYW_SUPER, /**< super */ + LEXER_KEYW_CONST, /**< const */ + LEXER_KEYW_EXPORT, /**< export */ + LEXER_KEYW_IMPORT, /**< import */ + + /* Future strict reserved words: these keywords + * must form a group after future reserved words. */ +#define LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD LEXER_KEYW_IMPLEMENTS + LEXER_KEYW_IMPLEMENTS, /**< implements */ + LEXER_KEYW_LET, /**< let */ + LEXER_KEYW_PRIVATE, /**< private */ + LEXER_KEYW_PUBLIC, /**< public */ + LEXER_KEYW_YIELD, /**< yield */ + LEXER_KEYW_INTERFACE, /**< interface */ + LEXER_KEYW_PACKAGE, /**< package */ + LEXER_KEYW_PROTECTED, /**< protected */ + LEXER_KEYW_STATIC, /**< static */ +} lexer_token_type_t; + +#define LEXER_NEWLINE_LS_PS_BYTE_1 0xe2 +#define LEXER_NEWLINE_LS_PS_BYTE_23(source) ((source)[1] == 0x80 && ((source)[2] | 0x1) == 0xa9) +#define LEXER_UTF8_4BYTE_START 0xf0 + +#define LEXER_IS_LEFT_BRACKET(type) \ + ((type) == LEXER_LEFT_BRACE || (type) == LEXER_LEFT_PAREN || (type) == LEXER_LEFT_SQUARE) + +#define LEXER_IS_RIGHT_BRACKET(type) \ + ((type) == LEXER_RIGHT_BRACE || (type) == LEXER_RIGHT_PAREN || (type) == LEXER_RIGHT_SQUARE) + +#define LEXER_UNARY_OP_TOKEN_TO_OPCODE(token_type) \ + ((((token_type) - LEXER_PLUS) * 2) + CBC_PLUS) + +#define LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE(token_type) \ + ((((token_type) - LEXER_KEYW_DELETE) * 6) + CBC_DELETE) + +#define LEXER_BINARY_OP_TOKEN_TO_OPCODE(token_type) \ + ((cbc_opcode_t) ((((token_type) - LEXER_BIT_OR) * 3) + CBC_BIT_OR)) + +#define LEXER_BINARY_LVALUE_OP_TOKEN_TO_OPCODE(token_type) \ + ((cbc_opcode_t) ((((token_type) - LEXER_ASSIGN_ADD) * 2) + CBC_ASSIGN_ADD)) + +#define LEXER_TO_ASCII_LOWERCASE(character) ((character) | 0x20) + +/** + * Lexer literal object types. + */ +typedef enum +{ + LEXER_LITERAL_OBJECT_ANY, /**< unspecified object type */ + LEXER_LITERAL_OBJECT_EVAL, /**< reference is equal to eval */ + LEXER_LITERAL_OBJECT_ARGUMENTS, /**< reference is equal to arguments */ +} lexer_literal_object_type_t; + +/** + * Lexer number types. + */ +typedef enum +{ + LEXER_NUMBER_DECIMAL, /**< decimal number */ + LEXER_NUMBER_HEXADECIMAL, /**< hexadecimal number */ + LEXER_NUMBER_OCTAL, /**< octal number */ +} lexer_number_type_t; + +/** + * Lexer character (string / identifier) literal data. + */ +typedef struct +{ + const uint8_t *char_p; /**< start of identifier or string token */ + uint16_t length; /**< length or index of a literal */ + uint8_t type; /**< type of the current literal */ + uint8_t has_escape; /**< has escape sequences */ +} lexer_lit_location_t; + +/** + * Range of input string which processing is postponed. + */ +typedef struct +{ + const uint8_t *source_p; /**< next source byte */ + const uint8_t *source_end_p; /**< last source byte */ + parser_line_counter_t line; /**< token start line */ + parser_line_counter_t column; /**< token start column */ +} lexer_range_t; + +/** + * Lexer token. + */ +typedef struct +{ + uint8_t type; /**< token type */ + uint8_t literal_is_reserved; /**< future reserved keyword + * (when char_literal.type is LEXER_IDENT_LITERAL) */ + uint8_t extra_value; /**< helper value for different purposes */ + uint8_t was_newline; /**< newline occured before this token */ + parser_line_counter_t line; /**< token start line */ + parser_line_counter_t column; /**< token start column */ + lexer_lit_location_t lit_location; /**< extra data for character literals */ +} lexer_token_t; + +/** + * Literal data set by lexer_construct_literal_object. + */ +typedef struct +{ + lexer_literal_t *literal_p; /**< pointer to the literal object */ + uint16_t index; /**< literal index */ + uint8_t type; /**< literal object type */ +} lexer_lit_object_t; + +/** + * @} + * @} + * @} + */ + +#endif /* !JS_LEXER_H */ diff --git a/jerry-core/parser/js/js-parser-expr.cpp b/jerry-core/parser/js/js-parser-expr.cpp new file mode 100644 index 0000000000..31d0af4c33 --- /dev/null +++ b/jerry-core/parser/js/js-parser-expr.cpp @@ -0,0 +1,1505 @@ +/* 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 "js-parser-internal.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_expr Expression parser + * @{ + */ + +/** + * Precedence of the binary tokens. + * + * See also: + * lexer_token_type_t + */ +static const uint8_t parser_binary_precedence_table[36] = +{ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 12, 12, 12, + 13, 13, 14, 14, 14 +}; + +/** + * Generate byte code for operators with lvalue. + */ +static PARSER_INLINE void +parser_push_result (parser_context_t *context_p) /**< context */ +{ + if (CBC_NO_RESULT_COMPOUND_ASSIGMENT (context_p->last_cbc_opcode)) + { + context_p->last_cbc_opcode = (uint16_t) PARSER_TO_BINARY_OPERATION_WITH_RESULT (context_p->last_cbc_opcode); + parser_flush_cbc (context_p); + } + else if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) + { + JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1)); + + if (context_p->last_cbc_opcode == CBC_POST_INCR + || context_p->last_cbc_opcode == CBC_POST_DECR) + { + if (context_p->stack_depth >= context_p->stack_limit) + { + /* Stack limit is increased for CBC_POST_INCR_PUSH_RESULT + * and CBC_POST_DECR_PUSH_RESULT opcodes. Needed by vm.cpp. */ + JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); + + context_p->stack_limit++; + + if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + } + } + + context_p->last_cbc_opcode++; + parser_flush_cbc (context_p); + } +} /* parser_push_result */ + +/** + * Generate byte code for operators with lvalue. + */ +static void +parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ + cbc_opcode_t opcode) /**< opcode */ +{ + if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, opcode + CBC_UNARY_LVALUE_WITH_IDENT)); + + if (context_p->status_flags & PARSER_IS_STRICT) + { + if (context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY) + { + parser_error_t error; + + if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) + { + error = PARSER_ERR_EVAL_CANNOT_ASSIGNED; + } + else + { + JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS); + error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED; + } + parser_raise_error (context_p, error); + } + if (opcode == CBC_DELETE) + { + parser_raise_error (context_p, PARSER_ERR_DELETE_IDENT_NOT_ALLOWED); + } + } + + if (opcode == CBC_DELETE) + { + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + } + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT); + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_emit_cbc_literal (context_p, + (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT), + context_p->last_cbc.value); + } + else + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); + + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + parser_emit_cbc_literal (context_p, + (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT), + context_p->last_cbc.third_literal_index); + } + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, opcode)); + context_p->last_cbc_opcode = (uint16_t) opcode; + } + else + { + switch (context_p->last_cbc_opcode) + { + case CBC_PUSH_PROP_LITERAL: + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_PUSH_LITERAL)); + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + break; + } + case CBC_PUSH_PROP_LITERAL_LITERAL: + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS)); + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + break; + } + case CBC_PUSH_PROP_THIS_LITERAL: + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_PUSH_THIS_LITERAL)); + context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; + break; + } + default: + { + /* Invalid LeftHandSide expression. */ + parser_emit_cbc_ext (context_p, (opcode == CBC_DELETE) ? CBC_EXT_PUSH_UNDEFINED_BASE + : CBC_EXT_THROW_REFERENCE_ERROR); + break; + } + } + parser_emit_cbc (context_p, opcode); + } +} /* parser_emit_unary_lvalue_opcode */ + +/** + * Parse array literal. + */ +static void +parser_parse_array_literal (parser_context_t *context_p) /**< context */ +{ + uint32_t pushed_items = 0; + + JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); + + parser_emit_cbc (context_p, CBC_CREATE_ARRAY); + lexer_next_token (context_p); + + while (PARSER_TRUE) + { + if (context_p->token.type == LEXER_RIGHT_SQUARE) + { + if (pushed_items > 0) + { + parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items); + } + return; + } + + pushed_items++; + + if (context_p->token.type == LEXER_COMMA) + { + parser_emit_cbc (context_p, CBC_PUSH_ELISION); + lexer_next_token (context_p); + } + else + { + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + + if (context_p->token.type == LEXER_COMMA) + { + lexer_next_token (context_p); + } + else if (context_p->token.type != LEXER_RIGHT_SQUARE) + { + parser_raise_error (context_p, PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED); + } + } + + if (pushed_items >= 64) + { + parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items); + pushed_items = 0; + } + } +} /* parser_parse_array_literal */ + +/** + * Object literal item types. + */ +typedef enum +{ + PARSER_OBJECT_PROPERTY_START, /**< marks the start of the property list */ + PARSER_OBJECT_PROPERTY_VALUE, /**< value property */ + PARSER_OBJECT_PROPERTY_GETTER, /**< getter property */ + PARSER_OBJECT_PROPERTY_SETTER, /**< setter property */ + PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS, /**< both getter and setter properties are set */ +} parser_object_literal_item_types_t; + +/** + * Parse object literal. + */ +static void +parser_append_object_literal_item (parser_context_t *context_p, /**< context */ + uint16_t item_index, /**< index of the item name */ + parser_object_literal_item_types_t item_type) /**< type of the item */ +{ + parser_stack_iterator_t iterator; + uint8_t *current_item_type_p; + + iterator.current_p = context_p->stack.first_p; + iterator.current_position = context_p->stack.last_position; + + while (PARSER_TRUE) + { + current_item_type_p = iterator.current_p->bytes + iterator.current_position - 1; + + if (*current_item_type_p == PARSER_OBJECT_PROPERTY_START) + { + parser_stack_push_uint16 (context_p, item_index); + parser_stack_push_uint8 (context_p, (uint8_t) item_type); + return; + } + + iterator.current_position--; + if (iterator.current_position == 0) + { + iterator.current_p = iterator.current_p->next_p; + iterator.current_position = PARSER_STACK_PAGE_SIZE; + } + + uint32_t current_item_index = iterator.current_p->bytes[iterator.current_position - 1]; + + iterator.current_position--; + if (iterator.current_position == 0) + { + iterator.current_p = iterator.current_p->next_p; + iterator.current_position = PARSER_STACK_PAGE_SIZE; + } + + current_item_index |= ((uint32_t) iterator.current_p->bytes[iterator.current_position - 1]) << 8; + + iterator.current_position--; + if (iterator.current_position == 0) + { + iterator.current_p = iterator.current_p->next_p; + iterator.current_position = PARSER_STACK_PAGE_SIZE; + } + + if (current_item_index == item_index) + { + if (item_type == PARSER_OBJECT_PROPERTY_VALUE + && *current_item_type_p == PARSER_OBJECT_PROPERTY_VALUE + && !(context_p->status_flags & PARSER_IS_STRICT)) + { + return; + } + + if (item_type == PARSER_OBJECT_PROPERTY_GETTER + && *current_item_type_p == PARSER_OBJECT_PROPERTY_SETTER) + { + break; + } + + if (item_type == PARSER_OBJECT_PROPERTY_SETTER + && *current_item_type_p == PARSER_OBJECT_PROPERTY_GETTER) + { + break; + } + + parser_raise_error (context_p, PARSER_ERR_OBJECT_PROPERTY_REDEFINED); + } + } + + uint8_t *last_page_p = context_p->stack.first_p->bytes; + + *current_item_type_p = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; + + if (current_item_type_p == (last_page_p + context_p->stack.last_position - 1)) + { + context_p->stack_top_uint8 = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; + } +} /* parser_append_object_literal_item */ + +/** + * Parse object literal. + */ +static void +parser_parse_object_literal (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); + + parser_emit_cbc (context_p, CBC_CREATE_OBJECT); + + parser_stack_push_uint8 (context_p, PARSER_OBJECT_PROPERTY_START); + + while (PARSER_TRUE) + { + lexer_expect_object_literal_id (context_p, PARSER_FALSE); + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + break; + } + + if (context_p->token.type == LEXER_PROPERTY_GETTER + || context_p->token.type == LEXER_PROPERTY_SETTER) + { + uint32_t status_flags; + cbc_ext_opcode_t opcode; + uint16_t literal_index; + parser_object_literal_item_types_t item_type; + + if (context_p->token.type == LEXER_PROPERTY_GETTER) + { + status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_GETTER; + opcode = CBC_EXT_SET_GETTER; + item_type = PARSER_OBJECT_PROPERTY_GETTER; + } + else + { + status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_SETTER; + opcode = CBC_EXT_SET_SETTER; + item_type = PARSER_OBJECT_PROPERTY_SETTER; + } + + if (context_p->status_flags & PARSER_INSIDE_WITH) + { + status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; + } + + lexer_expect_object_literal_id (context_p, PARSER_TRUE); + literal_index = context_p->lit_object.index; + + parser_append_object_literal_item (context_p, literal_index, item_type); + + parser_flush_cbc (context_p); + lexer_construct_function_object (context_p, status_flags); + + parser_emit_cbc_literal (context_p, + CBC_PUSH_LITERAL, + literal_index); + + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); + context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1); + + lexer_next_token (context_p); + } + else + { + uint16_t literal_index = context_p->lit_object.index; + + parser_append_object_literal_item (context_p, + literal_index, + PARSER_OBJECT_PROPERTY_VALUE); + + lexer_next_token (context_p); + if (context_p->token.type != LEXER_COLON) + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + + parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); + } + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + break; + } + else if (context_p->token.type != LEXER_COMMA) + { + parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); + } + } + + while (context_p->stack_top_uint8 != PARSER_OBJECT_PROPERTY_START) + { + parser_stack_pop (context_p, NULL, 3); + } + + parser_stack_pop_uint8 (context_p); +} /* parser_parse_object_literal */ + +/** + * Parse and record unary operators, and parse the primary literal. + */ +static void +parser_parse_unary_expression (parser_context_t *context_p, /**< context */ + size_t *grouping_level_p) /**< grouping level */ +{ + int new_was_seen = 0; + + /* Collect unary operators. */ + while (PARSER_TRUE) + { + /* Convert plus and minus binary operators to unary operators. */ + if (context_p->token.type == LEXER_ADD) + { + context_p->token.type = LEXER_PLUS; + } + else if (context_p->token.type == LEXER_SUBTRACT) + { + context_p->token.type = LEXER_NEGATE; + } + + /* Bracketed expressions are primary expressions. At this + * point their left paren is pushed onto the stack and + * they are processed when their closing paren is reached. */ + if (context_p->token.type == LEXER_LEFT_PAREN) + { + (*grouping_level_p)++; + new_was_seen = 0; + } + else if (context_p->token.type == LEXER_KEYW_NEW) + { + /* After 'new' unary operators are not allowed. */ + new_was_seen = 1; + } + else if (new_was_seen || !LEXER_IS_UNARY_OP_TOKEN (context_p->token.type)) + { + break; + } + + parser_stack_push_uint8 (context_p, context_p->token.type); + lexer_next_token (context_p); + } + + /* Parse primary expression. */ + switch (context_p->token.type) + { + case LEXER_LITERAL: + { + cbc_opcode_t opcode = CBC_PUSH_LITERAL; + + if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL + || context_p->token.lit_location.type == LEXER_STRING_LITERAL) + { + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + context_p->token.lit_location.type); + } + else if (context_p->token.lit_location.type == LEXER_NUMBER_LITERAL) + { + int is_negative_number = PARSER_FALSE; + + while (context_p->stack_top_uint8 == LEXER_PLUS + || context_p->stack_top_uint8 == LEXER_NEGATE) + { + if (context_p->stack_top_uint8 == LEXER_NEGATE) + { + is_negative_number = !is_negative_number; + } + parser_stack_pop_uint8 (context_p); + } + + if (lexer_construct_number_object (context_p, PARSER_TRUE, is_negative_number)) + { + JERRY_ASSERT (context_p->lit_object.index < CBC_PUSH_NUMBER_1_RANGE_END); + + if (context_p->lit_object.index == 0) + { + parser_emit_cbc (context_p, CBC_PUSH_NUMBER_0); + break; + } + + parser_emit_cbc_push_number (context_p, is_negative_number); + break; + } + } + + if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_EVAL) + { + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + context_p->last_cbc.value = context_p->lit_object.index; + context_p->last_cbc.literal_type = context_p->token.lit_location.type; + context_p->last_cbc.literal_object_type = context_p->lit_object.type; + break; + } + + if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; + context_p->last_cbc.third_literal_index = context_p->lit_object.index; + context_p->last_cbc.literal_type = context_p->token.lit_location.type; + context_p->last_cbc.literal_object_type = context_p->lit_object.type; + break; + } + + if (context_p->last_cbc_opcode == CBC_PUSH_THIS) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_PUSH_THIS_LITERAL; + } + } + + parser_emit_cbc_literal_from_token (context_p, opcode); + break; + } + case LEXER_KEYW_FUNCTION: + { + int literals = 0; + uint16_t literal1 = 0; + uint16_t literal2 = 0; + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + literals = 1; + literal1 = context_p->last_cbc.literal_index; + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + literals = 2; + literal1 = context_p->last_cbc.literal_index; + literal2 = context_p->last_cbc.value; + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else + { + parser_flush_cbc (context_p); + } + + uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_FUNC_EXPRESSION | PARSER_IS_CLOSURE; + + if (context_p->status_flags & PARSER_INSIDE_WITH) + { + status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; + } + + lexer_construct_function_object (context_p, status_flags); + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + + if (literals == 1) + { + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + context_p->last_cbc.literal_index = literal1; + context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1); + } + else if (literals == 2) + { + context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; + context_p->last_cbc.literal_index = literal1; + context_p->last_cbc.value = literal2; + context_p->last_cbc.third_literal_index = (uint16_t) (context_p->literal_count - 1); + } + else + { + parser_emit_cbc_literal (context_p, + CBC_PUSH_LITERAL, + (uint16_t) (context_p->literal_count - 1)); + } + + context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; + context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; + break; + } + case LEXER_LEFT_BRACE: + { + parser_parse_object_literal (context_p); + break; + } + case LEXER_LEFT_SQUARE: + { + parser_parse_array_literal (context_p); + break; + } + case LEXER_DIVIDE: + case LEXER_ASSIGN_DIVIDE: + { + lexer_construct_regexp_object (context_p, PARSER_FALSE); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1); + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; + context_p->last_cbc.third_literal_index = (uint16_t) (context_p->literal_count - 1); + } + else + { + parser_emit_cbc_literal (context_p, + CBC_PUSH_LITERAL, + (uint16_t) (context_p->literal_count - 1)); + } + + context_p->last_cbc.literal_type = LEXER_REGEXP_LITERAL; + context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; + break; + } + case LEXER_KEYW_THIS: + { + parser_emit_cbc (context_p, CBC_PUSH_THIS); + break; + } + case LEXER_LIT_TRUE: + { + parser_emit_cbc (context_p, CBC_PUSH_TRUE); + break; + } + case LEXER_LIT_FALSE: + { + parser_emit_cbc (context_p, CBC_PUSH_FALSE); + break; + } + case LEXER_LIT_NULL: + { + parser_emit_cbc (context_p, CBC_PUSH_NULL); + break; + } + default: + { + parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); + break; + } + } + lexer_next_token (context_p); +} /* parser_parse_unary_expression */ + +/** + * Parse the postfix part of unary operators, and + * generate byte code for the whole expression. + */ +static void +parser_process_unary_expression (parser_context_t *context_p) /**< context */ +{ + /* Parse postfix part of a primary expression. */ + while (PARSER_TRUE) + { + /* Since break would only break the switch, we use + * continue to continue this loop. Without continue, + * the code abandons the loop. */ + switch (context_p->token.type) + { + case LEXER_DOT: + { + parser_push_result (context_p); + + lexer_expect_identifier (context_p, LEXER_STRING_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_STRING_LITERAL); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + JERRY_ASSERT (CBC_ARGS_EQ (CBC_PUSH_PROP_LITERAL_LITERAL, + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL; + context_p->last_cbc.value = context_p->lit_object.index; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_THIS) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_THIS_LITERAL); + } + else + { + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_LITERAL); + } + lexer_next_token (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_LEFT_SQUARE: + { + parser_push_result (context_p); + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR); + if (context_p->token.type != LEXER_RIGHT_SQUARE) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED); + } + lexer_next_token (context_p); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_THIS_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL; + } + else + { + parser_emit_cbc (context_p, CBC_PUSH_PROP); + } + continue; + /* FALLTHRU */ + } + + case LEXER_LEFT_PAREN: + { + size_t call_arguments = 0; + uint16_t opcode = CBC_CALL; + int is_eval = PARSER_FALSE; + + parser_push_result (context_p); + + if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) + { + parser_stack_pop_uint8 (context_p); + opcode = CBC_NEW; + } + else + { + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL + && context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) + { + JERRY_ASSERT (context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL); + context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; + is_eval = PARSER_TRUE; + } + + if (context_p->last_cbc_opcode == CBC_PUSH_PROP) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_REFERENCE; + opcode = CBC_CALL_PROP; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_REFERENCE; + opcode = CBC_CALL_PROP; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE; + opcode = CBC_CALL_PROP; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE; + opcode = CBC_CALL_PROP; + } + else if ((context_p->status_flags & (PARSER_INSIDE_WITH | PARSER_RESOLVE_THIS_FOR_CALLS)) + && PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) + { + opcode = CBC_CALL_PROP; + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_IDENT_REFERENCE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_emit_cbc_literal (context_p, + CBC_PUSH_IDENT_REFERENCE, + context_p->last_cbc.value); + } + else + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); + + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + parser_emit_cbc_literal (context_p, + CBC_PUSH_IDENT_REFERENCE, + context_p->last_cbc.third_literal_index); + } + } + } + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + while (PARSER_TRUE) + { + if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) + { + parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); + } + + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + + if (context_p->token.type != LEXER_COMMA) + { + break; + } + lexer_next_token (context_p); + } + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + } + + lexer_next_token (context_p); + + if (is_eval) + { + parser_emit_cbc (context_p, CBC_EVAL); + } + + if (call_arguments == 0) + { + if (opcode == CBC_CALL) + { + parser_emit_cbc (context_p, CBC_CALL0); + continue; + } + if (opcode == CBC_CALL_PROP) + { + parser_emit_cbc (context_p, CBC_CALL0_PROP); + continue; + } + if (opcode == CBC_NEW) + { + parser_emit_cbc (context_p, CBC_NEW0); + continue; + } + } + + if (call_arguments == 1) + { + if (opcode == CBC_CALL) + { + parser_emit_cbc (context_p, CBC_CALL1); + continue; + } + if (opcode == CBC_CALL_PROP) + { + parser_emit_cbc (context_p, CBC_CALL1_PROP); + continue; + } + if (opcode == CBC_NEW) + { + parser_emit_cbc (context_p, CBC_NEW1); + continue; + } + } + + if (call_arguments == 2) + { + if (opcode == CBC_CALL) + { + parser_emit_cbc (context_p, CBC_CALL2); + continue; + } + if (opcode == CBC_CALL_PROP) + { + parser_flush_cbc (context_p); + /* Manually adjusting stack usage. */ + JERRY_ASSERT (context_p->stack_depth > 0); + context_p->stack_depth--; + parser_emit_cbc (context_p, CBC_CALL2_PROP); + continue; + } + } + + parser_emit_cbc_call (context_p, opcode, call_arguments); + continue; + /* FALLTHRU */ + } + + default: + { + if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) + { + parser_push_result (context_p); + parser_emit_cbc (context_p, CBC_NEW0); + parser_stack_pop_uint8 (context_p); + continue; + } + + if (!context_p->token.was_newline + && (context_p->token.type == LEXER_INCREASE || context_p->token.type == LEXER_DECREASE)) + { + cbc_opcode_t opcode = (context_p->token.type == LEXER_INCREASE) ? CBC_POST_INCR : CBC_POST_DECR; + parser_push_result (context_p); + parser_emit_unary_lvalue_opcode (context_p, opcode); + lexer_next_token (context_p); + } + break; + } + } + break; + } + + /* Generate byte code for the unary operators. */ + while (PARSER_TRUE) + { + uint8_t token = context_p->stack_top_uint8; + if (!LEXER_IS_UNARY_OP_TOKEN (token)) + { + break; + } + + parser_push_result (context_p); + parser_stack_pop_uint8 (context_p); + + if (LEXER_IS_UNARY_LVALUE_OP_TOKEN (token)) + { + token = (uint8_t) (LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE (token)); + parser_emit_unary_lvalue_opcode (context_p, (cbc_opcode_t) token); + } + else + { + token = (uint8_t) (LEXER_UNARY_OP_TOKEN_TO_OPCODE (token)); + + if (token == CBC_TYPEOF) + { + if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) + { + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_TYPEOF_IDENT; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_emit_cbc_literal (context_p, + CBC_TYPEOF_IDENT, + context_p->last_cbc.value); + } + else + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); + + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + parser_emit_cbc_literal (context_p, + CBC_TYPEOF_IDENT, + context_p->last_cbc.third_literal_index); + } + } + else + { + parser_emit_cbc (context_p, token); + } + } + else + { + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + /* It is not worth to combine with push multiple literals + * since the byte code size will not decrease. */ + JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, token + 1)); + context_p->last_cbc_opcode = (uint16_t) (token + 1); + } + else + { + parser_emit_cbc (context_p, token); + } + } + } + } +} /* parser_process_unary_expression */ + +/** + * Append a binary token. + */ +static void +parser_append_binary_token (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)); + + parser_push_result (context_p); + + if (context_p->token.type == LEXER_ASSIGN) + { + /* Unlike other tokens, the whole byte code is saved for binary + * assignment, since it has multiple forms depending on the + * previous instruction. */ + + if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, CBC_ASSIGN_SET_IDENT)); + + if ((context_p->status_flags & PARSER_IS_STRICT) + && context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY) + { + parser_error_t error; + + if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) + { + error = PARSER_ERR_EVAL_CANNOT_ASSIGNED; + } + else + { + JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS); + error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED; + } + parser_raise_error (context_p, error); + } + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + parser_stack_push_uint16 (context_p, context_p->last_cbc.value); + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + } + else + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); + + parser_stack_push_uint16 (context_p, context_p->last_cbc.third_literal_index); + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + } + parser_stack_push_uint8 (context_p, CBC_ASSIGN_SET_IDENT); + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_ASSIGN)); + parser_stack_push_uint8 (context_p, CBC_ASSIGN); + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) + { + if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_ASSIGN_PROP_LITERAL)); + parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); + parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_LITERAL); + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else + { + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_stack_push_uint8 (context_p, CBC_ASSIGN); + } + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS)); + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + parser_stack_push_uint8 (context_p, CBC_ASSIGN); + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) + { + if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_ASSIGN_PROP_THIS_LITERAL)); + parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); + parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_THIS_LITERAL); + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else + { + context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; + parser_stack_push_uint8 (context_p, CBC_ASSIGN); + } + } + else + { + /* Invalid LeftHandSide expression. */ + parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); + parser_stack_push_uint8 (context_p, CBC_ASSIGN); + } + } + else if (LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type)) + { + if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) + { + if ((context_p->status_flags & PARSER_IS_STRICT) + && context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY) + { + parser_error_t error; + + if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) + { + error = PARSER_ERR_EVAL_CANNOT_ASSIGNED; + } + else + { + JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS); + error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED; + } + parser_raise_error (context_p, error); + } + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_PUSH_IDENT_REFERENCE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_PUSH_LITERAL; + parser_emit_cbc_literal (context_p, + CBC_PUSH_IDENT_REFERENCE, + context_p->last_cbc.value); + } + else + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); + + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + parser_emit_cbc_literal (context_p, + CBC_PUSH_IDENT_REFERENCE, + context_p->last_cbc.third_literal_index); + } + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_PUSH_PROP_REFERENCE)); + context_p->last_cbc_opcode = CBC_PUSH_PROP_REFERENCE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, + CBC_PUSH_PROP_LITERAL_REFERENCE)); + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_REFERENCE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, + CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE)); + context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, + CBC_PUSH_PROP_THIS_LITERAL_REFERENCE)); + context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE; + } + else + { + /* Invalid LeftHandSide expression. */ + parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); + parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE); + } + } + else if (context_p->token.type == LEXER_LOGICAL_OR + || context_p->token.type == LEXER_LOGICAL_AND) + { + parser_branch_t branch; + uint16_t opcode = CBC_BRANCH_IF_LOGICAL_TRUE; + + if (context_p->token.type == LEXER_LOGICAL_AND) + { + opcode = CBC_BRANCH_IF_LOGICAL_FALSE; + } + + parser_emit_cbc_forward_branch (context_p, opcode, &branch); + parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); + } + + parser_stack_push_uint8 (context_p, context_p->token.type); +} /* parser_append_binary_token */ + +/** + * Emit opcode for binary computations. + */ +static void +parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ + uint8_t min_prec_treshold) /**< minimal precedence of tokens */ +{ + while (PARSER_TRUE) + { + uint8_t token = context_p->stack_top_uint8; + cbc_opcode_t opcode; + + /* For left-to-right operators (all binary operators except assignment + * and logical operators), the byte code is flushed if the precedence + * of the next operator is less or equal than the current operator. For + * assignment and logical operators, we add 1 to the min precendence to + * force right-to-left evaluation order. */ + + if (!LEXER_IS_BINARY_OP_TOKEN (token) + || parser_binary_precedence_table[token - LEXER_FIRST_BINARY_OP] < min_prec_treshold) + { + return; + } + + parser_push_result (context_p); + parser_stack_pop_uint8 (context_p); + + if (token == LEXER_ASSIGN) + { + opcode = (cbc_opcode_t) context_p->stack_top_uint8; + parser_stack_pop_uint8 (context_p); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + if (opcode == CBC_ASSIGN_SET_IDENT) + { + JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); + context_p->last_cbc.value = parser_stack_pop_uint16 (context_p); + context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT; + continue; + } + } + + if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG) + { + uint16_t index = parser_stack_pop_uint16 (context_p); + parser_emit_cbc_literal (context_p, opcode, index); + + if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL + && (context_p->stack_depth >= context_p->stack_limit)) + { + /* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.cpp. */ + JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); + + context_p->stack_limit++; + + if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + } + continue; + } + } + else if (LEXER_IS_BINARY_LVALUE_TOKEN (token)) + { + opcode = LEXER_BINARY_LVALUE_OP_TOKEN_TO_OPCODE (token); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_LVALUE_WITH_LITERAL, + CBC_HAS_LITERAL_ARG)); + context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_LVALUE_WITH_LITERAL); + continue; + } + } + else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND) + { + parser_branch_t branch; + parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); + parser_set_branch_to_current_position (context_p, &branch); + continue; + } + else + { + opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL)); + context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL); + continue; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS, + CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); + context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS); + continue; + } + } + parser_emit_cbc (context_p, opcode); + } +} /* parser_process_binary_opcodes */ + +/** + * Parse expression. + */ +void +parser_parse_expression (parser_context_t *context_p, /**< context */ + int options) /**< option flags */ +{ + size_t grouping_level = 0; + + parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); + + while (PARSER_TRUE) + { + if (options & PARSE_EXPR_HAS_LITERAL) + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); + /* True only for the first expression. */ + options &= ~PARSE_EXPR_HAS_LITERAL; + } + else + { + parser_parse_unary_expression (context_p, &grouping_level); + } + + while (PARSER_TRUE) + { + parser_process_unary_expression (context_p); + + /* The engine flush binary opcodes above this precedence. */ + uint8_t min_prec_treshold = CBC_MAXIMUM_BYTE_VALUE; + + if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) + { + min_prec_treshold = parser_binary_precedence_table[context_p->token.type - LEXER_FIRST_BINARY_OP]; + if (LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type) + || context_p->token.type == LEXER_LOGICAL_OR + || context_p->token.type == LEXER_LOGICAL_AND) + { + /* Right-to-left evaluation order. */ + min_prec_treshold++; + } + } + else + { + min_prec_treshold = 0; + } + + parser_process_binary_opcodes (context_p, min_prec_treshold); + + if (context_p->token.type == LEXER_RIGHT_PAREN) + { + if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN + || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST) + { + JERRY_ASSERT (grouping_level > 0); + grouping_level--; + + if (context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST) + { + parser_push_result (context_p); + parser_flush_cbc (context_p); + } + + parser_stack_pop_uint8 (context_p); + lexer_next_token (context_p); + continue; + } + } + else if (context_p->token.type == LEXER_QUESTION_MARK) + { + cbc_opcode_t opcode = CBC_BRANCH_IF_FALSE_FORWARD; + parser_branch_t cond_branch; + parser_branch_t uncond_branch; + + parser_push_result (context_p); + + if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_BRANCH_IF_TRUE_FORWARD; + } + + parser_emit_cbc_forward_branch (context_p, opcode, &cond_branch); + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &uncond_branch); + parser_set_branch_to_current_position (context_p, &cond_branch); + + /* Although byte code is constructed for two branches, + * only one of them will be executed. To reflect this + * the stack is manually adjusted. */ + JERRY_ASSERT (context_p->stack_depth > 0); + context_p->stack_depth--; + + if (context_p->token.type != LEXER_COLON) + { + parser_raise_error (context_p, PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED); + } + + lexer_next_token (context_p); + + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_set_branch_to_current_position (context_p, &uncond_branch); + + /* Last opcode rewrite is not allowed because + * the result may come from the first branch. */ + parser_flush_cbc (context_p); + continue; + } + break; + } + + if (context_p->token.type == LEXER_COMMA) + { + if (!(options & PARSE_EXPR_NO_COMMA) || grouping_level > 0) + { + if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) + { + parser_emit_cbc (context_p, CBC_POP); + } + if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN) + { + parser_mem_page_t *page_p = context_p->stack.first_p; + + JERRY_ASSERT (page_p != NULL); + + page_p->bytes[context_p->stack.last_position - 1] = LEXER_COMMA_SEP_LIST; + context_p->stack_top_uint8 = LEXER_COMMA_SEP_LIST; + } + lexer_next_token (context_p); + continue; + } + } + else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) + { + parser_append_binary_token (context_p); + lexer_next_token (context_p); + continue; + } + break; + } + + if (grouping_level != 0) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + + JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); + parser_stack_pop_uint8 (context_p); + + if (options & PARSE_EXPR_STATEMENT) + { + if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) + { + parser_emit_cbc (context_p, CBC_POP); + } + } + else if (options & PARSE_EXPR_BLOCK) + { + if (CBC_NO_RESULT_COMPOUND_ASSIGMENT (context_p->last_cbc_opcode)) + { + context_p->last_cbc_opcode = PARSER_TO_BINARY_OPERATION_WITH_BLOCK (context_p->last_cbc_opcode); + parser_flush_cbc (context_p); + } + else if (CBC_NO_RESULT_BLOCK (context_p->last_cbc_opcode)) + { + JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 2)); + PARSER_PLUS_EQUAL_U16 (context_p->last_cbc_opcode, 2); + parser_flush_cbc (context_p); + } + else + { + if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) + { + JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1)); + context_p->last_cbc_opcode++; + } + parser_emit_cbc (context_p, CBC_POP_BLOCK); + } + } + else + { + parser_push_result (context_p); + } +} /* parser_parse_expression */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h new file mode 100644 index 0000000000..e6e82d9ce5 --- /dev/null +++ b/jerry-core/parser/js/js-parser-internal.h @@ -0,0 +1,378 @@ +/* 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. + */ + +#ifndef JS_PARSER_INTERNAL_H +#define JS_PARSER_INTERNAL_H + +#include "common.h" + +#include "js-parser.h" +#include "js-parser-limits.h" +#include "js-lexer.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_internals Internals + * @{ + */ + +/* General parser flags. */ +#define PARSER_IS_STRICT 0x00001u +#define PARSER_IS_FUNCTION 0x00002u +#define PARSER_IS_CLOSURE 0x00004u +#define PARSER_IS_PROPERTY_GETTER 0x00008u +#define PARSER_IS_PROPERTY_SETTER 0x00010u +#define PARSER_IS_FUNC_EXPRESSION 0x00020u +#define PARSER_HAS_NON_STRICT_ARG 0x00040u +#define PARSER_INSIDE_WITH 0x00080u +#define PARSER_RESOLVE_THIS_FOR_CALLS 0x00100u +#define PARSER_NAMED_FUNCTION_EXP 0x00200u +#define PARSER_HAS_INITIALIZED_VARS 0x00400u +#define PARSER_NO_END_LABEL 0x00800u +#define PARSER_NO_REG_STORE 0x01000u +#define PARSER_ARGUMENTS_NEEDED 0x02000u +#define PARSER_ARGUMENTS_NOT_NEEDED 0x04000u +#define PARSER_LEXICAL_ENV_NEEDED 0x08000u +#define PARSER_HAS_LATE_LIT_INIT 0x10000u + +/* Expression parsing flags. */ +#define PARSE_EXPR 0x00 +#define PARSE_EXPR_STATEMENT 0x01 +#define PARSE_EXPR_BLOCK 0x02 +#define PARSE_EXPR_NO_COMMA 0x04 +#define PARSE_EXPR_HAS_LITERAL 0x08 + +/* The maximum of PARSER_CBC_STREAM_PAGE_SIZE is 127. */ +#define PARSER_CBC_STREAM_PAGE_SIZE \ + ((uint32_t) (64 - sizeof (void*))) + +#define PARSER_STACK_PAGE_SIZE \ + ((uint32_t) (((sizeof (void*) > 4) ? 128 : 64) - sizeof (void*))) + +/* Avoid compiler warnings for += operations. */ +#define PARSER_PLUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) + (value)) +#define PARSER_MINUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) - (value)) +#define PARSER_PLUS_EQUAL_LC(base, value) (base) = (parser_line_counter_t) ((base) + (value)) + +/** + * Parser boolean type. + */ +typedef enum +{ + PARSER_FALSE = 0, /**< false constant */ + PARSER_TRUE = 1 /**< true constant */ +} parser_boolean_t; + +/** + * Argument for a compact-byte code. + */ +typedef struct +{ + uint16_t literal_index; /**< literal index argument */ + uint16_t value; /**< other argument (second literal or byte). */ + uint16_t third_literal_index; /**< literal index argument */ + uint8_t literal_type; /**< last literal type */ + uint8_t literal_object_type; /**< last literal object type */ +} cbc_argument_t; + +/* Useful parser macros. */ + +#define PARSER_CBC_UNAVAILABLE CBC_EXT_OPCODE + +#define PARSER_TO_EXT_OPCODE(opcode) ((uint16_t) ((opcode) + 256)) +#define PARSER_GET_EXT_OPCODE(opcode) ((opcode) - 256) +#define PARSER_IS_BASIC_OPCODE(opcode) ((opcode) < 256) +#define PARSER_IS_PUSH_LITERAL(opcode) \ + ((opcode) == CBC_PUSH_LITERAL \ + || (opcode) == CBC_PUSH_TWO_LITERALS \ + || (opcode) == CBC_PUSH_THREE_LITERALS) + +#define PARSER_GET_LITERAL(literal_index) \ + ((lexer_literal_t *) parser_list_get (&context_p->literal_pool, (literal_index))) + +#define PARSER_TO_BINARY_OPERATION_WITH_RESULT(opcode) \ + (PARSER_TO_EXT_OPCODE(opcode) - CBC_ASSIGN_ADD + CBC_EXT_ASSIGN_ADD_PUSH_RESULT) + +#define PARSER_TO_BINARY_OPERATION_WITH_BLOCK(opcode) \ + ((uint16_t) (PARSER_TO_EXT_OPCODE(opcode) - CBC_ASSIGN_ADD + CBC_EXT_ASSIGN_ADD_BLOCK)) + +#define PARSER_GET_FLAGS(op) \ + (PARSER_IS_BASIC_OPCODE (op) ? cbc_flags[(op)] : cbc_ext_flags[PARSER_GET_EXT_OPCODE (op)]) + +#define PARSER_OPCODE_IS_RETURN(op) \ + ((op) == CBC_RETURN || (op) == CBC_RETURN_WITH_BLOCK || (op) == CBC_RETURN_WITH_LITERAL) + +#define PARSER_ARGS_EQ(op, types) \ + ((PARSER_GET_FLAGS (op) & CBC_ARG_TYPES) == (types)) + +/** + * All data allocated by the parser is + * stored in parser_data_pages in the memory. + */ +typedef struct parser_mem_page_t +{ + struct parser_mem_page_t *next_p; /**< next page */ + uint8_t bytes[1]; /**< memory bytes */ +} parser_mem_page_t; + +/** + * Structure for managing parser memory. + */ +typedef struct +{ + parser_mem_page_t *first_p; /**< first allocated page */ + parser_mem_page_t *last_p; /**< last allocated page */ + uint32_t last_position; /**< position of the last allocated byte */ +} parser_mem_data_t; + +/** + * Parser memory list. + */ +typedef struct +{ + parser_mem_data_t data; /**< storage space */ + uint32_t page_size; /**< size of each page */ + uint32_t item_size; /**< size of each item */ + uint32_t item_count; /**< number of items on each page */ +} parser_list_t; + +/** + * Iterator for parser memory list. + */ +typedef struct +{ + parser_list_t *list_p; /**< parser list */ + parser_mem_page_t *current_p; /**< currently processed page */ + size_t current_position; /**< current position on the page */ +} parser_list_iterator_t; + +/** + * Parser memory stack. + */ +typedef struct +{ + parser_mem_data_t data; /**< storage space */ + parser_mem_page_t *free_page_p; /**< space for fast allocation */ +} parser_stack_t; + +/** + * Iterator for parser memory stack. + */ +typedef struct +{ + parser_mem_page_t *current_p; /**< currently processed page */ + size_t current_position; /**< current position on the page */ +} parser_stack_iterator_t; + +/** + * Branch type. + */ +typedef struct +{ + parser_mem_page_t *page_p; /**< branch location page */ + uint32_t offset; /**< branch location offset */ +} parser_branch_t; + +/** + * Branch chain type. + */ +typedef struct parser_branch_node_t +{ + struct parser_branch_node_t *next_p; /**< next linked list node */ + parser_branch_t branch; /**< branch */ +} parser_branch_node_t; + +/** + * Those members of a context which needs + * to be saved when a sub-function is parsed. + */ +typedef struct parser_saved_context_t +{ + /* Parser members. */ + uint32_t status_flags; /**< parsing options */ + uint16_t stack_depth; /**< current stack depth */ + uint16_t stack_limit; /**< maximum stack depth */ + struct parser_saved_context_t *prev_context_p; /**< last saved context */ + parser_stack_iterator_t last_statement; /**< last statement position */ + + /* Literal types */ + uint16_t argument_count; /**< number of function arguments */ + uint16_t register_count; /**< number of registers */ + uint16_t literal_count; /**< number of literals */ + + /* Memory storage members. */ + parser_mem_data_t byte_code; /**< byte code buffer */ + uint32_t byte_code_size; /**< byte code size for branches */ + parser_mem_data_t literal_pool_data; /**< literal list */ + +#ifdef PARSER_DEBUG + uint16_t context_stack_depth; /**< current context stack depth */ +#endif +} parser_saved_context_t; + +/** + * Shared parser context. + */ +typedef struct +{ + PARSER_TRY_CONTEXT (try_buffer); /**< try_buffer */ + parser_error_t error; /**< error code */ + void *allocated_buffer_p; /**< dinamically allocated buffer + * which needs to be freed on error */ + + /* Parser members. */ + uint32_t status_flags; /**< status flags */ + uint16_t stack_depth; /**< current stack depth */ + uint16_t stack_limit; /**< maximum stack depth */ + parser_saved_context_t *last_context_p; /**< last saved context */ + parser_stack_iterator_t last_statement; /**< last statement position */ + + /* Lexer members. */ + lexer_token_t token; /**< current token */ + lexer_lit_object_t lit_object; /**< current literal object */ + const uint8_t *source_p; /**< next source byte */ + const uint8_t *source_end_p; /**< last source byte */ + parser_line_counter_t line; /**< current line */ + parser_line_counter_t column; /**< current column */ + + /* Compact byte code members. */ + cbc_argument_t last_cbc; /**< argument of the last cbc */ + uint16_t last_cbc_opcode; /**< opcode of the last cbc */ + + /* Literal types */ + uint16_t argument_count; /**< number of function arguments */ + uint16_t register_count; /**< number of registers */ + uint16_t literal_count; /**< number of literals */ + + /* Memory storage members. */ + parser_mem_data_t byte_code; /**< byte code buffer */ + uint32_t byte_code_size; /**< current byte code size for branches */ + parser_list_t literal_pool; /**< literal list */ + parser_mem_data_t stack; /**< storage space */ + parser_mem_page_t *free_page_p; /**< space for fast allocation */ + uint8_t stack_top_uint8; /**< top byte stored on the stack */ + +#ifdef PARSER_DEBUG + /* Variables for debugging / logging. */ + uint16_t context_stack_depth; /**< current context stack depth */ +#endif /* PARSER_DEBUG */ + +#ifdef PARSER_DUMP_BYTE_CODE + int is_show_opcodes; /**< show opcodes */ + uint32_t total_byte_code_size; /**< total byte code size */ +#endif /* PARSER_DUMP_BYTE_CODE */ +} parser_context_t; + +/* Memory management. + * Note: throws an error if unsuccessful. */ +void *parser_malloc (parser_context_t *, size_t); +void parser_free (void *); +void *parser_malloc_local (parser_context_t *, size_t); +void parser_free_local (void *); + +/* Parser byte stream. */ + +void parser_cbc_stream_init (parser_mem_data_t *); +void parser_cbc_stream_free (parser_mem_data_t *); +void parser_cbc_stream_alloc_page (parser_context_t *, parser_mem_data_t *); + +/* Parser list. Ensures pointer alignment. */ + +void parser_list_init (parser_list_t *, uint32_t, uint32_t); +void parser_list_free (parser_list_t *); +void parser_list_reset (parser_list_t *); +void *parser_list_append (parser_context_t *, parser_list_t *); +void *parser_list_get (parser_list_t *, size_t); +void parser_list_iterator_init (parser_list_t *, parser_list_iterator_t *); +void *parser_list_iterator_next (parser_list_iterator_t *); + +/* Parser stack. Optimized for pushing bytes. + * Pop functions never throws error. */ + +void parser_stack_init (parser_context_t *); +void parser_stack_free (parser_context_t *); +void parser_stack_push_uint8 (parser_context_t *, uint8_t); +void parser_stack_pop_uint8 (parser_context_t *); +void parser_stack_push_uint16 (parser_context_t *, uint16_t); +uint16_t parser_stack_pop_uint16 (parser_context_t *); +void parser_stack_push (parser_context_t *, const void *, uint32_t); +void parser_stack_pop (parser_context_t *, void *, uint32_t); +void parser_stack_iterator_skip (parser_stack_iterator_t *, size_t); +void parser_stack_iterator_read (parser_stack_iterator_t *, void *, size_t); +void parser_stack_iterator_write (parser_stack_iterator_t *, const void *, size_t); + +/* Compact byte code emitting functions. */ + +void parser_flush_cbc (parser_context_t *); +void parser_emit_cbc (parser_context_t *, uint16_t); +void parser_emit_cbc_literal (parser_context_t *, uint16_t, uint16_t); +void parser_emit_cbc_literal_from_token (parser_context_t *, uint16_t); +void parser_emit_cbc_call (parser_context_t *, uint16_t, size_t); +void parser_emit_cbc_push_number (parser_context_t *, int); +void parser_emit_cbc_forward_branch (parser_context_t *, uint16_t, parser_branch_t *); +parser_branch_node_t *parser_emit_cbc_forward_branch_item (parser_context_t *, uint16_t, parser_branch_node_t *); +void parser_emit_cbc_backward_branch (parser_context_t *, uint16_t, uint32_t); +void parser_set_branch_to_current_position (parser_context_t *, parser_branch_t *); +void parser_set_breaks_to_current_position (parser_context_t *, parser_branch_node_t *); +void parser_set_continues_to_current_position (parser_context_t *, parser_branch_node_t *); + +/* Convenience macros. */ +#define parser_emit_cbc_ext(context_p, opcode) \ + parser_emit_cbc ((context_p), PARSER_TO_EXT_OPCODE (opcode)) +#define parser_emit_cbc_ext_literal(context_p, opcode, literal_index) \ + parser_emit_cbc_literal ((context_p), PARSER_TO_EXT_OPCODE (opcode), (literal_index)) +#define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \ + parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments)) +#define parser_emit_cbc_ext_forward_branch(context_p, opcode, branch_p) \ + parser_emit_cbc_forward_branch ((context_p), PARSER_TO_EXT_OPCODE (opcode), (branch_p)) +#define parser_emit_cbc_ext_backward_branch(context_p, opcode, offset) \ + parser_emit_cbc_backward_branch ((context_p), PARSER_TO_EXT_OPCODE (opcode), (offset)) + +/* Lexer functions */ + +void lexer_next_token (parser_context_t *); +void lexer_expect_identifier (parser_context_t *, uint8_t); +void lexer_scan_identifier (parser_context_t *, int); +void lexer_expect_object_literal_id (parser_context_t *, int); +void lexer_construct_literal_object (parser_context_t *, lexer_lit_location_t *, uint8_t); +int lexer_construct_number_object (parser_context_t *, int, int); +void lexer_construct_function_object (parser_context_t *, uint32_t); +void lexer_construct_regexp_object (parser_context_t *, int); +int lexer_same_identifiers (lexer_lit_location_t *, lexer_lit_location_t *); + +/* Parser functions. */ + +void parser_parse_expression (parser_context_t *, int); +void parser_parse_statements (parser_context_t *); +void parser_scan_until (parser_context_t *, lexer_range_t *, lexer_token_type_t); +ecma_compiled_code_t *parser_parse_function (parser_context_t *, uint32_t); +void parser_free_jumps (parser_stack_iterator_t); + +/* Error management. */ + +void parser_raise_error (parser_context_t *, parser_error_t); + +/** + * @} + * @} + * @} + */ + +#endif /* !JS_PARSER_INTERNAL_H */ diff --git a/jerry-core/parser/js/js-parser-limits.h b/jerry-core/parser/js/js-parser-limits.h new file mode 100644 index 0000000000..c95497f320 --- /dev/null +++ b/jerry-core/parser/js/js-parser-limits.h @@ -0,0 +1,99 @@ +/* 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. + */ + +#ifndef JS_PARSER_LIMITS_H +#define JS_PARSER_LIMITS_H + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_internals Internals + * @{ + */ + +/* Maximum identifier length accepted by the parser. + * Limit: LEXER_MAX_STRING_LENGTH. */ +#ifndef PARSER_MAXIMUM_IDENT_LENGTH +#define PARSER_MAXIMUM_IDENT_LENGTH 255 +#endif /* PARSER_MAXIMUM_IDENT_LENGTH */ + +/* Maximum string length. + * Limit: 65535. */ +#ifndef PARSER_MAXIMUM_STRING_LENGTH +#define PARSER_MAXIMUM_STRING_LENGTH 65535 +#endif /* PARSER_MAXIMUM_STRING_LENGTH */ + +/* Maximum number of literals. + * Limit: 32767. Recommended: 510, 32767 */ +#ifndef PARSER_MAXIMUM_NUMBER_OF_LITERALS +#define PARSER_MAXIMUM_NUMBER_OF_LITERALS 32767 +#endif /* PARSER_MAXIMUM_NUMBER_OF_LITERALS */ + +/* Maximum number of registers. + * Limit: PARSER_MAXIMUM_NUMBER_OF_LITERALS */ +#ifndef PARSER_MAXIMUM_NUMBER_OF_REGISTERS +#define PARSER_MAXIMUM_NUMBER_OF_REGISTERS 256 +#endif + +/* Maximum code size. + * Limit: 16777215. Recommended: 65535, 16777215. */ +#ifndef PARSER_MAXIMUM_CODE_SIZE +#define PARSER_MAXIMUM_CODE_SIZE 16777215 +#endif + +/* Maximum number of values pushed onto the stack by a function. + * Limit: 65500. Recommended: 1024. */ +#ifndef PARSER_MAXIMUM_STACK_LIMIT +#define PARSER_MAXIMUM_STACK_LIMIT 1024 + +#endif /* !PARSER_MAXIMUM_STACK_LIMIT */ + +/* Checks. */ + +#if (PARSER_MAXIMUM_STRING_LENGTH < 1) || (PARSER_MAXIMUM_STRING_LENGTH > 65535) +#error "Maximum string length is not within range." +#endif /* (PARSER_MAXIMUM_STRING_LENGTH < 1) || (PARSER_MAXIMUM_STRING_LENGTH > 65535) */ + +#if (PARSER_MAXIMUM_IDENT_LENGTH < 1) || (PARSER_MAXIMUM_IDENT_LENGTH > PARSER_MAXIMUM_STRING_LENGTH) +#error "Maximum identifier length is not within range." +#endif /* (PARSER_MAXIMUM_IDENT_LENGTH < 1) || (PARSER_MAXIMUM_IDENT_LENGTH > PARSER_MAXIMUM_STRING_LENGTH) */ + +#if (PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) || (PARSER_MAXIMUM_NUMBER_OF_LITERALS > 32767) +#error "Maximum number of literals is not within range." +#endif /* (PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) || (PARSER_MAXIMUM_NUMBER_OF_LITERALS > 32767) */ + +#if (PARSER_MAXIMUM_NUMBER_OF_REGISTERS > PARSER_MAXIMUM_NUMBER_OF_LITERALS) +#error "Maximum number of registers is not within range." +#endif /* (PARSER_MAXIMUM_NUMBER_OF_REGISTERS > PARSER_MAXIMUM_NUMBER_OF_LITERALS) */ + +#if (PARSER_MAXIMUM_CODE_SIZE < 4096) || (PARSER_MAXIMUM_CODE_SIZE > 16777215) +#error "Maximum code size is not within range." +#endif /* (PARSER_MAXIMUM_CODE_SIZE < 4096) || (PARSER_MAXIMUM_CODE_SIZE > 16777215) */ + +#if (PARSER_MAXIMUM_STACK_LIMIT < 16) || (PARSER_MAXIMUM_STACK_LIMIT > 65500) +#error "Maximum function stack usage is not within range." +#endif /* (PARSER_MAXIMUM_STACK_LIMIT < 16) || (PARSER_MAXIMUM_STACK_LIMIT > 65500) */ + +/** + * @} + * @} + * @} + */ + +#endif /* !JS_PARSER_LIMITS_H */ diff --git a/jerry-core/parser/js/js-parser-mem.cpp b/jerry-core/parser/js/js-parser-mem.cpp new file mode 100644 index 0000000000..a946330bde --- /dev/null +++ b/jerry-core/parser/js/js-parser-mem.cpp @@ -0,0 +1,669 @@ +/* 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 "js-parser-internal.h" + +/** \addtogroup mem Memory allocation + * @{ + * + * \addtogroup mem_parser Parser memory manager + * @{ + */ + +/**********************************************************************/ +/* Memory allocation */ +/**********************************************************************/ + +/** + * Allocate memory. + * + * @return allocated memory. + */ +void * +parser_malloc (parser_context_t *context_p, /**< context */ + size_t size) /**< size of the memory */ +{ + void *result; + + JERRY_ASSERT (size > 0); + result = PARSER_MALLOC (size); + if (result == 0) + { + parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY); + } + return result; +} /* parser_malloc */ + +/** + * Free memory allocated by parser_malloc. + */ +void parser_free (void *ptr) /**< pointer to free */ +{ + PARSER_FREE (ptr); +} /* parser_free */ + +/** + * Allocate local memory for short term use. + * + * @return allocated memory. + */ +void * +parser_malloc_local (parser_context_t *context_p, /**< context */ + size_t size) /**< size of the memory */ +{ + void *result; + + JERRY_ASSERT (size > 0); + result = PARSER_MALLOC_LOCAL (size); + if (result == 0) + { + parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY); + } + return result; +} /* parser_malloc_local */ + +/** + * Free memory allocated by parser_malloc_local. + */ +void parser_free_local (void *ptr) /**< pointer to free */ +{ + PARSER_FREE_LOCAL (ptr); +} /* parser_free_local */ + +/**********************************************************************/ +/* Parser data management functions */ +/**********************************************************************/ + +/** + * Initialize parse data. + */ +static void +parser_data_init (parser_mem_data_t *data_p, /**< memory manager */ + uint32_t page_size) /**< size of each page */ +{ + data_p->first_p = NULL; + data_p->last_p = NULL; + data_p->last_position = page_size; +} /* parser_data_init */ + +/** + * Free parse data. + */ +static void +parser_data_free (parser_mem_data_t *data_p) /**< memory manager */ +{ + parser_mem_page_t *page_p = data_p->first_p; + + while (page_p != NULL) + { + parser_mem_page_t *next_p = page_p->next_p; + + parser_free (page_p); + page_p = next_p; + } +} /* parser_data_free */ + +/**********************************************************************/ +/* Parser byte stream management functions */ +/**********************************************************************/ + +/** + * Initialize byte stream. + */ +void +parser_cbc_stream_init (parser_mem_data_t *data_p) /**< memory manager */ +{ + parser_data_init (data_p, PARSER_CBC_STREAM_PAGE_SIZE); +} /* parser_cbc_stream_init */ + +/** + * Free byte stream. + */ +void +parser_cbc_stream_free (parser_mem_data_t *data_p) /**< memory manager */ +{ + parser_data_free (data_p); +} /* parser_cbc_stream_free */ + +/** + * Appends a byte at the end of the byte stream. + */ +void +parser_cbc_stream_alloc_page (parser_context_t *context_p, /**< context */ + parser_mem_data_t *data_p) /**< memory manager */ +{ + size_t size = sizeof (parser_mem_page_t *) + PARSER_CBC_STREAM_PAGE_SIZE; + parser_mem_page_t *page_p = (parser_mem_page_t *) parser_malloc (context_p, size); + + page_p->next_p = NULL; + data_p->last_position = 0; + + if (data_p->last_p != NULL) + { + data_p->last_p->next_p = page_p; + } + else + { + data_p->first_p = page_p; + } + data_p->last_p = page_p; +} /* parser_cbc_stream_alloc_page */ + +/**********************************************************************/ +/* Parser list management functions */ +/**********************************************************************/ + +/** + * Initialize parser list. + */ +void +parser_list_init (parser_list_t *list_p, /**< parser list */ + uint32_t item_size, /**< size for each page */ + uint32_t item_count) /**< number of items on each page */ +{ + /* Align to pointer size. */ + item_size = (uint32_t) (((item_size) + sizeof (void *) - 1) & ~(sizeof (void *) - 1)); + parser_data_init (&list_p->data, item_size * item_count); + list_p->page_size = item_size * item_count; + list_p->item_size = item_size; + list_p->item_count = item_count; +} /* parser_list_init */ + +/** + * Free parser list. + */ +void +parser_list_free (parser_list_t *list_p) /**< parser list */ +{ + parser_data_free (&list_p->data); +} /* parser_list_free */ + +/** + * Reset parser list. + */ +void +parser_list_reset (parser_list_t *list_p) /**< parser list */ +{ + parser_data_init (&list_p->data, list_p->page_size); +} /* parser_list_reset */ + +/** + * Allocate space for the next item. + * + * @return pointer to the appended item. + */ +void * +parser_list_append (parser_context_t *context_p, /**< context */ + parser_list_t *list_p) /**< parser list */ +{ + parser_mem_page_t *page_p = list_p->data.last_p; + void *result; + + if (list_p->data.last_position + list_p->item_size > list_p->page_size) + { + size_t size = sizeof (parser_mem_page_t *) + list_p->page_size; + + page_p = (parser_mem_page_t *) parser_malloc (context_p, size); + + page_p->next_p = NULL; + list_p->data.last_position = 0; + + if (list_p->data.last_p != NULL) + { + list_p->data.last_p->next_p = page_p; + } + else + { + list_p->data.first_p = page_p; + } + list_p->data.last_p = page_p; + } + + result = page_p->bytes + list_p->data.last_position; + list_p->data.last_position += list_p->item_size; + return result; +} /* parser_list_append */ + +/** + * Return the nth item of the list. + * + * @return pointer to the item. + */ +void * +parser_list_get (parser_list_t *list_p, /**< parser list */ + size_t index) /**< item index */ +{ + size_t item_count = list_p->item_count; + parser_mem_page_t *page_p = list_p->data.first_p; + + while (index >= item_count) + { + JERRY_ASSERT (page_p != NULL); + page_p = page_p->next_p; + index -= item_count; + } + + JERRY_ASSERT (page_p != NULL); + JERRY_ASSERT (page_p != list_p->data.last_p + || (index * list_p->item_size < list_p->data.last_position)); + return page_p->bytes + (index * list_p->item_size); +} /* parser_list_get */ + +/** + * Initialize a parser list iterator. + */ +void +parser_list_iterator_init (parser_list_t *list_p, /**< parser list */ + parser_list_iterator_t *iterator_p) /**< iterator */ +{ + iterator_p->list_p = list_p; + iterator_p->current_p = list_p->data.first_p; + iterator_p->current_position = 0; +} /* parser_list_iterator_init */ + +/** + * Next iterator step. + * + * @return the address of the current item, or NULL at the end. + */ +void * +parser_list_iterator_next (parser_list_iterator_t *iterator_p) /**< iterator */ +{ + void *result; + + if (iterator_p->current_p == NULL) + { + return NULL; + } + + result = iterator_p->current_p->bytes + iterator_p->current_position; + iterator_p->current_position += iterator_p->list_p->item_size; + + if (iterator_p->current_p->next_p == NULL) + { + if (iterator_p->current_position >= iterator_p->list_p->data.last_position) + { + iterator_p->current_p = NULL; + iterator_p->current_position = 0; + } + } + else if (iterator_p->current_position >= iterator_p->list_p->page_size) + { + iterator_p->current_p = iterator_p->current_p->next_p; + iterator_p->current_position = 0; + } + return result; +} /* parser_list_iterator_next */ + +/**********************************************************************/ +/* Parser stack management functions */ +/**********************************************************************/ + +/* Stack is a reversed storage. */ + +/** + * Initialize parser stack. + */ +void +parser_stack_init (parser_context_t *context_p) /**< context */ +{ + parser_data_init (&context_p->stack, PARSER_STACK_PAGE_SIZE); + context_p->free_page_p = NULL; +} /* parser_stack_init */ + +/** + * Free parser stack. + */ +void +parser_stack_free (parser_context_t *context_p) /**< context */ +{ + parser_data_free (&context_p->stack); + + if (context_p->free_page_p != NULL) + { + parser_free (context_p->free_page_p); + } +} /* parser_stack_free */ + +/** + * Pushes an uint8_t value onto the stack. + */ +void +parser_stack_push_uint8 (parser_context_t *context_p, /**< context */ + uint8_t uint8_value) /**< value pushed onto the stack */ +{ + parser_mem_page_t *page_p = context_p->stack.first_p; + + /* This assert might trigger false positive valgrind errors, when + * parser_stack_push() pushes not fully initialized structures. + * More precisely when the last byte of the structure is uninitialized. */ + JERRY_ASSERT (page_p == NULL + || context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); + + if (context_p->stack.last_position >= PARSER_STACK_PAGE_SIZE) + { + if (context_p->free_page_p != NULL) + { + page_p = context_p->free_page_p; + context_p->free_page_p = NULL; + } + else + { + size_t size = sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE; + + page_p = (parser_mem_page_t *) parser_malloc (context_p, size); + } + + page_p->next_p = context_p->stack.first_p; + context_p->stack.last_position = 0; + context_p->stack.first_p = page_p; + } + + page_p->bytes[context_p->stack.last_position++] = uint8_value; + context_p->stack_top_uint8 = uint8_value; +} /* parser_stack_push_uint8 */ + +/** + * Pops the last uint8_t value from the stack. + */ +void +parser_stack_pop_uint8 (parser_context_t *context_p) /**< context */ +{ + parser_mem_page_t *page_p = context_p->stack.first_p; + + JERRY_ASSERT (page_p != NULL + && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); + + context_p->stack.last_position--; + + if (context_p->stack.last_position == 0) + { + context_p->stack.first_p = page_p->next_p; + context_p->stack.last_position = PARSER_STACK_PAGE_SIZE; + + if (context_p->free_page_p == NULL) + { + context_p->free_page_p = page_p; + } + else + { + parser_free (page_p); + } + + page_p = context_p->stack.first_p; + + JERRY_ASSERT (page_p != NULL); + } + + context_p->stack_top_uint8 = page_p->bytes[context_p->stack.last_position - 1]; +} /* parser_stack_pop_uint8 */ + +/** + * Pushes an uint16_t value onto the stack. + */ +void +parser_stack_push_uint16 (parser_context_t *context_p, /**< context */ + uint16_t uint16_value) /**< value pushed onto the stack */ +{ + if (context_p->stack.last_position + 2 <= PARSER_STACK_PAGE_SIZE) + { + parser_mem_page_t *page_p = context_p->stack.first_p; + + JERRY_ASSERT (page_p != NULL + && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); + + page_p->bytes[context_p->stack.last_position++] = (uint8_t) (uint16_value >> 8); + page_p->bytes[context_p->stack.last_position++] = (uint8_t) uint16_value; + context_p->stack_top_uint8 = (uint8_t) uint16_value; + } + else + { + parser_stack_push_uint8 (context_p, (uint8_t) (uint16_value >> 8)); + parser_stack_push_uint8 (context_p, (uint8_t) uint16_value); + } +} /* parser_stack_push_uint16 */ + +/** + * Pops the last uint16_t value from the stack. + * + * @return the value popped from the stack. + */ +uint16_t +parser_stack_pop_uint16 (parser_context_t *context_p) /**< context */ +{ + uint32_t value = context_p->stack_top_uint8; + + if (context_p->stack.last_position >= 3) + { + parser_mem_page_t *page_p = context_p->stack.first_p; + + JERRY_ASSERT (page_p != NULL + && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); + + value |= ((uint32_t) page_p->bytes[context_p->stack.last_position - 2]) << 8; + context_p->stack_top_uint8 = page_p->bytes[context_p->stack.last_position - 3]; + context_p->stack.last_position -= 2; + } + else + { + parser_stack_pop_uint8 (context_p); + value |= ((uint32_t) context_p->stack_top_uint8) << 8; + parser_stack_pop_uint8 (context_p); + } + return (uint16_t) value; +} /* parser_stack_pop_uint16 */ + +/** + * Pushes a data onto the stack. + */ +void +parser_stack_push (parser_context_t *context_p, /**< context */ + const void *data_p, /* data pushed onto the stack */ + uint32_t length) /* length of the data */ +{ + uint32_t fragment_length = PARSER_STACK_PAGE_SIZE - context_p->stack.last_position; + const uint8_t *bytes_p = (const uint8_t *) data_p; + parser_mem_page_t *page_p; + + JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); + + context_p->stack_top_uint8 = bytes_p[length - 1]; + + if (fragment_length > 0) + { + /* Fill the remaining bytes. */ + if (fragment_length > length) + { + fragment_length = length; + } + + memcpy (context_p->stack.first_p->bytes + context_p->stack.last_position, + bytes_p, + fragment_length); + + if (fragment_length == length) + { + context_p->stack.last_position += length; + return; + } + + bytes_p += fragment_length; + length -= fragment_length; + } + + if (context_p->free_page_p != NULL) + { + page_p = context_p->free_page_p; + context_p->free_page_p = NULL; + } + else + { + size_t size = sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE; + + page_p = (parser_mem_page_t *) parser_malloc (context_p, size); + } + + page_p->next_p = context_p->stack.first_p; + + context_p->stack.first_p = page_p; + + memcpy (page_p->bytes, bytes_p, length); + context_p->stack.last_position = length; +} /* parser_stack_push */ + +/** + * Pop bytes from the top of the stack. + */ +void +parser_stack_pop (parser_context_t *context_p, /**< context */ + void *data_p, /* destination buffer, can be NULL */ + uint32_t length) /* length of the data */ +{ + uint8_t *bytes_p = (uint8_t *) data_p; + parser_mem_page_t *page_p = context_p->stack.first_p; + + JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); + + if (context_p->stack.last_position > length) + { + context_p->stack.last_position -= length; + context_p->stack_top_uint8 = page_p->bytes[context_p->stack.last_position - 1]; + + if (bytes_p != NULL) + { + memcpy (bytes_p, context_p->stack.first_p->bytes + context_p->stack.last_position, length); + } + return; + } + + JERRY_ASSERT (page_p->next_p != NULL); + + length -= context_p->stack.last_position; + + if (bytes_p != NULL) + { + memcpy (bytes_p + length, page_p->bytes, context_p->stack.last_position); + } + + context_p->stack.first_p = page_p->next_p; + context_p->stack.last_position = PARSER_STACK_PAGE_SIZE - length; + context_p->stack_top_uint8 = page_p->next_p->bytes[context_p->stack.last_position - 1]; + + if (bytes_p != NULL && length > 0) + { + memcpy (bytes_p, page_p->next_p->bytes + context_p->stack.last_position, length); + } + + JERRY_ASSERT (context_p->stack.last_position > 0); + + if (context_p->free_page_p == NULL) + { + context_p->free_page_p = page_p; + } + else + { + parser_free (page_p); + } +} /* parser_stack_pop */ + +/** + * Skip the next n bytes of the stack. + */ +void +parser_stack_iterator_skip (parser_stack_iterator_t *iterator, /**< iterator */ + size_t length) /**< number of skipped bytes */ +{ + JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); + + if (length < iterator->current_position) + { + iterator->current_position -= length; + } + else + { + iterator->current_position = PARSER_STACK_PAGE_SIZE - (length - iterator->current_position); + iterator->current_p = iterator->current_p->next_p; + } +} /* parser_stack_iterator_skip */ + +/** + * Read bytes from the stack. + */ +void +parser_stack_iterator_read (parser_stack_iterator_t *iterator, /**< iterator */ + void *data_p, /* destination buffer */ + size_t length) /* length of the data */ +{ + uint8_t *bytes_p = (uint8_t *) data_p; + + JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); + + if (length <= iterator->current_position) + { + memcpy (bytes_p, + iterator->current_p->bytes + iterator->current_position - length, + length); + } + else + { + JERRY_ASSERT (iterator->current_p->next_p != NULL); + + length -= iterator->current_position; + memcpy (bytes_p + length, + iterator->current_p->bytes, + iterator->current_position); + memcpy (bytes_p, + iterator->current_p->next_p->bytes + PARSER_STACK_PAGE_SIZE - length, + length); + } +} /* parser_stack_iterator_read */ + +/** + * Write bytes onto the stack. + */ +void +parser_stack_iterator_write (parser_stack_iterator_t *iterator, /**< iterator */ + const void *data_p, /* destination buffer */ + size_t length) /* length of the data */ +{ + const uint8_t *bytes_p = (const uint8_t *) data_p; + + JERRY_ASSERT (length < PARSER_STACK_PAGE_SIZE && length > 0); + + if (length <= iterator->current_position) + { + memcpy (iterator->current_p->bytes + iterator->current_position - length, + bytes_p, + length); + } + else + { + JERRY_ASSERT (iterator->current_p->next_p != NULL); + + length -= iterator->current_position; + memcpy (iterator->current_p->bytes, + bytes_p + length, + iterator->current_position); + memcpy (iterator->current_p->next_p->bytes + PARSER_STACK_PAGE_SIZE - length, + bytes_p, + length); + } +} /* parser_stack_iterator_write */ + +/** + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-parser-scanner.cpp b/jerry-core/parser/js/js-parser-scanner.cpp new file mode 100644 index 0000000000..6c6b18a43b --- /dev/null +++ b/jerry-core/parser/js/js-parser-scanner.cpp @@ -0,0 +1,678 @@ +/* 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 "js-parser-internal.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_scanner Scanner + * @{ + */ + +/** + * Scan mode types types. + */ +typedef enum +{ + SCAN_MODE_PRIMARY_EXPRESSION, /**< scanning primary expression */ + SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW, /**< scanning primary expression after new */ + SCAN_MODE_POST_PRIMARY_EXPRESSION, /**< scanning post primary expression */ + SCAN_MODE_PRIMARY_EXPRESSION_END, /**< scanning prymary expression end */ + SCAN_MODE_STATEMENT, /**< scanning statement */ + SCAN_MODE_FUNCTION_ARGUMENTS, /**< scanning function arguments */ + SCAN_MODE_PROPERTY_NAME, /**< scanning property name */ +} scan_modes_t; + +/** + * Scan stack mode types types. + */ +typedef enum +{ + SCAN_STACK_HEAD, /**< head */ + SCAN_STACK_PAREN_EXPRESSION, /**< parent expression group */ + SCAN_STACK_PAREN_STATEMENT, /**< parent stetement group */ + SCAN_STACK_COLON_EXPRESSION, /**< colon expression group */ + SCAN_STACK_COLON_STATEMENT, /**< colon statement group*/ + SCAN_STACK_SQUARE_BRACKETED_EXPRESSION, /**< square bracketed expression group */ + SCAN_STACK_OBJECT_LITERAL, /**< object literal group */ + SCAN_STACK_BLOCK_STATEMENT, /**< block statement group */ + SCAN_STACK_BLOCK_EXPRESSION, /**< block expression group*/ + SCAN_STACK_BLOCK_PROPERTY, /**< block property group */ +} scan_stack_modes_t; + +/** + * Scan primary expression. + * + * @return PARSER_TRUE for continue, PARSER_FALSE for break + */ +static int +parser_scan_primary_expression (parser_context_t *context_p, /**< context */ + lexer_token_type_t type, /**< current token type */ + scan_stack_modes_t stack_top, /**< current stack top */ + scan_modes_t *mode) /**< scan mode */ +{ + switch (type) + { + case LEXER_KEYW_NEW: + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW; + break; + } + case LEXER_DIVIDE: + case LEXER_ASSIGN_DIVIDE: + { + lexer_construct_regexp_object (context_p, PARSER_TRUE); + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + break; + } + case LEXER_KEYW_FUNCTION: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_EXPRESSION); + *mode = SCAN_MODE_FUNCTION_ARGUMENTS; + break; + } + case LEXER_LEFT_PAREN: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + case LEXER_LEFT_SQUARE: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_SQUARE_BRACKETED_EXPRESSION); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + case LEXER_LEFT_BRACE: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); + *mode = SCAN_MODE_PROPERTY_NAME; + return PARSER_TRUE; + } + case LEXER_LITERAL: + case LEXER_KEYW_THIS: + case LEXER_LIT_TRUE: + case LEXER_LIT_FALSE: + case LEXER_LIT_NULL: + { + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + break; + } + case LEXER_RIGHT_SQUARE: + { + if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) + { + parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); + } + parser_stack_pop_uint8 (context_p); + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + break; + } + case LEXER_COMMA: + { + if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) + { + parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); + } + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + case LEXER_RIGHT_PAREN: + { + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + if (stack_top == SCAN_STACK_PAREN_STATEMENT) + { + *mode = SCAN_MODE_STATEMENT; + } + else if (stack_top != SCAN_STACK_PAREN_EXPRESSION) + { + parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); + } + + parser_stack_pop_uint8 (context_p); + break; + } + case LEXER_SEMICOLON: + { + /* Needed by for (;;) statements. */ + if (stack_top != SCAN_STACK_PAREN_STATEMENT) + { + parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); + } + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + default: + { + parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); + } + } + return PARSER_FALSE; +} /* parser_scan_primary_expression */ + +/** + * Scan the tokens after the primary expression. + * + * @return PARSER_TRUE for break, PARSER_FALSE for fall through + */ +static int +parser_scan_post_primary_expression (parser_context_t *context_p, /**< context */ + lexer_token_type_t type, /**< current token type */ + scan_modes_t *mode) /**< scan mode */ +{ + switch (type) + { + case LEXER_DOT: + { + lexer_scan_identifier (context_p, PARSER_FALSE); + return PARSER_TRUE; + } + case LEXER_LEFT_PAREN: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_TRUE; + } + case LEXER_LEFT_SQUARE: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_SQUARE_BRACKETED_EXPRESSION); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_TRUE; + } + case LEXER_INCREASE: + case LEXER_DECREASE: + { + if (!context_p->token.was_newline) + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return PARSER_TRUE; + } + /* FALLTHRU */ + } + default: + { + break; + } + } + + return PARSER_FALSE; +} /* parser_scan_post_primary_expression */ + +/** + * Scan the tokens after the primary expression. + * + * @return PARSER_TRUE for continue, PARSER_FALSE for break + */ +static int +parser_scan_primary_expression_end (parser_context_t *context_p, /**< context */ + lexer_token_type_t type, /**< current token type */ + scan_stack_modes_t stack_top, /**< current stack top */ + lexer_token_type_t end_type, /**< terminator token type */ + scan_modes_t *mode) /**< scan mode */ +{ + switch (type) + { + case LEXER_QUESTION_MARK: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_COLON_EXPRESSION); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + case LEXER_COMMA: + { + if (stack_top == SCAN_STACK_OBJECT_LITERAL) + { + *mode = SCAN_MODE_PROPERTY_NAME; + return PARSER_TRUE; + } + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + case LEXER_COLON: + { + if (stack_top == SCAN_STACK_COLON_EXPRESSION + || stack_top == SCAN_STACK_COLON_STATEMENT) + { + if (stack_top == SCAN_STACK_COLON_EXPRESSION) + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + } + else + { + *mode = SCAN_MODE_STATEMENT; + } + parser_stack_pop_uint8 (context_p); + return PARSER_FALSE; + } + /* FALLTHRU */ + } + default: + { + break; + } + } + + if (LEXER_IS_BINARY_OP_TOKEN (type) + || (type == LEXER_SEMICOLON && stack_top == SCAN_STACK_PAREN_STATEMENT)) + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + + if ((type == LEXER_RIGHT_SQUARE && stack_top == SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) + || (type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_PAREN_EXPRESSION) + || (type == LEXER_RIGHT_BRACE && stack_top == SCAN_STACK_OBJECT_LITERAL)) + { + parser_stack_pop_uint8 (context_p); + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + + *mode = SCAN_MODE_STATEMENT; + if (type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_PAREN_STATEMENT) + { + parser_stack_pop_uint8 (context_p); + return PARSER_FALSE; + } + + /* Check whether we can enter to statement mode. */ + if (stack_top != SCAN_STACK_BLOCK_STATEMENT + && stack_top != SCAN_STACK_BLOCK_EXPRESSION + && !(stack_top == SCAN_STACK_HEAD && end_type == LEXER_SCAN_SWITCH)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); + } + + if (type == LEXER_RIGHT_BRACE + || context_p->token.was_newline) + { + return PARSER_TRUE; + } + + if (type != LEXER_SEMICOLON) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); + } + + return PARSER_FALSE; +} /* parser_scan_primary_expression_end */ + +/** + * Scan statements. + * + * @return PARSER_TRUE for continue, PARSER_FALSE for break + */ +static int +parser_scan_statement (parser_context_t *context_p, /**< context */ + lexer_token_type_t type, /**< current token type */ + scan_stack_modes_t stack_top, /**< current stack top */ + scan_modes_t *mode) /**< scan mode */ +{ + switch (type) + { + case LEXER_SEMICOLON: + case LEXER_KEYW_ELSE: + case LEXER_KEYW_DO: + case LEXER_KEYW_TRY: + case LEXER_KEYW_FINALLY: + case LEXER_KEYW_DEBUGGER: + { + return PARSER_FALSE; + } + case LEXER_KEYW_IF: + case LEXER_KEYW_WHILE: + case LEXER_KEYW_WITH: + case LEXER_KEYW_SWITCH: + case LEXER_KEYW_CATCH: + { + lexer_next_token (context_p); + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_STATEMENT); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + case LEXER_KEYW_FOR: + { + lexer_next_token (context_p); + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_STATEMENT); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (context_p->token.type == LEXER_KEYW_VAR) + { + return PARSER_FALSE; + } + return PARSER_TRUE; + } + case LEXER_KEYW_VAR: + case LEXER_KEYW_THROW: + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + case LEXER_KEYW_RETURN: + { + lexer_next_token (context_p); + if (!context_p->token.was_newline + && context_p->token.type != LEXER_SEMICOLON) + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + } + return PARSER_TRUE; + } + case LEXER_KEYW_BREAK: + case LEXER_KEYW_CONTINUE: + { + lexer_next_token (context_p); + if (!context_p->token.was_newline + && context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + return PARSER_FALSE; + } + return PARSER_TRUE; + } + case LEXER_KEYW_DEFAULT: + { + lexer_next_token (context_p); + if (context_p->token.type != LEXER_COLON) + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } + return PARSER_FALSE; + } + case LEXER_KEYW_CASE: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_COLON_STATEMENT); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + return PARSER_FALSE; + } + case LEXER_RIGHT_BRACE: + { + if (stack_top == SCAN_STACK_BLOCK_STATEMENT + || stack_top == SCAN_STACK_BLOCK_EXPRESSION + || stack_top == SCAN_STACK_BLOCK_PROPERTY) + { + parser_stack_pop_uint8 (context_p); + + if (stack_top == SCAN_STACK_BLOCK_EXPRESSION) + { + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + } + else if (stack_top == SCAN_STACK_BLOCK_PROPERTY) + { + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + lexer_next_token (context_p); + if (context_p->token.type != LEXER_COMMA + && context_p->token.type != LEXER_RIGHT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); + } + return PARSER_TRUE; + } + return PARSER_FALSE; + } + break; + } + case LEXER_LEFT_BRACE: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); + return PARSER_FALSE; + } + case LEXER_KEYW_FUNCTION: + { + parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); + *mode = SCAN_MODE_FUNCTION_ARGUMENTS; + return PARSER_FALSE; + } + default: + { + break; + } + } + + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + lexer_next_token (context_p); + if (context_p->token.type == LEXER_COLON) + { + *mode = SCAN_MODE_STATEMENT; + return PARSER_FALSE; + } + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + } + + return PARSER_TRUE; +} /* parser_scan_statement */ + +/** + * Pre-scan for token(s). + */ +void +parser_scan_until (parser_context_t *context_p, /**< context */ + lexer_range_t *range_p, /**< destination range */ + lexer_token_type_t end_type) /**< terminator token type */ +{ + scan_modes_t mode; + lexer_token_type_t end_type_b = end_type; + + range_p->source_p = context_p->source_p; + range_p->source_end_p = context_p->source_p; + range_p->line = context_p->line; + range_p->column = context_p->column; + + mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (end_type == LEXER_KEYW_CASE) + { + end_type = LEXER_SCAN_SWITCH; + end_type_b = LEXER_SCAN_SWITCH; + mode = SCAN_MODE_STATEMENT; + } + else + { + lexer_next_token (context_p); + + if (end_type == LEXER_KEYW_IN) + { + end_type_b = LEXER_SEMICOLON; + if (context_p->token.type == LEXER_KEYW_VAR) + { + lexer_next_token (context_p); + } + } + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_HEAD); + + while (PARSER_TRUE) + { + lexer_token_type_t type = (lexer_token_type_t) context_p->token.type; + scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; + + if (type == LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_EXPRESSION_EXPECTED); + } + + if (stack_top == SCAN_STACK_HEAD + && (type == end_type || type == end_type_b)) + { + parser_stack_pop_uint8 (context_p); + return; + } + + switch (mode) + { + case SCAN_MODE_PRIMARY_EXPRESSION: + { + if (type == LEXER_ADD + || type == LEXER_SUBTRACT + || LEXER_IS_UNARY_OP_TOKEN (type)) + { + break; + } + /* FALLTHRU */ + } + case SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW: + { + if (parser_scan_primary_expression (context_p, type, stack_top, &mode)) + { + continue; + } + break; + } + case SCAN_MODE_POST_PRIMARY_EXPRESSION: + { + if (parser_scan_post_primary_expression (context_p, type, &mode)) + { + break; + } + /* FALLTHRU */ + } + case SCAN_MODE_PRIMARY_EXPRESSION_END: + { + if (parser_scan_primary_expression_end (context_p, type, stack_top, end_type, &mode)) + { + continue; + } + break; + } + case SCAN_MODE_STATEMENT: + { + if (end_type == LEXER_SCAN_SWITCH + && stack_top == SCAN_STACK_HEAD + && (type == LEXER_KEYW_DEFAULT || type == LEXER_KEYW_CASE || type == LEXER_RIGHT_BRACE)) + { + parser_stack_pop_uint8 (context_p); + return; + } + + if (parser_scan_statement (context_p, type, stack_top, &mode)) + { + continue; + } + break; + } + case SCAN_MODE_FUNCTION_ARGUMENTS: + { + JERRY_ASSERT (stack_top == SCAN_STACK_BLOCK_STATEMENT + || stack_top == SCAN_STACK_BLOCK_EXPRESSION + || stack_top == SCAN_STACK_BLOCK_PROPERTY); + + if (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + lexer_next_token (context_p); + } + + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); + } + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + while (PARSER_TRUE) + { + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_COMMA) + { + break; + } + lexer_next_token (context_p); + } + } + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + mode = SCAN_MODE_STATEMENT; + break; + } + case SCAN_MODE_PROPERTY_NAME: + { + JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL); + + lexer_scan_identifier (context_p, PARSER_TRUE); + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + parser_stack_pop_uint8 (context_p); + mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + break; + } + + if (context_p->token.type == LEXER_PROPERTY_GETTER + || context_p->token.type == LEXER_PROPERTY_SETTER) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_PROPERTY); + mode = SCAN_MODE_FUNCTION_ARGUMENTS; + break; + } + + lexer_next_token (context_p); + if (context_p->token.type != LEXER_COLON) + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } + + mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + } + + range_p->source_end_p = context_p->source_p; + lexer_next_token (context_p); + } +} /* parser_scan_until */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-parser-statm.cpp b/jerry-core/parser/js/js-parser-statm.cpp new file mode 100644 index 0000000000..c8bfae2900 --- /dev/null +++ b/jerry-core/parser/js/js-parser-statm.cpp @@ -0,0 +1,2165 @@ +/* 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 "js-parser-internal.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_stmt Statement parser + * @{ + */ + +/* Strict mode string literal in directive prologues */ +#define PARSER_USE_STRICT_LITERAL "use strict" +#define PARSER_USE_STRICT_LENGTH 10 + +/** + * Parser statement types. + * + * When a new statement is added, the following + * functions may need to be updated as well: + * + * - parser_statement_length() + * - parser_parse_break_statement() + * - parser_parse_continue_statement() + * - parser_free_jumps() + * - 'case LEXER_RIGHT_BRACE:' in parser_parse_statements() + * - 'if (context_p->token.type == LEXER_RIGHT_BRACE)' in parser_parse_statements() + * - 'switch (context_p->stack_top_uint8)' in parser_parse_statements() + */ +typedef enum +{ + PARSER_STATEMENT_START, + PARSER_STATEMENT_BLOCK, + PARSER_STATEMENT_LABEL, + PARSER_STATEMENT_IF, + PARSER_STATEMENT_ELSE, + /* From switch -> for-in : break target statements */ + PARSER_STATEMENT_SWITCH, + PARSER_STATEMENT_SWITCH_NO_DEFAULT, + /* From do-while -> for->in : continue target statements */ + PARSER_STATEMENT_DO_WHILE, + PARSER_STATEMENT_WHILE, + PARSER_STATEMENT_FOR, + /* From for->in -> try : instructions with context + * Break and continue uses another instruction form + * when crosses their borders. */ + PARSER_STATEMENT_FOR_IN, + PARSER_STATEMENT_WITH, + PARSER_STATEMENT_TRY, +} parser_statement_type_t; + +/** + * Loop statement. + */ +typedef struct +{ + parser_branch_node_t *branch_list_p; /**< list of breaks and continues targeting this statement */ +} parser_loop_statement_t; + +/** + * Label statement. + */ +typedef struct +{ + lexer_lit_location_t label_ident; /**< name of the label */ + parser_branch_node_t *break_list_p; /**< list of breaks targeting this label */ +} parser_label_statement_t; + +/** + * If/else statement. + */ +typedef struct +{ + parser_branch_t branch; /**< branch to the end */ +} parser_if_else_statement_t; + +/** + * Switch statement. + */ +typedef struct +{ + parser_branch_t default_branch; /**< branch to the default case */ + parser_branch_node_t *branch_list_p; /**< branches of case statements */ +} parser_switch_statement_t; + +/** + * Do-while statement. + */ +typedef struct +{ + uint32_t start_offset; /**< start byte code offset */ +} parser_do_while_statement_t; + +/** + * While statement. + */ +typedef struct +{ + parser_branch_t branch; /**< branch to the end */ + lexer_range_t condition_range; /**< condition part */ + uint32_t start_offset; /**< start byte code offset */ +} parser_while_statement_t; + +/** + * For statement. + */ +typedef struct +{ + parser_branch_t branch; /**< branch to the end */ + lexer_range_t condition_range; /**< condition part */ + lexer_range_t expression_range; /**< increase part */ + uint32_t start_offset; /**< start byte code offset */ +} parser_for_statement_t; + +/** + * For-in statement. + */ +typedef struct +{ + parser_branch_t branch; /**< branch to the end */ + uint32_t start_offset; /**< start byte code offset */ +} parser_for_in_statement_t; + +/** + * With statement. + */ +typedef struct +{ + parser_branch_t branch; /**< branch to the end */ +} parser_with_statement_t; + +/** + * Lexer token types. + */ +typedef enum +{ + parser_try_block, /**< try block */ + parser_catch_block, /**< catch block */ + parser_finally_block, /**< finally block */ +} parser_try_block_type_t; + +/** + * Try statement. + */ +typedef struct +{ + parser_try_block_type_t type; /**< current block type */ + parser_branch_t branch; /**< branch to the end of the current block */ +} parser_try_statement_t; + +/** + * Returns the data consumed by a statement. It can be used + * to skip undesired frames on the stack during frame search. + * + * @return size consumed by a statement. + */ +static PARSER_INLINE size_t +parser_statement_length (uint8_t type) /**< type of statement */ +{ + static const uint8_t statement_lengths[12] = + { + /* PARSER_STATEMENT_BLOCK */ + 1, + /* PARSER_STATEMENT_LABEL */ + (uint8_t) (sizeof (parser_label_statement_t) + 1), + /* PARSER_STATEMENT_IF */ + (uint8_t) (sizeof (parser_if_else_statement_t) + 1), + /* PARSER_STATEMENT_ELSE */ + (uint8_t) (sizeof (parser_if_else_statement_t) + 1), + /* PARSER_STATEMENT_SWITCH */ + (uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_SWITCH_NO_DEFAULT */ + (uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_DO_WHILE */ + (uint8_t) (sizeof (parser_do_while_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_WHILE */ + (uint8_t) (sizeof (parser_while_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_FOR */ + (uint8_t) (sizeof (parser_for_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_FOR_IN */ + (uint8_t) (sizeof (parser_for_in_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_WITH */ + (uint8_t) (sizeof (parser_with_statement_t) + 1), + /* PARSER_STATEMENT_TRY */ + (uint8_t) (sizeof (parser_try_statement_t) + 1), + }; + + JERRY_ASSERT (type >= PARSER_STATEMENT_BLOCK && type <= PARSER_STATEMENT_TRY); + JERRY_ASSERT (PARSER_STATEMENT_TRY - PARSER_STATEMENT_BLOCK == 11); + + return statement_lengths[type - PARSER_STATEMENT_BLOCK]; +} /* parser_statement_length */ + +/** + * Initialize a range from the current location. + */ +static PARSER_INLINE void +parser_save_range (parser_context_t *context_p, /**< context */ + lexer_range_t *range_p, /**< destination range */ + const uint8_t *source_end_p) /**< source end */ +{ + range_p->source_p = context_p->source_p; + range_p->source_end_p = source_end_p; + range_p->line = context_p->line; + range_p->column = context_p->column; +} /* parser_save_range */ + +/** + * Set the current location on the stack. + */ +static PARSER_INLINE void +parser_set_range (parser_context_t *context_p, /**< context */ + lexer_range_t *range_p) /**< destination range */ +{ + context_p->source_p = range_p->source_p; + context_p->source_end_p = range_p->source_end_p; + context_p->line = range_p->line; + context_p->column = range_p->column; +} /* parser_set_range */ + +/** + * Initialize stack iterator. + */ +static PARSER_INLINE void +parser_stack_iterator_init (parser_context_t *context_p, /**< context */ + parser_stack_iterator_t *iterator) /**< iterator */ +{ + iterator->current_p = context_p->stack.first_p; + iterator->current_position = context_p->stack.last_position; +} /* parser_stack_iterator_init */ + +/** + * Read the next byte from the stack. + */ +static PARSER_INLINE uint8_t +parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator) /**< iterator */ +{ + JERRY_ASSERT (iterator->current_position > 0 && iterator->current_position <= PARSER_STACK_PAGE_SIZE); + return iterator->current_p->bytes[iterator->current_position - 1]; +} /* parser_stack_iterator_read_uint8 */ + +/** + * Change last byte of the stack. + */ +static PARSER_INLINE void +parser_stack_change_last_uint8 (parser_context_t *context_p, /**< context */ + uint8_t new_value) /**< new value */ +{ + parser_mem_page_t *page_p = context_p->stack.first_p; + + JERRY_ASSERT (page_p != NULL + && context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]); + + page_p->bytes[context_p->stack.last_position - 1] = new_value; + context_p->stack_top_uint8 = new_value; +} /* parser_stack_change_last_uint8 */ + +/** + * Parse expression enclosed in parens. + */ +static PARSER_INLINE void +parser_parse_enclosed_expr (parser_context_t *context_p) /**< context */ +{ + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR); + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + lexer_next_token (context_p); +} /* parser_parse_enclosed_expr */ + +/** + * Parse var statement. + */ +static void +parser_parse_var_statement (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_VAR); + + while (PARSER_TRUE) + { + lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; + + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_ASSIGN) + { + parser_parse_expression (context_p, + PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); + } + else + { + JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL); + /* We don't need to assign anything to this variable. */ + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + + if (context_p->token.type != LEXER_COMMA) + { + break; + } + } +} /* parser_parse_var_statement */ + +/** + * Parse function statement. + */ +static void +parser_parse_function_statement (parser_context_t *context_p) /**< context */ +{ + uint32_t status_flags; + lexer_literal_t *name_p; + lexer_literal_t *literal_p; + uint8_t no_reg_store; + + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION); + + lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + + if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS) + { + context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; + } + + name_p = context_p->lit_object.literal_p; + context_p->status_flags |= PARSER_NO_REG_STORE; + + status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; + if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) + { + JERRY_ASSERT (context_p->lit_object.type == LEXER_LITERAL_OBJECT_EVAL + || context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS); + status_flags |= PARSER_HAS_NON_STRICT_ARG; + } + + if (name_p->status_flags & LEXER_FLAG_INITIALIZED) + { + if (!(name_p->status_flags & (LEXER_FLAG_FUNCTION_NAME | LEXER_FLAG_FUNCTION_ARGUMENT))) + { + /* Overwrite the previous initialization. */ + ecma_compiled_code_t *compiled_code_p; + + literal_p = PARSER_GET_LITERAL ((size_t) (context_p->lit_object.index + 1)); + + JERRY_ASSERT (literal_p->type == LEXER_FUNCTION_LITERAL + && literal_p->status_flags == 0); + + compiled_code_p = parser_parse_function (context_p, status_flags); + util_free_literal (literal_p); + + literal_p->u.bytecode_p = compiled_code_p; + lexer_next_token (context_p); + return; + } + } + else if (context_p->lit_object.index + 1 == context_p->literal_count) + { + /* The most common case: the literal is the last literal. */ + name_p->status_flags |= LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED; + lexer_construct_function_object (context_p, status_flags); + lexer_next_token (context_p); + return; + } + + /* Clone the literal at the end. */ + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + *literal_p = *name_p; + no_reg_store = name_p->status_flags & (LEXER_FLAG_NO_REG_STORE | LEXER_FLAG_SOURCE_PTR); + literal_p->status_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | no_reg_store; + + name_p->type = LEXER_UNUSED_LITERAL; + name_p->status_flags &= LEXER_FLAG_FUNCTION_ARGUMENT | LEXER_FLAG_SOURCE_PTR; + /* Byte code references to this literal are + * redirected to the newly allocated literal. */ + name_p->prop.index = context_p->literal_count; + + context_p->literal_count++; + + lexer_construct_function_object (context_p, status_flags); + lexer_next_token (context_p); +} /* parser_parse_function_statement */ + +/** + * Parse if statement (starting part). + */ +static void +parser_parse_if_statement_start (parser_context_t *context_p) /**< context */ +{ + parser_if_else_statement_t if_statement; + + parser_parse_enclosed_expr (context_p); + + parser_emit_cbc_forward_branch (context_p, + CBC_BRANCH_IF_FALSE_FORWARD, + &if_statement.branch); + + parser_stack_push (context_p, &if_statement, sizeof (parser_if_else_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_IF); + parser_stack_iterator_init (context_p, &context_p->last_statement); +} /* parser_parse_if_statement_start */ + +/** + * Parse if statement (ending part). + */ +static int +parser_parse_if_statement_end (parser_context_t *context_p) /**< context */ +{ + parser_if_else_statement_t if_statement; + parser_if_else_statement_t else_statement; + parser_stack_iterator_t iterator; + + JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_IF); + + if (context_p->token.type != LEXER_KEYW_ELSE) + { + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &if_statement, sizeof (parser_if_else_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_set_branch_to_current_position (context_p, &if_statement.branch); + + return PARSER_FALSE; + } + + parser_stack_change_last_uint8 (context_p, PARSER_STATEMENT_ELSE); + parser_stack_iterator_init (context_p, &iterator); + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &if_statement, sizeof (parser_if_else_statement_t)); + + parser_emit_cbc_forward_branch (context_p, + CBC_JUMP_FORWARD, + &else_statement.branch); + + parser_set_branch_to_current_position (context_p, &if_statement.branch); + + parser_stack_iterator_write (&iterator, &else_statement, sizeof (parser_if_else_statement_t)); + + lexer_next_token (context_p); + return PARSER_TRUE; +} /* parser_parse_if_statement_end */ + +/** + * Parse with statement (starting part). + */ +static void +parser_parse_with_statement_start (parser_context_t *context_p) /**< context */ +{ + parser_with_statement_t with_statement; + + if (context_p->status_flags & PARSER_IS_STRICT) + { + parser_raise_error (context_p, PARSER_ERR_WITH_NOT_ALLOWED); + } + + parser_parse_enclosed_expr (context_p); + +#ifdef PARSER_DEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); +#endif + + context_p->status_flags |= PARSER_INSIDE_WITH | PARSER_LEXICAL_ENV_NEEDED; + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_WITH_CREATE_CONTEXT, + &with_statement.branch); + + parser_stack_push (context_p, &with_statement, sizeof (parser_with_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WITH); + parser_stack_iterator_init (context_p, &context_p->last_statement); +} /* parser_parse_with_statement_start */ + +/** + * Parse with statement (ending part). + */ +static void +parser_parse_with_statement_end (parser_context_t *context_p) /**< context */ +{ + parser_with_statement_t with_statement; + parser_stack_iterator_t iterator; + + JERRY_ASSERT (context_p->status_flags & PARSER_INSIDE_WITH); + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &with_statement, sizeof (parser_with_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_flush_cbc (context_p); + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); +#ifdef PARSER_DEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); +#endif + + parser_emit_cbc (context_p, CBC_CONTEXT_END); + parser_set_branch_to_current_position (context_p, &with_statement.branch); + + parser_stack_iterator_init (context_p, &iterator); + + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + + if (type == PARSER_STATEMENT_START) + { + context_p->status_flags &= ~PARSER_INSIDE_WITH; + return; + } + + if (type == PARSER_STATEMENT_WITH) + { + return; + } + + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + } +} /* parser_parse_with_statement_end */ + +/** + * Parse do-while statement (ending part). + */ +static void +parser_parse_do_while_statement_end (parser_context_t *context_p) /**< context */ +{ + parser_do_while_statement_t do_while_statement; + parser_loop_statement_t loop; + + JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_DO_WHILE); + + if (context_p->token.type != LEXER_KEYW_WHILE) + { + parser_raise_error (context_p, PARSER_ERR_WHILE_EXPECTED); + } + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_pop (context_p, &do_while_statement, sizeof (parser_do_while_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_set_continues_to_current_position (context_p, loop.branch_list_p); + + parser_parse_enclosed_expr (context_p); + + if (context_p->last_cbc_opcode != CBC_PUSH_FALSE) + { + cbc_opcode_t opcode = CBC_BRANCH_IF_TRUE_BACKWARD; + if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_BRANCH_IF_FALSE_BACKWARD; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_JUMP_BACKWARD; + } + parser_emit_cbc_backward_branch (context_p, opcode, do_while_statement.start_offset); + } + else + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + + parser_set_breaks_to_current_position (context_p, loop.branch_list_p); +} /* parser_parse_do_while_statement_end */ + +/** + * Parse while statement (starting part). + */ +static void +parser_parse_while_statement_start (parser_context_t *context_p) /**< context */ +{ + parser_while_statement_t while_statement; + parser_loop_statement_t loop; + + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_WHILE); + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); + } + + parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &while_statement.branch); + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + while_statement.start_offset = context_p->byte_code_size; + + /* The conditional part is processed at the end. */ + parser_scan_until (context_p, &while_statement.condition_range, LEXER_RIGHT_PAREN); + lexer_next_token (context_p); + + loop.branch_list_p = NULL; + + parser_stack_push (context_p, &while_statement, sizeof (parser_while_statement_t)); + parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WHILE); + parser_stack_iterator_init (context_p, &context_p->last_statement); +} /* parser_parse_while_statement_start */ + +/** + * Parse while statement (ending part). + */ +static void PARSER_NOINLINE +parser_parse_while_statement_end (parser_context_t *context_p) /**< context */ +{ + parser_while_statement_t while_statement; + parser_loop_statement_t loop; + lexer_token_t current_token; + lexer_range_t range; + cbc_opcode_t opcode; + + JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_WHILE); + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_pop (context_p, &while_statement, sizeof (parser_while_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_save_range (context_p, &range, context_p->source_end_p); + current_token = context_p->token; + + parser_set_branch_to_current_position (context_p, &while_statement.branch); + parser_set_continues_to_current_position (context_p, loop.branch_list_p); + + parser_set_range (context_p, &while_statement.condition_range); + lexer_next_token (context_p); + + parser_parse_expression (context_p, PARSE_EXPR); + if (context_p->token.type != LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); + } + + opcode = CBC_BRANCH_IF_TRUE_BACKWARD; + if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_BRANCH_IF_FALSE_BACKWARD; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_JUMP_BACKWARD; + } + + parser_emit_cbc_backward_branch (context_p, opcode, while_statement.start_offset); + parser_set_breaks_to_current_position (context_p, loop.branch_list_p); + + parser_set_range (context_p, &range); + context_p->token = current_token; +} /* parser_parse_while_statement_end */ + +/** + * Parse for statement (starting part). + */ +static void +parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ +{ + parser_loop_statement_t loop; + lexer_range_t start_range; + + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FOR); + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); + } + + parser_scan_until (context_p, &start_range, LEXER_KEYW_IN); + + if (context_p->token.type == LEXER_KEYW_IN) + { + parser_for_in_statement_t for_in_statement; + lexer_range_t range; + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR); + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + +#ifdef PARSER_DEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); +#endif + + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_FOR_IN_CREATE_CONTEXT, + &for_in_statement.branch); + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + for_in_statement.start_offset = context_p->byte_code_size; + + parser_save_range (context_p, &range, context_p->source_end_p); + parser_set_range (context_p, &start_range); + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_KEYW_VAR) + { + uint16_t literal_index; + + lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; + + literal_index = context_p->lit_object.index; + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_ASSIGN) + { + parser_branch_t branch; + + /* Initialiser is never executed. */ + parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch); + lexer_next_token (context_p); + parser_parse_expression (context_p, + PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA); + parser_set_branch_to_current_position (context_p, &branch); + } + + parser_emit_cbc_ext (context_p, CBC_EXT_FOR_IN_GET_NEXT); + parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index); + } + else + { + uint16_t opcode; + + parser_parse_expression (context_p, PARSE_EXPR); + + opcode = context_p->last_cbc_opcode; + + /* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */ + JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS + && opcode != CBC_PUSH_THREE_LITERALS); + + if (opcode == CBC_PUSH_LITERAL + && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) + { + opcode = CBC_ASSIGN_SET_IDENT; + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else if (opcode == CBC_PUSH_PROP) + { + opcode = CBC_ASSIGN; + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else if (opcode == CBC_PUSH_PROP_LITERAL) + { + opcode = CBC_PUSH_PROP_LITERAL_REFERENCE; + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + } + else if (opcode == CBC_PUSH_PROP_LITERAL_LITERAL) + { + opcode = CBC_ASSIGN; + context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; + } + else if (opcode == CBC_PUSH_PROP_THIS_LITERAL) + { + opcode = CBC_ASSIGN; + context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; + } + else + { + /* Invalid LeftHandSide expression. */ + parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); + opcode = CBC_ASSIGN; + } + + parser_emit_cbc_ext (context_p, CBC_EXT_FOR_IN_GET_NEXT); + parser_flush_cbc (context_p); + + context_p->last_cbc_opcode = opcode; + } + + if (context_p->token.type != LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_IN_EXPECTED); + } + + parser_flush_cbc (context_p); + parser_set_range (context_p, &range); + lexer_next_token (context_p); + + loop.branch_list_p = NULL; + + parser_stack_push (context_p, &for_in_statement, sizeof (parser_for_in_statement_t)); + parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_IN); + parser_stack_iterator_init (context_p, &context_p->last_statement); + } + else + { + parser_for_statement_t for_statement; + + start_range.source_end_p = context_p->source_end_p; + parser_set_range (context_p, &start_range); + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_SEMICOLON) + { + if (context_p->token.type == LEXER_KEYW_VAR) + { + parser_parse_var_statement (context_p); + } + else + { + parser_parse_expression (context_p, PARSE_EXPR_STATEMENT); + } + + if (context_p->token.type != LEXER_SEMICOLON) + { + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + } + + parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &for_statement.branch); + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + for_statement.start_offset = context_p->byte_code_size; + + /* The conditional and expression parts are processed at the end. */ + parser_scan_until (context_p, &for_statement.condition_range, LEXER_SEMICOLON); + parser_scan_until (context_p, &for_statement.expression_range, LEXER_RIGHT_PAREN); + lexer_next_token (context_p); + + loop.branch_list_p = NULL; + + parser_stack_push (context_p, &for_statement, sizeof (parser_for_statement_t)); + parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR); + parser_stack_iterator_init (context_p, &context_p->last_statement); + } +} /* parser_parse_for_statement_start */ + +/** + * Parse for statement (ending part). + */ +static void PARSER_NOINLINE +parser_parse_for_statement_end (parser_context_t *context_p) /**< context */ +{ + parser_for_statement_t for_statement; + parser_loop_statement_t loop; + lexer_token_t current_token; + lexer_range_t range; + cbc_opcode_t opcode; + + JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR); + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_pop (context_p, &for_statement, sizeof (parser_for_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_save_range (context_p, &range, context_p->source_end_p); + current_token = context_p->token; + + parser_set_range (context_p, &for_statement.expression_range); + lexer_next_token (context_p); + + parser_set_continues_to_current_position (context_p, loop.branch_list_p); + + if (context_p->token.type != LEXER_EOS) + { + parser_parse_expression (context_p, PARSE_EXPR_STATEMENT); + + if (context_p->token.type != LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); + } + } + + parser_set_branch_to_current_position (context_p, &for_statement.branch); + + parser_set_range (context_p, &for_statement.condition_range); + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_EOS) + { + parser_parse_expression (context_p, PARSE_EXPR); + + if (context_p->token.type != LEXER_EOS) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); + } + + opcode = CBC_BRANCH_IF_TRUE_BACKWARD; + if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_BRANCH_IF_FALSE_BACKWARD; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE) + { + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + opcode = CBC_JUMP_BACKWARD; + } + } + else + { + opcode = CBC_JUMP_BACKWARD; + } + + parser_emit_cbc_backward_branch (context_p, opcode, for_statement.start_offset); + parser_set_breaks_to_current_position (context_p, loop.branch_list_p); + + parser_set_range (context_p, &range); + context_p->token = current_token; +} /* parser_parse_for_statement_end */ + +/** + * Parse switch statement (starting part). + */ +static void PARSER_NOINLINE +parser_parse_switch_statement_start (parser_context_t *context_p) /**< context */ +{ + parser_switch_statement_t switch_statement; + parser_loop_statement_t loop; + parser_stack_iterator_t iterator; + lexer_range_t switch_body_start; + lexer_range_t unused_range; + int switch_case_was_found; + int default_case_was_found; + parser_branch_node_t *cases_p = NULL; + + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_SWITCH); + + parser_parse_enclosed_expr (context_p); + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + + parser_save_range (context_p, &switch_body_start, context_p->source_end_p); + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + /* Unlikely case, but possible. */ + parser_emit_cbc (context_p, CBC_POP); + parser_flush_cbc (context_p); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); + parser_stack_iterator_init (context_p, &context_p->last_statement); + return; + } + + if (context_p->token.type != LEXER_KEYW_CASE + && context_p->token.type != LEXER_KEYW_DEFAULT) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_SWITCH); + } + + /* The reason of using an iterator is error management. If an error + * occures, parser_free_jumps() free all data. However, the branches + * created by parser_emit_cbc_forward_branch_item() would not be freed. + * To free these branches, the current switch data is always stored + * on the stack. If any change happens, this data is updated. Updates + * are done using the iterator. */ + + switch_statement.branch_list_p = NULL; + loop.branch_list_p = NULL; + + parser_stack_push (context_p, &switch_statement, sizeof (parser_switch_statement_t)); + parser_stack_iterator_init (context_p, &iterator); + parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_SWITCH); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + switch_case_was_found = PARSER_FALSE; + default_case_was_found = PARSER_FALSE; + + while (PARSER_TRUE) + { + parser_scan_until (context_p, &unused_range, LEXER_KEYW_CASE); + + if (context_p->token.type == LEXER_KEYW_DEFAULT) + { + if (default_case_was_found) + { + parser_raise_error (context_p, PARSER_ERR_MULTIPLE_DEFAULTS_NOT_ALLOWED); + } + + lexer_next_token (context_p); + if (context_p->token.type != LEXER_COLON) + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } + + default_case_was_found = PARSER_TRUE; + } + else if (context_p->token.type == LEXER_KEYW_CASE + || context_p->token.type == LEXER_RIGHT_BRACE) + { + if (switch_case_was_found) + { + parser_branch_node_t *new_case_p; + uint16_t opcode = CBC_BRANCH_IF_STRICT_EQUAL; + + if (context_p->token.type != LEXER_KEYW_CASE) + { + /* We don't duplicate the value for the last case. */ + parser_emit_cbc (context_p, CBC_STRICT_EQUAL); + opcode = CBC_BRANCH_IF_TRUE_FORWARD; + } + new_case_p = parser_emit_cbc_forward_branch_item (context_p, opcode, NULL); + if (cases_p == NULL) + { + switch_statement.branch_list_p = new_case_p; + parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); + } + else + { + cases_p->next_p = new_case_p; + } + cases_p = new_case_p; + } + + /* End of switch statement. */ + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + break; + } + + lexer_next_token (context_p); + + parser_parse_expression (context_p, PARSE_EXPR); + + if (context_p->token.type != LEXER_COLON) + { + parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); + } + switch_case_was_found = PARSER_TRUE; + } + + lexer_next_token (context_p); + } + + JERRY_ASSERT (switch_case_was_found || default_case_was_found); + + if (!switch_case_was_found) + { + /* There was no case statement, so the expression result + * of the switch must be popped from the stack */ + parser_emit_cbc (context_p, CBC_POP); + } + + parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &switch_statement.default_branch); + parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); + + if (!default_case_was_found) + { + parser_stack_change_last_uint8 (context_p, PARSER_STATEMENT_SWITCH_NO_DEFAULT); + } + + parser_set_range (context_p, &switch_body_start); + lexer_next_token (context_p); +} /* parser_parse_switch_statement_start */ + +/** + * Parse try statement (ending part). + */ +static void +parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ +{ + parser_try_statement_t try_statement; + parser_stack_iterator_t iterator; + + JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY); + + parser_stack_iterator_init (context_p, &iterator); + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &try_statement, sizeof (parser_try_statement_t)); + + lexer_next_token (context_p); + + if (try_statement.type == parser_finally_block) + { + parser_flush_cbc (context_p); + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#ifdef PARSER_DEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#endif + + parser_emit_cbc (context_p, CBC_CONTEXT_END); + parser_set_branch_to_current_position (context_p, &try_statement.branch); + } + else + { + parser_set_branch_to_current_position (context_p, &try_statement.branch); + + if (try_statement.type == parser_catch_block) + { + if (context_p->token.type != LEXER_KEYW_FINALLY) + { + parser_flush_cbc (context_p); + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#ifdef PARSER_DEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#endif + + parser_emit_cbc (context_p, CBC_CONTEXT_END); + parser_flush_cbc (context_p); + try_statement.type = parser_finally_block; + } + } + else if (try_statement.type == parser_try_block) + { + if (context_p->token.type != LEXER_KEYW_CATCH + && context_p->token.type != LEXER_KEYW_FINALLY) + { + parser_raise_error (context_p, PARSER_ERR_CATCH_FINALLY_EXPECTED); + } + } + } + + if (try_statement.type == parser_finally_block) + { + parser_stack_pop (context_p, NULL, (uint32_t) (sizeof (parser_try_statement_t) + 1)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + return; + } + + if (context_p->token.type == LEXER_KEYW_CATCH) + { + uint16_t literal_index; + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); + } + + lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + + literal_index = context_p->lit_object.index; + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + + try_statement.type = parser_catch_block; + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_CATCH, + &try_statement.branch); + + parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index); + parser_flush_cbc (context_p); + } + else + { + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FINALLY); + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + + try_statement.type = parser_finally_block; + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_FINALLY, + &try_statement.branch); + } + + lexer_next_token (context_p); + parser_stack_iterator_write (&iterator, &try_statement, sizeof (parser_try_statement_t)); +} /* parser_parse_try_statement_end */ + +/** + * Parse default statement. + */ +static void +parser_parse_default_statement (parser_context_t *context_p) /**< context */ +{ + parser_stack_iterator_t iterator; + parser_switch_statement_t switch_statement; + + if (context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH + && context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH_NO_DEFAULT) + { + parser_raise_error (context_p, PARSER_ERR_DEFAULT_NOT_IN_SWITCH); + } + + lexer_next_token (context_p); + /* Already checked in parser_parse_switch_statement_start. */ + JERRY_ASSERT (context_p->token.type == LEXER_COLON); + lexer_next_token (context_p); + + parser_stack_iterator_init (context_p, &iterator); + parser_stack_iterator_skip (&iterator, 1 + sizeof (parser_loop_statement_t)); + parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); + + parser_set_branch_to_current_position (context_p, &switch_statement.default_branch); +} /* parser_parse_default_statement */ + +/** + * Parse case statement. + */ +static void +parser_parse_case_statement (parser_context_t *context_p) /**< context */ +{ + parser_stack_iterator_t iterator; + parser_switch_statement_t switch_statement; + lexer_range_t dummy_range; + parser_branch_node_t *branch_p; + + if (context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH + && context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH_NO_DEFAULT) + { + parser_raise_error (context_p, PARSER_ERR_CASE_NOT_IN_SWITCH); + } + + parser_scan_until (context_p, &dummy_range, LEXER_COLON); + lexer_next_token (context_p); + + parser_stack_iterator_init (context_p, &iterator); + parser_stack_iterator_skip (&iterator, 1 + sizeof (parser_loop_statement_t)); + parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); + + /* Free memory after the case statement is found. */ + + branch_p = switch_statement.branch_list_p; + JERRY_ASSERT (branch_p != NULL); + switch_statement.branch_list_p = branch_p->next_p; + parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); + + parser_set_branch_to_current_position (context_p, &branch_p->branch); + parser_free (branch_p); +} /* parser_parse_case_statement */ + +/** + * Parse break statement. + */ +static void +parser_parse_break_statement (parser_context_t *context_p) /**< context */ +{ + parser_stack_iterator_t iterator; + cbc_opcode_t opcode = CBC_JUMP_FORWARD; + + lexer_next_token (context_p); + parser_stack_iterator_init (context_p, &iterator); + + if (!context_p->token.was_newline + && context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + /* The label with the same name is searched on the stack. */ + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + if (type == PARSER_STATEMENT_START) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_BREAK_LABEL); + } + + if (type == PARSER_STATEMENT_FOR_IN + || type == PARSER_STATEMENT_WITH + || type == PARSER_STATEMENT_TRY) + { + opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; + } + + if (type == PARSER_STATEMENT_LABEL) + { + parser_label_statement_t label_statement; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); + + if (lexer_same_identifiers (&context_p->token.lit_location, &label_statement.label_ident)) + { + label_statement.break_list_p = parser_emit_cbc_forward_branch_item (context_p, + opcode, + label_statement.break_list_p); + parser_stack_iterator_write (&iterator, &label_statement, sizeof (parser_label_statement_t)); + lexer_next_token (context_p); + return; + } + parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); + } + else + { + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + } + } + } + + /* The first switch or loop statement is searched. */ + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + if (type == PARSER_STATEMENT_START) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_BREAK); + } + + if (type == PARSER_STATEMENT_FOR_IN + || type == PARSER_STATEMENT_WITH + || type == PARSER_STATEMENT_TRY) + { + opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; + } + + if (type == PARSER_STATEMENT_SWITCH + || type == PARSER_STATEMENT_SWITCH_NO_DEFAULT + || type == PARSER_STATEMENT_DO_WHILE + || type == PARSER_STATEMENT_WHILE + || type == PARSER_STATEMENT_FOR + || type == PARSER_STATEMENT_FOR_IN) + { + parser_loop_statement_t loop; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); + loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p, + opcode, + loop.branch_list_p); + parser_stack_iterator_write (&iterator, &loop, sizeof (parser_loop_statement_t)); + return; + } + + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + } +} /* parser_parse_break_statement */ + +/** + * Parse continue statement. + */ +static void +parser_parse_continue_statement (parser_context_t *context_p) /**< context */ +{ + parser_stack_iterator_t iterator; + cbc_opcode_t opcode = CBC_JUMP_FORWARD; + + lexer_next_token (context_p); + parser_stack_iterator_init (context_p, &iterator); + + if (!context_p->token.was_newline + && context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + parser_stack_iterator_t loop_iterator; + int for_in_was_seen = PARSER_FALSE; + + loop_iterator.current_p = NULL; + + /* The label with the same name is searched on the stack. */ + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + + if (type == PARSER_STATEMENT_START) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CONTINUE_LABEL); + } + + /* Only those labels are checked, whose are label of a loop. */ + if (loop_iterator.current_p != NULL && type == PARSER_STATEMENT_LABEL) + { + parser_label_statement_t label_statement; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); + + if (lexer_same_identifiers (&context_p->token.lit_location, &label_statement.label_ident)) + { + parser_loop_statement_t loop; + + parser_stack_iterator_skip (&loop_iterator, 1); + parser_stack_iterator_read (&loop_iterator, &loop, sizeof (parser_loop_statement_t)); + loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p, + opcode, + loop.branch_list_p); + loop.branch_list_p->branch.offset |= CBC_HIGHEST_BIT_MASK; + parser_stack_iterator_write (&loop_iterator, &loop, sizeof (parser_loop_statement_t)); + lexer_next_token (context_p); + return; + } + parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); + continue; + } + + if (type == PARSER_STATEMENT_WITH + || type == PARSER_STATEMENT_TRY + || for_in_was_seen) + { + opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; + } + else if (type == PARSER_STATEMENT_FOR_IN) + { + for_in_was_seen = PARSER_TRUE; + } + + if (type == PARSER_STATEMENT_DO_WHILE + || type == PARSER_STATEMENT_WHILE + || type == PARSER_STATEMENT_FOR + || type == PARSER_STATEMENT_FOR_IN) + { + loop_iterator = iterator; + } + else + { + loop_iterator.current_p = NULL; + } + + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + } + } + + /* The first loop statement is searched. */ + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + if (type == PARSER_STATEMENT_START) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CONTINUE); + } + + if (type == PARSER_STATEMENT_DO_WHILE + || type == PARSER_STATEMENT_WHILE + || type == PARSER_STATEMENT_FOR + || type == PARSER_STATEMENT_FOR_IN) + { + parser_loop_statement_t loop; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); + loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p, + opcode, + loop.branch_list_p); + loop.branch_list_p->branch.offset |= CBC_HIGHEST_BIT_MASK; + parser_stack_iterator_write (&iterator, &loop, sizeof (parser_loop_statement_t)); + return; + } + + if (type == PARSER_STATEMENT_WITH + || type == PARSER_STATEMENT_TRY) + { + opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; + } + + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + } +} /* parser_parse_continue_statement */ + +/** + * Parse label statement. + */ +static void +parser_parse_label (parser_context_t *context_p, /**< context */ + lexer_lit_location_t *label_literal_p) /**< saved literal */ +{ + parser_stack_iterator_t iterator; + parser_label_statement_t label_statement; + + parser_stack_iterator_init (context_p, &iterator); + + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + if (type == PARSER_STATEMENT_START) + { + break; + } + + if (type == PARSER_STATEMENT_LABEL) + { + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); + parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); + + if (lexer_same_identifiers (label_literal_p, &label_statement.label_ident)) + { + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_LABEL); + } + } + else + { + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + } + } + + label_statement.label_ident = *label_literal_p; + label_statement.break_list_p = NULL; + parser_stack_push (context_p, &label_statement, sizeof (parser_label_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_LABEL); + parser_stack_iterator_init (context_p, &context_p->last_statement); +} /* parser_parse_label */ + +/** + * Parse statements. + */ +void +parser_parse_statements (parser_context_t *context_p) /**< context */ +{ + /* Statement parsing cannot be nested. */ + JERRY_ASSERT (context_p->last_statement.current_p == NULL); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_START); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + while (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_STRING_LITERAL) + { + lexer_lit_location_t lit_location; + uint32_t status_flags = context_p->status_flags; +#ifdef PARSER_DUMP_BYTE_CODE + int switch_to_strict_mode = PARSER_FALSE; +#endif + + JERRY_ASSERT (context_p->stack_depth == 0); + + lit_location = context_p->token.lit_location; + + if (lit_location.length == PARSER_USE_STRICT_LENGTH + && !lit_location.has_escape + && memcmp (PARSER_USE_STRICT_LITERAL, lit_location.char_p, PARSER_USE_STRICT_LENGTH) == 0) + { + context_p->status_flags |= PARSER_IS_STRICT; + +#ifdef PARSER_DUMP_BYTE_CODE + switch_to_strict_mode = PARSER_TRUE; +#endif + } + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_SEMICOLON + && context_p->token.type != LEXER_RIGHT_BRACE) + { + if (!context_p->token.was_newline + || LEXER_IS_BINARY_OP_TOKEN (context_p->token.type) + || context_p->token.type == LEXER_LEFT_PAREN + || context_p->token.type == LEXER_LEFT_SQUARE + || context_p->token.type == LEXER_DOT) + { + /* The string is part of an expression statement. */ + context_p->status_flags = status_flags; + + lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL); + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + /* The extra_value is used for saving the token. */ + context_p->token.extra_value = context_p->token.type; + context_p->token.type = LEXER_EXPRESSION_START; + break; + } + } + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes + && switch_to_strict_mode) + { + printf (" Note: switch to strict mode\n\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + if (context_p->token.type == LEXER_SEMICOLON) + { + lexer_next_token (context_p); + } + + /* The last directive prologue can be the result of the script. */ + if (!(context_p->status_flags & PARSER_IS_FUNCTION)) + { + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_STRING_LITERAL) + { + lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL); + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + parser_emit_cbc (context_p, CBC_POP_BLOCK); + parser_flush_cbc (context_p); + } + } + } + + if (context_p->status_flags & PARSER_IS_STRICT + && context_p->status_flags & PARSER_HAS_NON_STRICT_ARG) + { + parser_raise_error (context_p, PARSER_ERR_NON_STRICT_ARG_DEFINITION); + } + + while (context_p->token.type != LEXER_EOS + || context_p->stack_top_uint8 != PARSER_STATEMENT_START) + { +#ifdef PARSER_DEBUG + JERRY_ASSERT (context_p->stack_depth == context_p->context_stack_depth); +#endif + + switch (context_p->token.type) + { + case LEXER_SEMICOLON: + { + break; + } + + case LEXER_RIGHT_BRACE: + { + if (context_p->stack_top_uint8 == PARSER_STATEMENT_LABEL + || context_p->stack_top_uint8 == PARSER_STATEMENT_IF + || context_p->stack_top_uint8 == PARSER_STATEMENT_ELSE + || context_p->stack_top_uint8 == PARSER_STATEMENT_DO_WHILE + || context_p->stack_top_uint8 == PARSER_STATEMENT_WHILE + || context_p->stack_top_uint8 == PARSER_STATEMENT_FOR + || context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_IN + || context_p->stack_top_uint8 == PARSER_STATEMENT_WITH) + { + parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED); + } + break; + } + + case LEXER_LEFT_BRACE: + { + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); + parser_stack_iterator_init (context_p, &context_p->last_statement); + lexer_next_token (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_VAR: + { + parser_parse_var_statement (context_p); + break; + } + + case LEXER_KEYW_FUNCTION: + { + parser_parse_function_statement (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_IF: + { + parser_parse_if_statement_start (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_SWITCH: + { + parser_parse_switch_statement_start (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_DO: + { + parser_do_while_statement_t do_while_statement; + parser_loop_statement_t loop; + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + + do_while_statement.start_offset = context_p->byte_code_size; + loop.branch_list_p = NULL; + + parser_stack_push (context_p, &do_while_statement, sizeof (parser_do_while_statement_t)); + parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_DO_WHILE); + parser_stack_iterator_init (context_p, &context_p->last_statement); + lexer_next_token (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_WHILE: + { + parser_parse_while_statement_start (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_FOR: + { + parser_parse_for_statement_start (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_WITH: + { + parser_parse_with_statement_start (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_TRY: + { + parser_try_statement_t try_statement; + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + +#ifdef PARSER_DEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); +#endif + + try_statement.type = parser_try_block; + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_TRY_CREATE_CONTEXT, + &try_statement.branch); + + parser_stack_push (context_p, &try_statement, sizeof (parser_try_statement_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_TRY); + parser_stack_iterator_init (context_p, &context_p->last_statement); + lexer_next_token (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_DEFAULT: + { + parser_parse_default_statement (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_CASE: + { + parser_parse_case_statement (context_p); + continue; + /* FALLTHRU */ + } + + case LEXER_KEYW_BREAK: + { + parser_parse_break_statement (context_p); + break; + } + + case LEXER_KEYW_CONTINUE: + { + parser_parse_continue_statement (context_p); + break; + } + + case LEXER_KEYW_THROW: + { + lexer_next_token (context_p); + if (context_p->token.was_newline) + { + parser_raise_error (context_p, PARSER_ERR_EXPRESSION_EXPECTED); + } + parser_parse_expression (context_p, PARSE_EXPR); + parser_emit_cbc (context_p, CBC_THROW); + break; + } + + case LEXER_KEYW_RETURN: + { + if (!(context_p->status_flags & PARSER_IS_FUNCTION)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_RETURN); + } + + lexer_next_token (context_p); + if (context_p->token.was_newline + || context_p->token.type == LEXER_SEMICOLON + || context_p->token.type == LEXER_RIGHT_BRACE) + { + parser_emit_cbc (context_p, CBC_RETURN_WITH_BLOCK); + break; + } + + parser_parse_expression (context_p, PARSE_EXPR); + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL; + } + else + { + parser_emit_cbc (context_p, CBC_RETURN); + } + break; + } + + case LEXER_KEYW_DEBUGGER: + { + parser_emit_cbc_ext (context_p, CBC_EXT_DEBUGGER); + lexer_next_token (context_p); + break; + } + + case LEXER_LITERAL: + { + if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + lexer_lit_location_t lit_location = context_p->token.lit_location; + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_COLON) + { + parser_parse_label (context_p, &lit_location); + lexer_next_token (context_p); + continue; + } + + lexer_construct_literal_object (context_p, &lit_location, LEXER_IDENT_LITERAL); + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + /* The extra_value is used for saving the token. */ + context_p->token.extra_value = context_p->token.type; + context_p->token.type = LEXER_EXPRESSION_START; + } + /* FALLTHRU */ + } + + default: + { + int options = PARSE_EXPR_BLOCK; + + if (context_p->status_flags & PARSER_IS_FUNCTION) + { + options = PARSE_EXPR_STATEMENT; + } + + if (context_p->token.type == LEXER_EXPRESSION_START) + { + /* Restore the token type form the extra_value. */ + context_p->token.type = context_p->token.extra_value; + options |= PARSE_EXPR_HAS_LITERAL; + } + + parser_parse_expression (context_p, options); + break; + } + } + + parser_flush_cbc (context_p); + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK) + { + parser_stack_pop_uint8 (context_p); + parser_stack_iterator_init (context_p, &context_p->last_statement); + lexer_next_token (context_p); + } + else if (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH + || context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH_NO_DEFAULT) + { + int has_default = (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH); + parser_loop_statement_t loop; + parser_switch_statement_t switch_statement; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_pop (context_p, &switch_statement, sizeof (parser_switch_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + JERRY_ASSERT (switch_statement.branch_list_p == NULL); + + if (!has_default) + { + parser_set_branch_to_current_position (context_p, &switch_statement.default_branch); + } + + parser_set_breaks_to_current_position (context_p, loop.branch_list_p); + lexer_next_token (context_p); + } + else if (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY) + { + parser_parse_try_statement_end (context_p); + } + else if (context_p->stack_top_uint8 == PARSER_STATEMENT_START) + { + if (context_p->status_flags & PARSER_IS_CLOSURE) + { + parser_stack_pop_uint8 (context_p); + context_p->last_statement.current_p = NULL; + JERRY_ASSERT (context_p->stack_depth == 0); +#ifdef PARSER_DEBUG + JERRY_ASSERT (context_p->context_stack_depth == 0); +#endif + /* There is no lexer_next_token here, since the + * next token belongs to the parent context. */ + return; + } + parser_raise_error (context_p, PARSER_ERR_INVALID_RIGHT_SQUARE); + } + } + else if (context_p->token.type == LEXER_SEMICOLON) + { + lexer_next_token (context_p); + } + else if (context_p->token.type != LEXER_EOS + && !context_p->token.was_newline) + { + parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); + } + + while (PARSER_TRUE) + { + switch (context_p->stack_top_uint8) + { + case PARSER_STATEMENT_LABEL: + { + parser_label_statement_t label; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &label, sizeof (parser_label_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_set_breaks_to_current_position (context_p, label.break_list_p); + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_IF: + { + if (parser_parse_if_statement_end (context_p)) + { + break; + } + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_ELSE: + { + parser_if_else_statement_t else_statement; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &else_statement, sizeof (parser_if_else_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_set_branch_to_current_position (context_p, &else_statement.branch); + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_DO_WHILE: + { + parser_parse_do_while_statement_end (context_p); + if (context_p->token.type == LEXER_SEMICOLON) + { + lexer_next_token (context_p); + } + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_WHILE: + { + parser_parse_while_statement_end (context_p); + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_FOR: + { + parser_parse_for_statement_end (context_p); + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_FOR_IN: + { + parser_for_in_statement_t for_in_statement; + parser_loop_statement_t loop; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t)); + parser_stack_pop (context_p, &for_in_statement, sizeof (parser_for_in_statement_t)); + parser_stack_iterator_init (context_p, &context_p->last_statement); + + parser_set_continues_to_current_position (context_p, loop.branch_list_p); + + parser_flush_cbc (context_p); + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); +#ifdef PARSER_DEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); +#endif + + parser_emit_cbc_ext_backward_branch (context_p, + CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT, + for_in_statement.start_offset); + + parser_set_breaks_to_current_position (context_p, loop.branch_list_p); + parser_set_branch_to_current_position (context_p, &for_in_statement.branch); + continue; + /* FALLTHRU */ + } + + case PARSER_STATEMENT_WITH: + { + parser_parse_with_statement_end (context_p); + continue; + /* FALLTHRU */ + } + + default: + { + break; + } + } + break; + } + } + + JERRY_ASSERT (context_p->stack_depth == 0); +#ifdef PARSER_DEBUG + JERRY_ASSERT (context_p->context_stack_depth == 0); +#endif + + parser_stack_pop_uint8 (context_p); + context_p->last_statement.current_p = NULL; + + if (context_p->status_flags & PARSER_IS_CLOSURE) + { + parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED); + } +} /* parser_parse_statements */ + +/** + * Free jumps stored on the stack if a parse error is occured. + */ +void PARSER_NOINLINE +parser_free_jumps (parser_stack_iterator_t iterator) /**< iterator position */ +{ + while (PARSER_TRUE) + { + uint8_t type = parser_stack_iterator_read_uint8 (&iterator); + parser_branch_node_t *branch_list_p = NULL; + + switch (type) + { + case PARSER_STATEMENT_START: + { + return; + } + + case PARSER_STATEMENT_LABEL: + { + parser_label_statement_t label; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &label, sizeof (parser_label_statement_t)); + parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); + branch_list_p = label.break_list_p; + break; + } + + case PARSER_STATEMENT_SWITCH: + case PARSER_STATEMENT_SWITCH_NO_DEFAULT: + { + parser_switch_statement_t switch_statement; + parser_loop_statement_t loop; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); + parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t)); + parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t)); + parser_stack_iterator_skip (&iterator, sizeof (parser_switch_statement_t)); + + branch_list_p = switch_statement.branch_list_p; + while (branch_list_p != NULL) + { + parser_branch_node_t *next_p = branch_list_p->next_p; + parser_free (branch_list_p); + branch_list_p = next_p; + } + branch_list_p = loop.branch_list_p; + break; + } + + case PARSER_STATEMENT_DO_WHILE: + case PARSER_STATEMENT_WHILE: + case PARSER_STATEMENT_FOR: + case PARSER_STATEMENT_FOR_IN: + { + parser_loop_statement_t loop; + + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t)); + parser_stack_iterator_skip (&iterator, parser_statement_length (type) - 1); + branch_list_p = loop.branch_list_p; + break; + } + + default: + { + parser_stack_iterator_skip (&iterator, parser_statement_length (type)); + continue; + } + } + + while (branch_list_p != NULL) + { + parser_branch_node_t *next_p = branch_list_p->next_p; + parser_free (branch_list_p); + branch_list_p = next_p; + } + } +} /* parser_free_jumps */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-parser-util.cpp b/jerry-core/parser/js/js-parser-util.cpp new file mode 100644 index 0000000000..5c870061b3 --- /dev/null +++ b/jerry-core/parser/js/js-parser-util.cpp @@ -0,0 +1,942 @@ +/* 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 "js-parser-internal.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_utils Utility + * @{ + */ + +/**********************************************************************/ +/* Emitting byte codes */ +/**********************************************************************/ + +/** + * Append two bytes to the cbc stream. + */ +static void +parser_emit_two_bytes (parser_context_t *context_p, /**< context */ + uint8_t first_byte, /**< first byte */ + uint8_t second_byte) /**< second byte */ +{ + uint32_t last_position = context_p->byte_code.last_position; + + if (last_position + 2 <= PARSER_CBC_STREAM_PAGE_SIZE) + { + parser_mem_page_t *page_p = context_p->byte_code.last_p; + + page_p->bytes[last_position] = first_byte; + page_p->bytes[last_position + 1] = second_byte; + context_p->byte_code.last_position = last_position + 2; + } + else if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) + { + parser_mem_page_t *page_p; + + parser_cbc_stream_alloc_page (context_p, &context_p->byte_code); + page_p = context_p->byte_code.last_p; + page_p->bytes[0] = first_byte; + page_p->bytes[1] = second_byte; + context_p->byte_code.last_position = 2; + } + else + { + context_p->byte_code.last_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] = first_byte; + parser_cbc_stream_alloc_page (context_p, &context_p->byte_code); + context_p->byte_code.last_p->bytes[0] = second_byte; + context_p->byte_code.last_position = 1; + } +} /* parser_emit_two_bytes */ + +#define PARSER_APPEND_TO_BYTE_CODE(context_p, byte) \ + if ((context_p)->byte_code.last_position >= PARSER_CBC_STREAM_PAGE_SIZE) \ + { \ + parser_cbc_stream_alloc_page ((context_p), &(context_p)->byte_code); \ + } \ + (context_p)->byte_code.last_p->bytes[(context_p)->byte_code.last_position++] = (uint8_t) (byte) + +/** + * Append the current byte code to the stream + */ +void +parser_flush_cbc (parser_context_t *context_p) /**< context */ +{ + uint8_t flags; + + if (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE) + { + return; + } + + context_p->status_flags |= PARSER_NO_END_LABEL; + + if (PARSER_IS_BASIC_OPCODE (context_p->last_cbc_opcode)) + { + cbc_opcode_t opcode = (cbc_opcode_t) context_p->last_cbc_opcode; + flags = cbc_flags[opcode]; + + PARSER_APPEND_TO_BYTE_CODE (context_p, opcode); + context_p->byte_code_size++; + } + else + { + cbc_ext_opcode_t opcode = (cbc_ext_opcode_t) PARSER_GET_EXT_OPCODE (context_p->last_cbc_opcode); + + flags = cbc_ext_flags[opcode]; + parser_emit_two_bytes (context_p, CBC_EXT_OPCODE, opcode); + context_p->byte_code_size += 2; + } + + JERRY_ASSERT ((flags >> CBC_STACK_ADJUST_SHIFT) >= CBC_STACK_ADJUST_BASE + || (CBC_STACK_ADJUST_BASE - (flags >> CBC_STACK_ADJUST_SHIFT)) <= context_p->stack_depth); + PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, CBC_STACK_ADJUST_VALUE (flags)); + + if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) + { + uint16_t literal_index = context_p->last_cbc.literal_index; + + parser_emit_two_bytes (context_p, + (uint8_t) (literal_index & 0xff), + (uint8_t) (literal_index >> 8)); + context_p->byte_code_size += 2; + } + + if (flags & CBC_HAS_LITERAL_ARG2) + { + uint16_t literal_index = context_p->last_cbc.value; + + parser_emit_two_bytes (context_p, + (uint8_t) (literal_index & 0xff), + (uint8_t) (literal_index >> 8)); + context_p->byte_code_size += 2; + + if (!(flags & CBC_HAS_LITERAL_ARG)) + { + literal_index = context_p->last_cbc.third_literal_index; + + parser_emit_two_bytes (context_p, + (uint8_t) (literal_index & 0xff), + (uint8_t) (literal_index >> 8)); + context_p->byte_code_size += 2; + } + } + + if (flags & CBC_HAS_BYTE_ARG) + { + uint8_t byte_argument = (uint8_t) context_p->last_cbc.value; + + JERRY_ASSERT (context_p->last_cbc.value <= CBC_MAXIMUM_BYTE_VALUE); + + if (flags & CBC_POP_STACK_BYTE_ARG) + { + JERRY_ASSERT (context_p->stack_depth >= byte_argument); + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, byte_argument); + } + + PARSER_APPEND_TO_BYTE_CODE (context_p, byte_argument); + context_p->byte_code_size++; + } + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + const char *name_p; + + if (PARSER_IS_BASIC_OPCODE (context_p->last_cbc_opcode)) + { + name_p = cbc_names[context_p->last_cbc_opcode]; + } + else + { + name_p = cbc_ext_names[PARSER_GET_EXT_OPCODE (context_p->last_cbc_opcode)]; + } + + printf (" [%3d] %s", (int) context_p->stack_depth, name_p); + + if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) + { + uint16_t literal_index = context_p->last_cbc.literal_index; + lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); + printf (" idx:%d->", literal_index); + util_print_literal (literal_p); + } + + if (flags & CBC_HAS_LITERAL_ARG2) + { + uint16_t literal_index = context_p->last_cbc.value; + lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); + printf (" idx:%d->", literal_index); + util_print_literal (literal_p); + + if (!(flags & CBC_HAS_LITERAL_ARG)) + { + literal_index = context_p->last_cbc.third_literal_index; + + lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); + printf (" idx:%d->", literal_index); + util_print_literal (literal_p); + } + } + + if (flags & CBC_HAS_BYTE_ARG) + { + printf (" byte_arg:%d", (int) context_p->last_cbc.value); + } + + printf ("\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + if (context_p->stack_depth > context_p->stack_limit) + { + context_p->stack_limit = context_p->stack_depth; + if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + } + + context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; +} /* parser_flush_cbc */ + +/** + * Append a byte code + */ +void +parser_emit_cbc (parser_context_t *context_p, /**< context */ + uint16_t opcode) /**< opcode */ +{ + JERRY_ASSERT (PARSER_ARGS_EQ (opcode, 0)); + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->last_cbc_opcode = opcode; +} /* parser_emit_cbc */ + +/** + * Append a byte code with a literal argument + */ +void +parser_emit_cbc_literal (parser_context_t *context_p, /**< context */ + uint16_t opcode, /**< opcode */ + uint16_t literal_index) /**< literal index */ +{ + JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_LITERAL_ARG)); + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->last_cbc_opcode = opcode; + context_p->last_cbc.literal_index = literal_index; + context_p->last_cbc.literal_type = LEXER_UNUSED_LITERAL; + context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; +} /* parser_emit_cbc_literal */ + +/** + * Append a byte code with the current literal argument + */ +void +parser_emit_cbc_literal_from_token (parser_context_t *context_p, /**< context */ + uint16_t opcode) /**< opcode */ +{ + JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_LITERAL_ARG)); + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->last_cbc_opcode = opcode; + context_p->last_cbc.literal_index = context_p->lit_object.index; + context_p->last_cbc.literal_type = context_p->token.lit_location.type; + context_p->last_cbc.literal_object_type = context_p->lit_object.type; +} /* parser_emit_cbc_literal_from_token */ + +/** + * Append a byte code with a call argument + */ +void +parser_emit_cbc_call (parser_context_t *context_p, /**< context */ + uint16_t opcode, /**< opcode */ + size_t call_arguments) /**< number of arguments */ +{ + JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_BYTE_ARG)); + JERRY_ASSERT (call_arguments <= CBC_MAXIMUM_BYTE_VALUE); + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->last_cbc_opcode = opcode; + context_p->last_cbc.value = (uint16_t) call_arguments; +} /* parser_emit_cbc_call */ + +/** + * Append a push number 1/2 byte code + */ +void +parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */ + int is_negative_number) /**< sign is negative */ +{ + uint16_t value = context_p->lit_object.index; + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + JERRY_ASSERT (value < CBC_PUSH_NUMBER_1_RANGE_END); + JERRY_ASSERT (CBC_STACK_ADJUST_VALUE (cbc_flags[CBC_PUSH_NUMBER_1]) == 1); + + context_p->stack_depth++; + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + int real_value = value; + + if (is_negative_number) + { + real_value = -real_value; + } + + printf (" [%3d] %s number:%d\n", (int) context_p->stack_depth, cbc_names[CBC_PUSH_NUMBER_1], real_value); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + if (is_negative_number) + { + PARSER_PLUS_EQUAL_U16 (value, CBC_PUSH_NUMBER_1_RANGE_END); + } + + parser_emit_two_bytes (context_p, + CBC_PUSH_NUMBER_1, + (uint8_t) value); + + context_p->byte_code_size += 2; + + if (context_p->stack_depth > context_p->stack_limit) + { + context_p->stack_limit = context_p->stack_depth; + if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + } +} /* parser_emit_cbc_push_number */ + +/** + * Append a byte code with a branch argument + */ +void +parser_emit_cbc_forward_branch (parser_context_t *context_p, /**< context */ + uint16_t opcode, /**< opcode */ + parser_branch_t *branch_p) /**< branch result */ +{ + uint8_t flags; + uint32_t extra_byte_code_increase; + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->status_flags |= PARSER_NO_END_LABEL; + + if (PARSER_IS_BASIC_OPCODE (opcode)) + { + flags = cbc_flags[opcode]; + extra_byte_code_increase = 0; + } + else + { + PARSER_APPEND_TO_BYTE_CODE (context_p, CBC_EXT_OPCODE); + opcode = (uint16_t) PARSER_GET_EXT_OPCODE (opcode); + + flags = cbc_ext_flags[opcode]; + extra_byte_code_increase = 1; + } + + JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG); + JERRY_ASSERT (CBC_BRANCH_IS_FORWARD (flags)); + JERRY_ASSERT (CBC_BRANCH_OFFSET_LENGTH (opcode) == 1); + + /* Branch opcodes never push anything onto the stack. */ + JERRY_ASSERT ((flags >> CBC_STACK_ADJUST_SHIFT) >= CBC_STACK_ADJUST_BASE + || (CBC_STACK_ADJUST_BASE - (flags >> CBC_STACK_ADJUST_SHIFT)) <= context_p->stack_depth); + PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, CBC_STACK_ADJUST_VALUE (flags)); + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + if (extra_byte_code_increase == 0) + { + printf (" [%3d] %s\n", (int) context_p->stack_depth, cbc_names[opcode]); + } + else + { + printf (" [%3d] %s\n", (int) context_p->stack_depth, cbc_ext_names[opcode]); + } + } +#endif /* PARSER_DUMP_BYTE_CODE */ + +#if PARSER_MAXIMUM_CODE_SIZE <= 65535 + opcode++; +#else /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + PARSER_PLUS_EQUAL_U16 (opcode, 2); +#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + + parser_emit_two_bytes (context_p, (uint8_t) opcode, 0); + branch_p->page_p = context_p->byte_code.last_p; + branch_p->offset = (context_p->byte_code.last_position - 1) | (context_p->byte_code_size << 8); + + context_p->byte_code_size += extra_byte_code_increase; + +#if PARSER_MAXIMUM_CODE_SIZE <= 65535 + PARSER_APPEND_TO_BYTE_CODE (context_p, 0); + context_p->byte_code_size += 3; +#else /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + parser_emit_two_bytes (context_p, 0, 0); + context_p->byte_code_size += 4; +#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + + if (context_p->stack_depth > context_p->stack_limit) + { + context_p->stack_limit = context_p->stack_depth; + if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + } +} /* parser_emit_cbc_forward_branch */ + +/** + * Append a branch byte code and create an item + */ +parser_branch_node_t * +parser_emit_cbc_forward_branch_item (parser_context_t *context_p, /**< context */ + uint16_t opcode, /**< opcode */ + parser_branch_node_t *next_p) /**< next branch */ +{ + parser_branch_t branch; + parser_branch_node_t *new_item; + + /* Since byte code insertion may throw an out-of-memory error, + * the branch is constructed locally, and copied later. */ + parser_emit_cbc_forward_branch (context_p, opcode, &branch); + + new_item = (parser_branch_node_t *) parser_malloc (context_p, sizeof (parser_branch_node_t)); + new_item->branch = branch; + new_item->next_p = next_p; + return new_item; +} /* parser_emit_cbc_forward_branch_item */ + +/** + * Append a byte code with a branch argument + */ +void +parser_emit_cbc_backward_branch (parser_context_t *context_p, /**< context */ + uint16_t opcode, /**< opcode */ + uint32_t offset) /**< destination offset */ +{ + uint8_t flags; +#ifdef PARSER_DUMP_BYTE_CODE + const char *name; +#endif /* PARSER_DUMP_BYTE_CODE */ + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->status_flags |= PARSER_NO_END_LABEL; + offset = context_p->byte_code_size - offset; + + if (PARSER_IS_BASIC_OPCODE (opcode)) + { + flags = cbc_flags[opcode]; + +#ifdef PARSER_DUMP_BYTE_CODE + name = cbc_names[opcode]; +#endif /* PARSER_DUMP_BYTE_CODE */ + } + else + { + PARSER_APPEND_TO_BYTE_CODE (context_p, CBC_EXT_OPCODE); + opcode = (uint16_t) PARSER_GET_EXT_OPCODE (opcode); + + flags = cbc_ext_flags[opcode]; + context_p->byte_code_size++; + +#ifdef PARSER_DUMP_BYTE_CODE + name = cbc_ext_names[opcode]; +#endif /* PARSER_DUMP_BYTE_CODE */ + } + + JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG); + JERRY_ASSERT (CBC_BRANCH_IS_BACKWARD (flags)); + JERRY_ASSERT (CBC_BRANCH_OFFSET_LENGTH (opcode) == 1); + JERRY_ASSERT (offset <= context_p->byte_code_size); + + /* Branch opcodes never push anything onto the stack. */ + JERRY_ASSERT ((flags >> CBC_STACK_ADJUST_SHIFT) >= CBC_STACK_ADJUST_BASE + || (CBC_STACK_ADJUST_BASE - (flags >> CBC_STACK_ADJUST_SHIFT)) <= context_p->stack_depth); + PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, CBC_STACK_ADJUST_VALUE (flags)); + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + printf (" [%3d] %s\n", (int) context_p->stack_depth, name); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + context_p->byte_code_size += 2; +#if PARSER_MAXIMUM_CODE_SIZE <= 65535 + if (offset > 255) + { + opcode++; + context_p->byte_code_size++; + } +#else /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + if (offset > 65535) + { + PARSER_PLUS_EQUAL_U16 (opcode, 2); + context_p->byte_code_size += 2; + } + else if (offset > 255) + { + opcode++; + context_p->byte_code_size++; + } +#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + + PARSER_APPEND_TO_BYTE_CODE (context_p, (uint8_t) opcode); + +#if PARSER_MAXIMUM_CODE_SIZE > 65535 + if (offset > 65535) + { + PARSER_APPEND_TO_BYTE_CODE (context_p, offset >> 16); + } +#endif /* PARSER_MAXIMUM_CODE_SIZE > 65535 */ + + if (offset > 255) + { + PARSER_APPEND_TO_BYTE_CODE (context_p, (offset >> 8) & 0xff); + } + + PARSER_APPEND_TO_BYTE_CODE (context_p, offset & 0xff); +} /* parser_emit_cbc_backward_branch */ + +#undef PARSER_CHECK_LAST_POSITION +#undef PARSER_APPEND_TO_BYTE_CODE + +/** + * Set a branch to the current byte code position + */ +void +parser_set_branch_to_current_position (parser_context_t *context_p, /**< context */ + parser_branch_t *branch_p) /**< branch result */ +{ + uint32_t delta; + size_t offset; + parser_mem_page_t *page_p = branch_p->page_p; + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->status_flags &= ~PARSER_NO_END_LABEL; + + JERRY_ASSERT (context_p->byte_code_size > (branch_p->offset >> 8)); + + delta = context_p->byte_code_size - (branch_p->offset >> 8); + offset = (branch_p->offset & CBC_LOWER_SEVEN_BIT_MASK); + + JERRY_ASSERT (delta <= PARSER_MAXIMUM_CODE_SIZE); + +#if PARSER_MAXIMUM_CODE_SIZE <= 65535 + page_p->bytes[offset++] = (uint8_t) (delta >> 8); + if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) + { + page_p = page_p->next_p; + offset = 0; + } +#else + page_p->bytes[offset++] = (uint8_t) (delta >> 16); + if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) + { + page_p = page_p->next_p; + offset = 0; + } + page_p->bytes[offset++] = (uint8_t) ((delta >> 8) & 0xff); + if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) + { + page_p = page_p->next_p; + offset = 0; + } +#endif + page_p->bytes[offset++] = delta & 0xff; +} /* parser_set_branch_to_current_position */ + +/** + * Set breaks to the current byte code position + */ +void +parser_set_breaks_to_current_position (parser_context_t *context_p, /**< context */ + parser_branch_node_t *current_p) /**< branch list */ +{ + while (current_p != NULL) + { + parser_branch_node_t *next_p = current_p->next_p; + + if (!(current_p->branch.offset & CBC_HIGHEST_BIT_MASK)) + { + parser_set_branch_to_current_position (context_p, ¤t_p->branch); + } + parser_free (current_p); + current_p = next_p; + } +} /* parser_set_breaks_to_current_position */ + +/** + * Set continues to the current byte code position + */ +void +parser_set_continues_to_current_position (parser_context_t *context_p, /**< context */ + parser_branch_node_t *current_p) /**< branch list */ +{ + while (current_p != NULL) + { + if (current_p->branch.offset & CBC_HIGHEST_BIT_MASK) + { + parser_set_branch_to_current_position (context_p, ¤t_p->branch); + } + current_p = current_p->next_p; + } +} /* parser_set_continues_to_current_position */ + +/** + * Returns with the striong representation of the error + */ +const char * +parser_error_to_string (parser_error_t error) /**< error code */ +{ + switch (error) + { + case PARSER_ERR_OUT_OF_MEMORY: + { + return "Out of memory."; + } + case PARSER_ERR_LITERAL_LIMIT_REACHED: + { + return "Maximum number of literals reached."; + } + case PARSER_ERR_ARGUMENT_LIMIT_REACHED: + { + return "Maximum number of function arguments reached."; + } + case PARSER_ERR_STACK_LIMIT_REACHED: + { + return "Maximum function stack size reached."; + } + case PARSER_ERR_REGISTER_LIMIT_REACHED: + { + return "Maximum number of registers is reached."; + } + case PARSER_ERR_INVALID_CHARACTER: + { + return "Invalid (unexpected) character."; + } + case PARSER_ERR_INVALID_HEX_DIGIT: + { + return "Invalid hexadecimal digit."; + } + case PARSER_ERR_INVALID_ESCAPE_SEQUENCE: + { + return "Invalid escape sequence."; + } + case PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE: + { + return "Invalid unicode escape sequence."; + } + case PARSER_ERR_INVALID_IDENTIFIER_START: + { + return "Character cannot be start of an identifier."; + } + case PARSER_ERR_INVALID_IDENTIFIER_PART: + { + return "Character cannot be part of an identifier."; + } + case PARSER_ERR_INVALID_NUMBER: + { + return "Invalid number."; + } + case PARSER_ERR_MISSING_EXPONENT: + { + return "Missing exponent part."; + } + case PARSER_ERR_IDENTIFIER_AFTER_NUMBER: + { + return "Identifier cannot start after a number."; + } + case PARSER_ERR_INVALID_REGEXP: + { + return "Invalid regular expression."; + } + case PARSER_ERR_UNKNOWN_REGEXP_FLAG: + { + return "Unknown regexp flag."; + } + case PARSER_ERR_DUPLICATED_REGEXP_FLAG: + { + return "Duplicated regexp flag."; + } + case PARSER_ERR_UNSUPPORTED_REGEXP: + { + return "Regexp is not supported in compact profile."; + } + case PARSER_ERR_IDENTIFIER_TOO_LONG: + { + return "Identifier is too long."; + } + case PARSER_ERR_STRING_TOO_LONG: + { + return "String is too long."; + } + case PARSER_ERR_NUMBER_TOO_LONG: + { + return "Number too long."; + } + case PARSER_ERR_REGEXP_TOO_LONG: + { + return "Regexp too long."; + } + case PARSER_ERR_UNTERMINATED_MULTILINE_COMMENT: + { + return "Unterminated multiline comment."; + } + case PARSER_ERR_UNTERMINATED_STRING: + { + return "Unterminated string literal."; + } + case PARSER_ERR_UNTERMINATED_REGEXP: + { + return "Unterminated regexp literal."; + } + case PARSER_ERR_NEWLINE_NOT_ALLOWED: + { + return "Newline is not allowed in strings or regexps."; + } + case PARSER_ERR_OCTAL_NUMBER_NOT_ALLOWED: + { + return "Octal numbers are not allowed in strict mode."; + } + case PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED: + { + return "Octal escape sequences are not allowed in strict mode."; + } + case PARSER_ERR_STRICT_IDENT_NOT_ALLOWED: + { + return "Identifier name is reserved in strict mode."; + } + case PARSER_ERR_EVAL_NOT_ALLOWED: + { + return "Eval is not allowed to use here in strict mode."; + } + case PARSER_ERR_ARGUMENTS_NOT_ALLOWED: + { + return "Arguments is not allowed to use here in strict mode."; + } + case PARSER_ERR_DELETE_IDENT_NOT_ALLOWED: + { + return "Deleting identifier is not allowed in strict mode."; + } + case PARSER_ERR_EVAL_CANNOT_ASSIGNED: + { + return "Eval cannot assigned in strict mode."; + } + case PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED: + { + return "Arguments cannot assigned in strict mode."; + } + case PARSER_ERR_WITH_NOT_ALLOWED: + { + return "With statement not allowed in strict mode."; + } + case PARSER_ERR_MULTIPLE_DEFAULTS_NOT_ALLOWED: + { + return "Multiple default cases not allowed."; + } + case PARSER_ERR_DEFAULT_NOT_IN_SWITCH: + { + return "Default statement must be in a switch block."; + } + case PARSER_ERR_CASE_NOT_IN_SWITCH: + { + return "Case statement must be in a switch block."; + } + case PARSER_ERR_LEFT_PAREN_EXPECTED: + { + return "Expected '(' token."; + } + case PARSER_ERR_LEFT_BRACE_EXPECTED: + { + return "Expected '{' token."; + } + case PARSER_ERR_RIGHT_PAREN_EXPECTED: + { + return "Expected ')' token."; + } + case PARSER_ERR_RIGHT_SQUARE_EXPECTED: + { + return "Expected ']' token."; + } + case PARSER_ERR_COLON_EXPECTED: + { + return "Expected ':' token."; + } + case PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED: + { + return "Expected ':' token for ?: conditional expression."; + } + case PARSER_ERR_SEMICOLON_EXPECTED: + { + return "Expected ';' token."; + } + case PARSER_ERR_IN_EXPECTED: + { + return "Expected 'in' token."; + } + case PARSER_ERR_WHILE_EXPECTED: + { + return "While expected for do-while loop."; + } + case PARSER_ERR_CATCH_FINALLY_EXPECTED: + { + return "Catch or finally block expected."; + } + case PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED: + { + return "Expected ',' or ']' after an array item."; + } + case PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED: + { + return "Expected ',' or '}' after a property definition."; + } + case PARSER_ERR_IDENTIFIER_EXPECTED: + { + return "Identifier expected."; + } + case PARSER_ERR_EXPRESSION_EXPECTED: + { + return "Expression expected."; + } + case PARSER_ERR_PRIMARY_EXP_EXPECTED: + { + return "Primary expression expected."; + } + case PARSER_ERR_STATEMENT_EXPECTED: + { + return "Statement expected."; + } + case PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED: + { + return "Property identifier expected."; + } + case PARSER_ERR_ARGUMENT_LIST_EXPECTED: + { + return "Expected argument list."; + } + case PARSER_ERR_NO_ARGUMENTS_EXPECTED: + { + return "Property getters must have no arguments."; + } + case PARSER_ERR_ONE_ARGUMENT_EXPECTED: + { + return "Property setters must have one argument."; + } + case PARSER_ERR_INVALID_EXPRESSION: + { + return "Invalid expression."; + } + case PARSER_ERR_INVALID_SWITCH: + { + return "Invalid switch body."; + } + case PARSER_ERR_INVALID_BREAK: + { + return "Break statement must be inside a loop or switch."; + } + case PARSER_ERR_INVALID_BREAK_LABEL: + { + return "Labelled statement targeted by a break not found."; + } + case PARSER_ERR_INVALID_CONTINUE: + { + return "Continue statement must be inside a loop."; + } + case PARSER_ERR_INVALID_CONTINUE_LABEL: + { + return "Labelled statement targeted by a continue noty found."; + } + case PARSER_ERR_INVALID_RETURN: + { + return "Return statement must be inside a function body."; + } + case PARSER_ERR_INVALID_RIGHT_SQUARE: + { + return "Unexpected '}' token."; + } + case PARSER_ERR_DUPLICATED_LABEL: + { + return "Duplicated label."; + } + case PARSER_ERR_OBJECT_PROPERTY_REDEFINED: + { + return "Property of object literal redefined."; + } + case PARSER_ERR_NON_STRICT_ARG_DEFINITION: + { + return "Non strict argument definition."; + } + default: + { + break; + } + } + + JERRY_ASSERT (error == PARSER_ERR_NO_ERROR); + return "No error."; +} /* parser_error_to_string */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-parser.cpp b/jerry-core/parser/js/js-parser.cpp new file mode 100644 index 0000000000..f4a75ea750 --- /dev/null +++ b/jerry-core/parser/js/js-parser.cpp @@ -0,0 +1,2258 @@ +/* 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 "ecma-helpers.h" +#include "jerry-snapshot.h" +#include "js-parser-internal.h" +#include "lit-literal.h" + +#ifdef PARSER_DUMP_BYTE_CODE +static int parser_show_instrs = PARSER_FALSE; +#endif /* PARSER_DUMP_BYTE_CODE */ + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_parser Parser + * @{ + */ + +/** + * Compute real literal indicies. + * + * @return length of the prefix opcodes + */ +static size_t +parser_compute_indicies (parser_context_t *context_p, /**< context */ + uint16_t *ident_end, /**< end of the identifier group */ + uint16_t *uninitialized_var_end, /**< end of the uninitialized var group */ + uint16_t *initialized_var_end, /**< end of the initialized var group */ + uint16_t *const_literal_end) /**< end of the const literal group */ +{ + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + size_t length = 0; + uint16_t literal_one_byte_limit; + uint32_t status_flags = context_p->status_flags; + uint16_t argument_count; + + uint16_t register_count = context_p->register_count; + uint16_t uninitialized_var_count = 0; + uint16_t initialized_var_count = 0; + uint16_t ident_count = 0; + uint16_t const_literal_count = 0; + + uint16_t register_index; + uint16_t uninitialized_var_index; + uint16_t initialized_var_index; + uint16_t ident_index; + uint16_t const_literal_index; + uint16_t literal_index; + + if (status_flags & PARSER_ARGUMENTS_NOT_NEEDED) + { + status_flags &= ~PARSER_ARGUMENTS_NEEDED; + context_p->status_flags = status_flags; + } + + /* First phase: count the number of items in each group. */ + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { +#ifndef PARSER_DUMP_BYTE_CODE + if (literal_p->type == LEXER_IDENT_LITERAL + || literal_p->type == LEXER_STRING_LITERAL) + { + const uint8_t *char_p = literal_p->u.char_p; + + if ((literal_p->status_flags & LEXER_FLAG_SOURCE_PTR) + && literal_p->prop.length < 0xfff) + { + size_t bytes_to_end = (size_t) (context_p->source_end_p - char_p); + + if (bytes_to_end < 0xfffff) + { + literal_p->u.source_data = ((uint32_t) bytes_to_end) | (((uint32_t) literal_p->prop.length) << 20); + literal_p->status_flags |= LEXER_FLAG_LATE_INIT; + status_flags |= PARSER_HAS_LATE_LIT_INIT; + context_p->status_flags = status_flags; + char_p = NULL; + } + } + + if (char_p != NULL) + { + lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (char_p, + literal_p->prop.length); + literal_p->u.value = rcs_cpointer_compress (lit); + + if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) + { + PARSER_FREE ((uint8_t *) char_p); + } + } + } +#endif /* !PARSER_DUMP_BYTE_CODE */ + + switch (literal_p->type) + { + case LEXER_IDENT_LITERAL: + { + if (literal_p->status_flags & LEXER_FLAG_VAR) + { + if (status_flags & PARSER_NO_REG_STORE) + { + literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + } + + if (literal_p->status_flags & LEXER_FLAG_INITIALIZED) + { + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME) + { + JERRY_ASSERT (literal_p == PARSER_GET_LITERAL (0)); + + status_flags |= PARSER_NAMED_FUNCTION_EXP | PARSER_NO_REG_STORE | PARSER_LEXICAL_ENV_NEEDED; + context_p->status_flags = status_flags; + + literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + context_p->literal_count++; + } + + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + if ((status_flags & PARSER_ARGUMENTS_NEEDED) + && !(status_flags & PARSER_IS_STRICT)) + { + literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + } + + /* Arguments are bound to their position, or + * moved to the initialized var section. */ + if (literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) + { + initialized_var_count++; + context_p->literal_count++; + } + } + else if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) + && register_count < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + register_count++; + } + else + { + literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + initialized_var_count++; + } + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + } + else if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) + && register_count < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + register_count++; + } + else + { + literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; + uninitialized_var_count++; + } + } + else + { + ident_count++; + } + break; + } + case LEXER_STRING_LITERAL: + case LEXER_NUMBER_LITERAL: + { + const_literal_count++; + break; + } + case LEXER_UNUSED_LITERAL: + { + if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) + { + context_p->literal_count--; + } + break; + } + } + } + + if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) + { + literal_one_byte_limit = CBC_MAXIMUM_BYTE_VALUE - 1; + } + else + { + literal_one_byte_limit = CBC_LOWER_SEVEN_BIT_MASK; + } + + if (uninitialized_var_count > 0) + { + /* Opcode byte and a literal argument. */ + length += 2; + if ((register_count + uninitialized_var_count - 1) > literal_one_byte_limit) + { + length++; + } + } + + register_index = context_p->register_count; + uninitialized_var_index = register_count; + initialized_var_index = (uint16_t) (uninitialized_var_index + uninitialized_var_count); + ident_index = (uint16_t) (initialized_var_index + initialized_var_count); + const_literal_index = (uint16_t) (ident_index + ident_count); + literal_index = (uint16_t) (const_literal_index + const_literal_count); + + if (initialized_var_count > 2) + { + status_flags |= PARSER_HAS_INITIALIZED_VARS; + context_p->status_flags = status_flags; + + /* Opcode byte and two literal arguments. */ + length += 3; + if (initialized_var_index > literal_one_byte_limit) + { + length++; + } + if (ident_index - 1 > literal_one_byte_limit) + { + length++; + } + } + + /* Second phase: Assign an index to each literal. */ + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + argument_count = 0; + + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + uint16_t init_index; + + if (literal_p->type != LEXER_IDENT_LITERAL) + { + if (literal_p->type == LEXER_STRING_LITERAL + || literal_p->type == LEXER_NUMBER_LITERAL) + { + JERRY_ASSERT ((literal_p->status_flags & ~(LEXER_FLAG_SOURCE_PTR | LEXER_FLAG_LATE_INIT)) == 0); + literal_p->prop.index = const_literal_index; + const_literal_index++; + continue; + } + + if (literal_p->type != LEXER_UNUSED_LITERAL) + { + JERRY_ASSERT (literal_p->status_flags == 0); + JERRY_ASSERT (literal_p->type == LEXER_FUNCTION_LITERAL + || literal_p->type == LEXER_REGEXP_LITERAL); + + literal_p->prop.index = literal_index; + literal_index++; + continue; + } + + JERRY_ASSERT ((literal_p->status_flags & ~(LEXER_FLAG_FUNCTION_ARGUMENT | LEXER_FLAG_SOURCE_PTR)) == 0); + + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + argument_count++; + } + continue; + } + + if (!(literal_p->status_flags & LEXER_FLAG_VAR)) + { + literal_p->prop.index = ident_index; + ident_index++; + continue; + } + + if (!(literal_p->status_flags & LEXER_FLAG_INITIALIZED)) + { + if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE)) + { + JERRY_ASSERT (register_count < PARSER_MAXIMUM_NUMBER_OF_REGISTERS); + /* This var literal can be stored in a register. */ + literal_p->prop.index = register_index; + register_index++; + } + else + { + literal_p->prop.index = uninitialized_var_index; + uninitialized_var_index++; + } + continue; + } + + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + if (literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) + { + literal_p->prop.index = initialized_var_index; + initialized_var_index++; + init_index = argument_count; + argument_count++; + } + else + { + literal_p->prop.index = argument_count; + argument_count++; + continue; + } + } + else + { + if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE)) + { + JERRY_ASSERT (register_count < PARSER_MAXIMUM_NUMBER_OF_REGISTERS); + /* This var literal can be stored in a register. */ + literal_p->prop.index = register_index; + register_index++; + } + else + { + literal_p->prop.index = initialized_var_index; + initialized_var_index++; + } + + init_index = literal_index; + literal_index++; + + if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME)) + { + literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); + + JERRY_ASSERT (literal_p != NULL + && literal_p->type == LEXER_FUNCTION_LITERAL); + literal_p->prop.index = init_index; + } + } + + /* A CBC_INITIALIZE_VAR instruction or part of a CBC_INITIALIZE_VARS instruction. */ + if (!(status_flags & PARSER_HAS_INITIALIZED_VARS)) + { + length += 2; + if (literal_p->prop.index > literal_one_byte_limit) + { + length++; + } + } + + length++; + if (init_index > literal_one_byte_limit) + { + length++; + } + } + + JERRY_ASSERT (argument_count == context_p->argument_count); + JERRY_ASSERT (register_index == register_count); + JERRY_ASSERT (uninitialized_var_index == register_count + uninitialized_var_count); + JERRY_ASSERT (initialized_var_index == uninitialized_var_index + initialized_var_count); + JERRY_ASSERT (ident_index == initialized_var_index + ident_count); + JERRY_ASSERT (const_literal_index == ident_index + const_literal_count); + JERRY_ASSERT (literal_index == context_p->literal_count); + + *ident_end = ident_index; + *uninitialized_var_end = uninitialized_var_index; + *initialized_var_end = initialized_var_index; + *const_literal_end = const_literal_index; + context_p->register_count = register_index; + + return length; +} /* parser_compute_indicies */ + +/** + * Encode a literal argument. + * + * @return position after the encoded values + */ +static PARSER_INLINE uint8_t * +parser_encode_literal (uint8_t *dst_p, /**< destination buffer */ + uint16_t literal_index, /**< literal index */ + uint16_t literal_one_byte_limit) /**< maximum value of a literal + * encoded in one byte */ +{ + if (literal_index <= literal_one_byte_limit) + { + *dst_p++ = (uint8_t) (literal_index); + } + else + { + if (literal_one_byte_limit == CBC_MAXIMUM_BYTE_VALUE - 1) + { + *dst_p++ = (uint8_t) (CBC_MAXIMUM_BYTE_VALUE); + *dst_p++ = (uint8_t) (literal_index - CBC_MAXIMUM_BYTE_VALUE); + } + else + { + *dst_p++ = (uint8_t) (literal_index >> 8) | CBC_HIGHEST_BIT_MASK; + *dst_p++ = (uint8_t) (literal_index & CBC_MAXIMUM_BYTE_VALUE); + } + } + return dst_p; +} /* parser_encode_literal */ + +/** + * Generate initializer byte codes. + * + * @return the end of the initializer stream + */ +static uint8_t * +parser_generate_initializers (parser_context_t *context_p, /**< context */ + uint8_t *dst_p, /**< destination buffer */ + lit_cpointer_t *literal_pool_p, /**< start of literal pool */ + uint16_t uninitialized_var_end, /**< end of the uninitialized var group */ + uint16_t initialized_var_end, /**< end of the initialized var group */ + uint16_t const_literal_end, /**< end of the const literal group */ + uint16_t literal_one_byte_limit) /**< maximum value of a literal + * encoded in one byte */ +{ + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + uint16_t argument_count; + + if (uninitialized_var_end > context_p->register_count) + { + *dst_p++ = CBC_DEFINE_VARS; + dst_p = parser_encode_literal (dst_p, + (uint16_t) (uninitialized_var_end - 1), + literal_one_byte_limit); + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + } + + if (context_p->status_flags & PARSER_HAS_INITIALIZED_VARS) + { + const uint8_t expected_status_flags = LEXER_FLAG_VAR | LEXER_FLAG_NO_REG_STORE | LEXER_FLAG_INITIALIZED; +#ifdef PARSER_DEBUG + uint16_t next_index = uninitialized_var_end; +#endif + + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + + *dst_p++ = CBC_INITIALIZE_VARS; + dst_p = parser_encode_literal (dst_p, + (uint16_t) uninitialized_var_end, + literal_one_byte_limit); + dst_p = parser_encode_literal (dst_p, + (uint16_t) (initialized_var_end - 1), + literal_one_byte_limit); + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + argument_count = 0; + + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + argument_count++; + } + + if ((literal_p->status_flags & expected_status_flags) == expected_status_flags) + { + uint16_t init_index; + + JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); +#ifdef PARSER_DEBUG + JERRY_ASSERT (literal_p->prop.index == next_index); + next_index++; +#endif + literal_p->status_flags = (uint8_t) (literal_p->status_flags & ~LEXER_FLAG_INITIALIZED); + + + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME) + { + init_index = const_literal_end; + } + else if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + init_index = (uint16_t) (argument_count - 1); + } + else + { + literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); + + JERRY_ASSERT (literal_p != NULL + && literal_p->type == LEXER_FUNCTION_LITERAL); + init_index = literal_p->prop.index; + } + + dst_p = parser_encode_literal (dst_p, init_index, literal_one_byte_limit); + } + } + + JERRY_ASSERT (argument_count == context_p->argument_count); + } + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + argument_count = 0; + + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + const uint8_t expected_status_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED; + + if (literal_p->type != LEXER_UNUSED_LITERAL) + { + if (literal_p->type == LEXER_IDENT_LITERAL + || literal_p->type == LEXER_STRING_LITERAL) + { +#ifdef PARSER_DUMP_BYTE_CODE + lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (literal_p->u.char_p, + literal_p->prop.length); + literal_pool_p[literal_p->prop.index] = rcs_cpointer_compress (lit); + + if (!context_p->is_show_opcodes + && !(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) + { + PARSER_FREE (literal_p->u.char_p); + } +#else /* PARSER_DUMP_BYTE_CODE */ + literal_pool_p[literal_p->prop.index] = literal_p->u.value; +#endif /* PARSER_DUMP_BYTE_CODE */ + } + else if ((literal_p->type == LEXER_FUNCTION_LITERAL) + || (literal_p->type == LEXER_REGEXP_LITERAL)) + { + ECMA_SET_NON_NULL_POINTER (literal_pool_p[literal_p->prop.index].value.base_cp, + literal_p->u.bytecode_p); + } + else + { + JERRY_ASSERT (literal_p->type == LEXER_NUMBER_LITERAL); + literal_pool_p[literal_p->prop.index] = literal_p->u.value; + } + } + + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + argument_count++; + } + + if ((literal_p->status_flags & expected_status_flags) == expected_status_flags) + { + uint16_t index = literal_p->prop.index; + uint16_t init_index; + + JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); + + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + + if (literal_p->status_flags & LEXER_FLAG_FUNCTION_NAME) + { + init_index = const_literal_end; + } + else if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + { + init_index = (uint16_t) (argument_count - 1); + + if (init_index == literal_p->prop.index) + { + continue; + } + } + else + { + literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); + + JERRY_ASSERT (literal_p != NULL + && literal_p->type == LEXER_FUNCTION_LITERAL); + init_index = literal_p->prop.index; + ECMA_SET_NON_NULL_POINTER (literal_pool_p[literal_p->prop.index].value.base_cp, + literal_p->u.bytecode_p); + } + + *dst_p++ = CBC_INITIALIZE_VAR; + dst_p = parser_encode_literal (dst_p, index, literal_one_byte_limit); + dst_p = parser_encode_literal (dst_p, init_index, literal_one_byte_limit); + } + } + + JERRY_ASSERT (argument_count == context_p->argument_count); + return dst_p; +} /* parser_generate_initializers */ + +/* + * During byte code post processing certain bytes are not + * copied into the final byte code buffer. For example, if + * one byte is enough for encoding a literal index, the + * second byte is not copied. However, when a byte is skipped, + * the offsets of those branches which crosses (jumps over) + * that byte code should also be decreased by one. Instead + * of finding these jumps every time when a byte is skipped, + * all branch offset updates are computed in one step. + * + * Branch offset mapping example: + * + * Let's assume that each parser_mem_page of the byte_code + * buffer is 8 bytes long and only 4 bytes are kept for a + * given page: + * + * +---+---+---+---+---+---+---+---+ + * | X | 1 | 2 | 3 | X | 4 | X | X | + * +---+---+---+---+---+---+---+---+ + * + * X marks those bytes which are removed. The resulting + * offset mapping is the following: + * + * +---+---+---+---+---+---+---+---+ + * | 0 | 1 | 2 | 3 | 3 | 4 | 4 | 4 | + * +---+---+---+---+---+---+---+---+ + * + * Each X is simply replaced by the index of the previous + * index starting from zero. This shows the number of + * copied bytes before a given byte including the byte + * itself. The last byte always shows the number of bytes + * copied from this page. + * + * This mapping allows recomputing all branch targets, + * since mapping[to] - mapping[from] is the new argument + * for forward branches. As for backward branches, the + * equation is reversed to mapping[from] - mapping[to]. + * + * The mapping is relative to one page, so distance + * computation affecting multiple pages requires a loop. + * We should also note that only argument bytes can + * be skipped, so removed bytes cannot be targeted by + * branches. Valid branches always target instruction + * starts only. + */ + +/** + * Recompute the argument of a forward branch. + * + * @return the new distance + */ +static size_t +parser_update_forward_branch (parser_mem_page_t *page_p, /**< current page */ + size_t full_distance, /**< full distance */ + uint8_t bytes_copied_before_jump) /**< bytes copied before jump */ +{ + size_t new_distance = 0; + + while (full_distance > PARSER_CBC_STREAM_PAGE_SIZE) + { + new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; + full_distance -= PARSER_CBC_STREAM_PAGE_SIZE; + page_p = page_p->next_p; + } + + new_distance += page_p->bytes[full_distance - 1] & CBC_LOWER_SEVEN_BIT_MASK; + return new_distance - bytes_copied_before_jump; +} /* parser_update_forward_branch */ + +/** + * Recompute the argument of a backward branch. + * + * @return the new distance + */ +static size_t +parser_update_backward_branch (parser_mem_page_t *page_p, /**< current page */ + size_t full_distance, /**< full distance */ + uint8_t bytes_copied_before_jump) /**< bytes copied before jump */ +{ + size_t new_distance = bytes_copied_before_jump; + + while (full_distance >= PARSER_CBC_STREAM_PAGE_SIZE) + { + JERRY_ASSERT (page_p != NULL); + new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; + full_distance -= PARSER_CBC_STREAM_PAGE_SIZE; + page_p = page_p->next_p; + } + + if (full_distance > 0) + { + size_t offset = PARSER_CBC_STREAM_PAGE_SIZE - full_distance; + + JERRY_ASSERT (page_p != NULL); + + new_distance += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; + new_distance -= page_p->bytes[offset - 1] & CBC_LOWER_SEVEN_BIT_MASK; + } + + return new_distance; +} /* parser_update_backward_branch */ + +/** + * Update targets of all branches in one step. + */ +static void +parse_update_branches (parser_context_t *context_p, /**< context */ + uint8_t *byte_code_p) /**< byte code */ +{ + parser_mem_page_t *page_p = context_p->byte_code.first_p; + parser_mem_page_t *prev_page_p = NULL; + parser_mem_page_t *last_page_p = context_p->byte_code.last_p; + size_t last_position = context_p->byte_code.last_position; + size_t offset = 0; + size_t bytes_copied = 0; + + if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) + { + last_page_p = NULL; + last_position = 0; + } + + while (page_p != last_page_p || offset < last_position) + { + /* Branch instructions are marked to improve search speed. */ + if (page_p->bytes[offset] & CBC_HIGHEST_BIT_MASK) + { + uint8_t *bytes_p = byte_code_p + bytes_copied; + uint8_t flags; + uint8_t bytes_copied_before_jump = 0; + size_t branch_argument_length; + size_t target_distance; + size_t length; + + if (offset > 0) + { + bytes_copied_before_jump = page_p->bytes[offset - 1] & CBC_LOWER_SEVEN_BIT_MASK; + } + bytes_p += bytes_copied_before_jump; + + if (*bytes_p == CBC_EXT_OPCODE) + { + bytes_p++; + flags = cbc_ext_flags[*bytes_p]; + } + else + { + flags = cbc_flags[*bytes_p]; + } + + JERRY_ASSERT (flags & CBC_HAS_BRANCH_ARG); + branch_argument_length = CBC_BRANCH_OFFSET_LENGTH (*bytes_p); + bytes_p++; + + /* Decoding target. */ + length = branch_argument_length; + target_distance = 0; + do + { + target_distance = (target_distance << 8) | *bytes_p; + bytes_p++; + } + while (--length > 0); + + if (CBC_BRANCH_IS_FORWARD (flags)) + { + /* Branch target was not set. */ + JERRY_ASSERT (target_distance > 0); + + target_distance = parser_update_forward_branch (page_p, + offset + target_distance, + bytes_copied_before_jump); + } + else + { + if (target_distance < offset) + { + uint8_t bytes_copied_before_target = page_p->bytes[offset - target_distance - 1]; + bytes_copied_before_target = bytes_copied_before_target & CBC_LOWER_SEVEN_BIT_MASK; + + target_distance = (size_t) (bytes_copied_before_jump - bytes_copied_before_target); + } + else if (target_distance == offset) + { + target_distance = bytes_copied_before_jump; + } + else + { + target_distance = parser_update_backward_branch (prev_page_p, + target_distance - offset, + bytes_copied_before_jump); + } + } + + /* Encoding target again. */ + do + { + bytes_p--; + *bytes_p = (uint8_t) (target_distance & 0xff); + target_distance >>= 8; + } + while (--branch_argument_length > 0); + } + + offset++; + if (offset >= PARSER_CBC_STREAM_PAGE_SIZE) + { + parser_mem_page_t *next_p = page_p->next_p; + + /* We reverse the pages before the current page. */ + page_p->next_p = prev_page_p; + prev_page_p = page_p; + + bytes_copied += page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1] & CBC_LOWER_SEVEN_BIT_MASK; + page_p = next_p; + offset = 0; + } + } + + /* After this point the pages of the byte code stream are + * not used anymore. However, they needs to be freed during + * cleanup, so the first and last pointers of the stream + * descriptor are reversed as well. */ + if (last_page_p != NULL) + { + JERRY_ASSERT (last_page_p == context_p->byte_code.last_p); + last_page_p->next_p = prev_page_p; + } + else + { + last_page_p = context_p->byte_code.last_p; + } + + context_p->byte_code.last_p = context_p->byte_code.first_p; + context_p->byte_code.first_p = last_page_p; +} /* parse_update_branches */ + +#ifdef PARSER_DUMP_BYTE_CODE + +/** + * Print literal. + */ +static void +parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ + uint16_t literal_index, /**< literal index */ + parser_list_t *literal_pool_p) /**< literal pool */ +{ + parser_list_iterator_t literal_iterator; + uint16_t argument_end; + uint16_t register_end; + uint16_t ident_end; + uint16_t const_literal_end; + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; + argument_end = args_p->argument_end; + 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 *) compiled_code_p; + argument_end = args_p->argument_end; + register_end = args_p->register_end; + ident_end = args_p->ident_end; + const_literal_end = args_p->const_literal_end; + } + + parser_list_iterator_init (literal_pool_p, &literal_iterator); + + while (PARSER_TRUE) + { + lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); + + if (literal_p == NULL) + { + + if (literal_index == const_literal_end) + { + printf (" idx:%d(self)->function", literal_index); + break; + } + + JERRY_ASSERT (literal_index < argument_end); + printf (" idx:%d(arg)->undefined", literal_index); + break; + } + + if (literal_p->prop.index == literal_index + && literal_p->type != LEXER_UNUSED_LITERAL) + { + printf (" idx:%d", literal_index); + + if (literal_index < argument_end) + { + printf ("(arg)->"); + } + else if (literal_index < register_end) + { + printf ("(reg)->"); + } + else if (literal_index < ident_end) + { + printf ("(ident)->"); + } + else + { + printf ("(lit)->"); + } + + util_print_literal (literal_p); + return; + } + } +} /* parse_print_literal */ + +#define PARSER_READ_IDENTIFIER_INDEX(name) \ + name = *byte_code_p++; \ + if (name >= encoding_limit) \ + { \ + name = (uint16_t) (((name << 8) | byte_code_p[0]) - encoding_delta); \ + byte_code_p++; \ + } + +/** + * Print CBC_DEFINE_VARS instruction. + * + * @return next byte code position + */ +static uint8_t * +parse_print_define_vars (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ + uint8_t *byte_code_p, /**< byte code position */ + uint16_t encoding_limit, /**< literal encoding limit */ + uint16_t encoding_delta, /**< literal encoding delta */ + parser_list_t *literal_pool_p) /**< literal pool */ +{ + uint16_t identifier_index; + uint16_t identifier_end; + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; + identifier_index = args_p->register_end; + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; + identifier_index = args_p->register_end; + } + + PARSER_READ_IDENTIFIER_INDEX (identifier_end); + + printf (" from: %d to: %d\n", identifier_index, identifier_end); + + while (identifier_index <= identifier_end) + { + printf (" "); + parse_print_literal (compiled_code_p, identifier_index, literal_pool_p); + identifier_index++; + printf ("\n"); + } + + return byte_code_p; +} /* parse_print_define_vars */ + +/** + * Print CBC_INITIALIZE_VARS instruction. + * + * @return next byte code position + */ +static uint8_t * +parse_print_initialize_vars (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ + uint8_t *byte_code_p, /**< byte code position */ + uint16_t encoding_limit, /**< literal encoding limit */ + uint16_t encoding_delta, /**< literal encoding delta */ + parser_list_t *literal_pool_p) /**< literal pool */ +{ + uint16_t identifier_index; + uint16_t identifier_end; + + PARSER_READ_IDENTIFIER_INDEX (identifier_index); + PARSER_READ_IDENTIFIER_INDEX (identifier_end); + + printf (" from: %d to: %d\n", identifier_index, identifier_end); + + while (identifier_index <= identifier_end) + { + uint16_t literal_index; + + printf (" "); + parse_print_literal (compiled_code_p, identifier_index, literal_pool_p); + printf (" ="); + + PARSER_READ_IDENTIFIER_INDEX (literal_index); + + parse_print_literal (compiled_code_p, literal_index, literal_pool_p); + identifier_index++; + printf ("\n"); + } + + return byte_code_p; +} /* parse_print_initialize_vars */ + +/** + * Print byte code. + */ +static void +parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ + parser_list_t *literal_pool_p, /**< literal pool */ + size_t length) /**< length of byte code */ +{ + uint8_t flags; + uint8_t *byte_code_start_p; + uint8_t *byte_code_end_p; + uint8_t *byte_code_p; + uint16_t encoding_limit; + uint16_t encoding_delta; + uint16_t stack_limit; + uint16_t argument_end; + uint16_t register_end; + uint16_t ident_end; + uint16_t const_literal_end; + uint16_t literal_end; + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args = (cbc_uint16_arguments_t *) compiled_code_p; + stack_limit = args->stack_limit; + argument_end = args->argument_end; + register_end = args->register_end; + ident_end = args->ident_end; + const_literal_end = args->const_literal_end; + literal_end = args->literal_end; + } + else + { + cbc_uint8_arguments_t *args = (cbc_uint8_arguments_t *) compiled_code_p; + stack_limit = args->stack_limit; + argument_end = args->argument_end; + register_end = args->register_end; + ident_end = args->ident_end; + const_literal_end = args->const_literal_end; + literal_end = args->literal_end; + } + + printf ("\nFinal byte code dump:\n\n Maximum stack depth: %d\n Flags: [", (int) stack_limit); + + if (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_FULL_LITERAL_ENCODING)) + { + printf ("small_lit_enc"); + encoding_limit = 255; + encoding_delta = 0xfe01; + } + else + { + printf ("full_lit_enc"); + encoding_limit = 128; + encoding_delta = 0x8000; + } + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + printf (",uint16_arguments"); + } + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) + { + printf (",strict_mode"); + } + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_ARGUMENTS_NEEDED) + { + printf (",arguments_needed"); + } + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) + { + printf (",no_lexical_env"); + } + + printf ("]\n"); + + printf (" Argument range end: %d\n", (int) argument_end); + printf (" Register range end: %d\n", (int) register_end); + printf (" Identifier range end: %d\n", (int) ident_end); + printf (" Const literal range end: %d\n", (int) const_literal_end); + printf (" Literal range end: %d\n\n", (int) literal_end); + + byte_code_start_p = (uint8_t *) compiled_code_p; + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + byte_code_start_p += sizeof (cbc_uint16_arguments_t); + } + else + { + byte_code_start_p += sizeof (cbc_uint8_arguments_t); + } + + byte_code_start_p += literal_end * sizeof (lit_cpointer_t); + byte_code_end_p = byte_code_start_p + length; + byte_code_p = byte_code_start_p; + + while (byte_code_p < byte_code_end_p) + { + cbc_opcode_t opcode; + cbc_ext_opcode_t ext_opcode; + size_t cbc_offset; + + opcode = (cbc_opcode_t) *byte_code_p; + ext_opcode = CBC_EXT_NOP; + cbc_offset = (size_t) (byte_code_p - byte_code_start_p); + + if (opcode != CBC_EXT_OPCODE) + { + flags = cbc_flags[opcode]; + printf (" %3d : %s", (int) cbc_offset, cbc_names[opcode]); + byte_code_p++; + + if (opcode == CBC_INITIALIZE_VARS) + { + byte_code_p = parse_print_initialize_vars (compiled_code_p, + byte_code_p, + encoding_limit, + encoding_delta, + literal_pool_p); + continue; + } + + if (opcode == CBC_DEFINE_VARS) + { + byte_code_p = parse_print_define_vars (compiled_code_p, + byte_code_p, + encoding_limit, + encoding_delta, + literal_pool_p); + continue; + } + + if (opcode == CBC_PUSH_NUMBER_1) + { + int value = *byte_code_p++; + + if (value >= CBC_PUSH_NUMBER_1_RANGE_END) + { + value = -(value - CBC_PUSH_NUMBER_1_RANGE_END); + } + + printf (" number:%d\n", value); + continue; + } + } + else + { + ext_opcode = (cbc_ext_opcode_t) byte_code_p[1]; + flags = cbc_ext_flags[ext_opcode]; + printf (" %3d : %s", (int) cbc_offset, cbc_ext_names[ext_opcode]); + byte_code_p += 2; + } + + if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) + { + uint16_t literal_index; + + PARSER_READ_IDENTIFIER_INDEX (literal_index); + parse_print_literal (compiled_code_p, literal_index, literal_pool_p); + } + + if (flags & CBC_HAS_LITERAL_ARG2) + { + uint16_t literal_index; + + PARSER_READ_IDENTIFIER_INDEX (literal_index); + parse_print_literal (compiled_code_p, literal_index, literal_pool_p); + + if (!(flags & CBC_HAS_LITERAL_ARG)) + { + PARSER_READ_IDENTIFIER_INDEX (literal_index); + parse_print_literal (compiled_code_p, literal_index, literal_pool_p); + } + } + + if (flags & CBC_HAS_BYTE_ARG) + { + printf (" byte_arg:%d", *byte_code_p); + byte_code_p++; + } + + if (flags & CBC_HAS_BRANCH_ARG) + { + size_t branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (opcode); + size_t offset = 0; + + if (opcode == CBC_EXT_OPCODE) + { + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode); + } + + do + { + offset = (offset << 8) | *byte_code_p++; + } + while (--branch_offset_length > 0); + + if (CBC_BRANCH_IS_FORWARD (flags)) + { + printf (" offset:%d(->%d)", (int) offset, (int) (cbc_offset + offset)); + } + else + { + printf (" offset:%d(->%d)", (int) offset, (int) (cbc_offset - offset)); + } + } + printf ("\n"); + } +} /* parse_print_final_cbc */ + +#undef PARSER_READ_IDENTIFIER_INDEX + +#endif /* PARSER_DUMP_BYTE_CODE */ + +#define PARSER_NEXT_BYTE(page_p, offset) \ + do { \ + if (++(offset) >= PARSER_CBC_STREAM_PAGE_SIZE) \ + { \ + offset = 0; \ + page_p = page_p->next_p; \ + } \ + } while (0) + +#define PARSER_NEXT_BYTE_UPDATE(page_p, offset, real_offset) \ + do { \ + page_p->bytes[offset] = real_offset; \ + if (++(offset) >= PARSER_CBC_STREAM_PAGE_SIZE) \ + { \ + offset = 0; \ + real_offset = 0; \ + page_p = page_p->next_p; \ + } \ + } while (0) + +/** + * Post processing main function. + * + * @return compiled code + */ +static ecma_compiled_code_t * +parser_post_processing (parser_context_t *context_p) /**< context */ +{ + uint16_t literal_one_byte_limit; + uint16_t ident_end; + uint16_t uninitialized_var_end; + uint16_t initialized_var_end; + uint16_t const_literal_end; + parser_mem_page_t *page_p; + parser_mem_page_t *last_page_p = context_p->byte_code.last_p; + size_t last_position = context_p->byte_code.last_position; + size_t offset; + size_t length; + size_t total_size; + size_t initializers_length; + uint8_t real_offset; + uint8_t *byte_code_p; + int needs_uint16_arguments; + cbc_opcode_t last_opcode = CBC_EXT_OPCODE; + ecma_compiled_code_t *compiled_code_p; + lit_cpointer_t *literal_pool_p; + uint8_t *dst_p; + + if ((size_t) context_p->stack_limit + (size_t) context_p->register_count > PARSER_MAXIMUM_STACK_LIMIT) + { + parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); + } + + JERRY_ASSERT (context_p->literal_count <= PARSER_MAXIMUM_NUMBER_OF_LITERALS); + + initializers_length = parser_compute_indicies (context_p, + &ident_end, + &uninitialized_var_end, + &initialized_var_end, + &const_literal_end); + length = initializers_length; + + if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) + { + literal_one_byte_limit = CBC_MAXIMUM_BYTE_VALUE - 1; + } + else + { + literal_one_byte_limit = CBC_LOWER_SEVEN_BIT_MASK; + } + + if (last_position >= PARSER_CBC_STREAM_PAGE_SIZE) + { + last_page_p = NULL; + last_position = 0; + } + + page_p = context_p->byte_code.first_p; + offset = 0; + + while (page_p != last_page_p || offset < last_position) + { + uint8_t *opcode_p; + uint8_t flags; + size_t branch_offset_length; + + opcode_p = page_p->bytes + offset; + last_opcode = (cbc_opcode_t) (*opcode_p); + PARSER_NEXT_BYTE (page_p, offset); + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (last_opcode); + flags = cbc_flags[last_opcode]; + length++; + + if (last_opcode == CBC_EXT_OPCODE) + { + cbc_ext_opcode_t ext_opcode; + + ext_opcode = (cbc_ext_opcode_t) page_p->bytes[offset]; + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode); + flags = cbc_ext_flags[ext_opcode]; + PARSER_NEXT_BYTE (page_p, offset); + length++; + } + + while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) + { + uint8_t *first_byte = page_p->bytes + offset; + size_t literal_index = *first_byte; + lexer_literal_t *literal_p; + + PARSER_NEXT_BYTE (page_p, offset); + length++; + + literal_index |= ((size_t) page_p->bytes[offset]) << 8; + literal_p = PARSER_GET_LITERAL (literal_index); + + if (literal_p->type == LEXER_UNUSED_LITERAL) + { + /* In a few cases uninitialized literals may have been converted to initialized + * literals later. Byte code references to the old (uninitialized) literals + * must be redirected to the new instance of the literal. */ + literal_p = PARSER_GET_LITERAL (literal_p->prop.index); + + JERRY_ASSERT (literal_p != NULL && literal_p->type != LEXER_UNUSED_LITERAL); + } + + if (literal_p->prop.index <= literal_one_byte_limit) + { + *first_byte = (uint8_t) literal_p->prop.index; + } + else + { + if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) + { + JERRY_ASSERT (literal_index <= CBC_MAXIMUM_SMALL_VALUE); + *first_byte = CBC_MAXIMUM_BYTE_VALUE; + page_p->bytes[offset] = (uint8_t) (literal_p->prop.index - CBC_MAXIMUM_BYTE_VALUE); + length++; + } + else + { + JERRY_ASSERT (literal_index <= CBC_MAXIMUM_FULL_VALUE); + *first_byte = (uint8_t) (literal_p->prop.index >> 8) | CBC_HIGHEST_BIT_MASK; + page_p->bytes[offset] = (uint8_t) (literal_p->prop.index & 0xff); + length++; + } + } + PARSER_NEXT_BYTE (page_p, offset); + + if (flags & CBC_HAS_LITERAL_ARG2) + { + if (flags & CBC_HAS_LITERAL_ARG) + { + flags = CBC_HAS_LITERAL_ARG; + } + else + { + flags = CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2; + } + } + else + { + break; + } + } + + if (flags & CBC_HAS_BYTE_ARG) + { + /* This argument will be copied without modification. */ + PARSER_NEXT_BYTE (page_p, offset); + length++; + } + + if (flags & CBC_HAS_BRANCH_ARG) + { + int prefix_zero = PARSER_TRUE; +#if PARSER_MAXIMUM_CODE_SIZE <= 65535 + cbc_opcode_t jump_forward = CBC_JUMP_FORWARD_2; +#else /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + cbc_opcode_t jump_forward = CBC_JUMP_FORWARD_3; +#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + + /* The leading zeroes are dropped from the stream. + * Although dropping these zeroes for backward + * branches are unnecessary, we use the same + * code path for simplicity. */ + JERRY_ASSERT (branch_offset_length > 0 && branch_offset_length <= 3); + + while (--branch_offset_length > 0) + { + uint8_t byte = page_p->bytes[offset]; + if (byte > 0 || !prefix_zero) + { + prefix_zero = PARSER_FALSE; + length++; + } + else + { + JERRY_ASSERT (CBC_BRANCH_IS_FORWARD (flags)); + } + PARSER_NEXT_BYTE (page_p, offset); + } + + if (last_opcode == jump_forward + && prefix_zero + && page_p->bytes[offset] == CBC_BRANCH_OFFSET_LENGTH (jump_forward) + 1) + { + /* Uncoditional jumps which jump right after the instruction + * are effectively NOPs. These jumps are removed from the + * stream. The 1 byte long CBC_JUMP_FORWARD form marks these + * instructions, since this form is constructed during post + * processing and cannot be emitted directly. */ + *opcode_p = CBC_JUMP_FORWARD; + length --; + } + else + { + /* Other last bytes are always copied. */ + length++; + } + + PARSER_NEXT_BYTE (page_p, offset); + } + } + + if (!(context_p->status_flags & PARSER_NO_END_LABEL) + || !(PARSER_OPCODE_IS_RETURN (last_opcode))) + { + context_p->status_flags &= ~PARSER_NO_END_LABEL; + length++; + } + + needs_uint16_arguments = PARSER_FALSE; + total_size = sizeof (cbc_uint8_arguments_t); + + if ((context_p->register_count + context_p->stack_limit) > CBC_MAXIMUM_BYTE_VALUE + || context_p->literal_count > CBC_MAXIMUM_BYTE_VALUE) + { + needs_uint16_arguments = PARSER_TRUE; + total_size = sizeof (cbc_uint16_arguments_t); + } + + total_size += length + context_p->literal_count * sizeof (lit_cpointer_t); + compiled_code_p = (ecma_compiled_code_t *) parser_malloc (context_p, total_size); + + byte_code_p = (uint8_t *) compiled_code_p; + compiled_code_p->status_flags = CBC_CODE_FLAGS_FUNCTION | (1 << ECMA_BYTECODE_REF_SHIFT); + + if (needs_uint16_arguments) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; + + args_p->stack_limit = (uint16_t) (context_p->register_count + context_p->stack_limit); + args_p->argument_end = context_p->argument_count; + args_p->register_end = context_p->register_count; + args_p->ident_end = ident_end; + args_p->const_literal_end = const_literal_end; + args_p->literal_end = context_p->literal_count; + + compiled_code_p->status_flags |= CBC_CODE_FLAGS_UINT16_ARGUMENTS; + byte_code_p += sizeof (cbc_uint16_arguments_t); + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; + + args_p->stack_limit = (uint8_t) (context_p->register_count + context_p->stack_limit); + args_p->argument_end = (uint8_t) context_p->argument_count; + args_p->register_end = (uint8_t) context_p->register_count; + args_p->ident_end = (uint8_t) ident_end; + args_p->const_literal_end = (uint8_t) const_literal_end; + args_p->literal_end = (uint8_t) context_p->literal_count; + + byte_code_p += sizeof (cbc_uint8_arguments_t); + } + + if (context_p->literal_count > CBC_MAXIMUM_SMALL_VALUE) + { + compiled_code_p->status_flags |= CBC_CODE_FLAGS_FULL_LITERAL_ENCODING; + } + + if (context_p->status_flags & PARSER_IS_STRICT) + { + compiled_code_p->status_flags |= CBC_CODE_FLAGS_STRICT_MODE; + } + + if (context_p->status_flags & PARSER_ARGUMENTS_NEEDED) + { + compiled_code_p->status_flags |= CBC_CODE_FLAGS_ARGUMENTS_NEEDED; + /* Arguments is stored in the lexical environment. */ + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + } + + if (!(context_p->status_flags & PARSER_LEXICAL_ENV_NEEDED)) + { + compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED; + } + + literal_pool_p = (lit_cpointer_t *) byte_code_p; + byte_code_p += context_p->literal_count * sizeof (lit_cpointer_t); + +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + + if (snapshot_report_byte_code_compilation + && context_p->argument_count > 0) + { + /* Reset all arguments to NULL. */ + for (offset = 0; offset < context_p->argument_count; offset++) + { + literal_pool_p[offset] = NOT_A_LITERAL; + } + } + +#endif + + dst_p = parser_generate_initializers (context_p, + byte_code_p, + literal_pool_p, + uninitialized_var_end, + initialized_var_end, + const_literal_end, + literal_one_byte_limit); + + JERRY_ASSERT (dst_p == byte_code_p + initializers_length); + + page_p = context_p->byte_code.first_p; + offset = 0; + real_offset = 0; + + while (page_p != last_page_p || offset < last_position) + { + uint8_t flags; + uint8_t *opcode_p; + uint8_t *branch_mark_p; + cbc_opcode_t opcode; + size_t branch_offset_length; + + opcode_p = dst_p; + branch_mark_p = page_p->bytes + offset; + opcode = (cbc_opcode_t) (*branch_mark_p); + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (opcode); + + if (opcode == CBC_JUMP_FORWARD) + { + /* These opcodes are deleted from the stream. */ +#if PARSER_MAXIMUM_CODE_SIZE <= 65535 + size_t length = 3; +#else /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + size_t length = 4; +#endif /* PARSER_MAXIMUM_CODE_SIZE <= 65535 */ + + do + { + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + } + while (--length > 0); + + continue; + } + + /* Storing the opcode */ + *dst_p++ = opcode; + real_offset++; + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + flags = cbc_flags[opcode]; + + if (opcode == CBC_EXT_OPCODE) + { + cbc_ext_opcode_t ext_opcode; + + ext_opcode = (cbc_ext_opcode_t) page_p->bytes[offset]; + flags = cbc_ext_flags[ext_opcode]; + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (ext_opcode); + + /* Storing the extended opcode */ + *dst_p++ = ext_opcode; + opcode_p++; + real_offset++; + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + } + + if (flags & CBC_HAS_BRANCH_ARG) + { + *branch_mark_p |= CBC_HIGHEST_BIT_MASK; + } + + /* Only literal and call arguments can be combined. */ + JERRY_ASSERT (!(flags & CBC_HAS_BRANCH_ARG) + || !(flags & (CBC_HAS_BYTE_ARG | CBC_HAS_LITERAL_ARG))); + + while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) + { + uint8_t first_byte = page_p->bytes[offset]; + + *dst_p++ = first_byte; + real_offset++; + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + + if (first_byte > literal_one_byte_limit) + { + *dst_p++ = page_p->bytes[offset]; + real_offset++; + } + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + + if (flags & CBC_HAS_LITERAL_ARG2) + { + if (flags & CBC_HAS_LITERAL_ARG) + { + flags = CBC_HAS_LITERAL_ARG; + } + else + { + flags = CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2; + } + } + else + { + break; + } + } + + if (flags & CBC_HAS_BYTE_ARG) + { + /* This argument will be copied without modification. */ + *dst_p++ = page_p->bytes[offset]; + real_offset++; + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + } + + if (flags & CBC_HAS_BRANCH_ARG) + { + int prefix_zero = PARSER_TRUE; + + /* The leading zeroes are dropped from the stream. */ + JERRY_ASSERT (branch_offset_length > 0 && branch_offset_length <= 3); + + while (--branch_offset_length > 0) + { + uint8_t byte = page_p->bytes[offset]; + if (byte > 0 || !prefix_zero) + { + prefix_zero = PARSER_FALSE; + *dst_p++ = page_p->bytes[offset]; + real_offset++; + } + else + { + /* When a leading zero is dropped, the branch + * offset length must be decreased as well. */ + (*opcode_p)--; + } + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + } + + *dst_p++ = page_p->bytes[offset]; + real_offset++; + PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset); + } + } + + if (!(context_p->status_flags & PARSER_NO_END_LABEL)) + { + *dst_p++ = CBC_RETURN_WITH_BLOCK; + } + JERRY_ASSERT (dst_p == byte_code_p + length); + + parse_update_branches (context_p, + byte_code_p + initializers_length); + + parser_cbc_stream_free (&context_p->byte_code); + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + + parse_print_final_cbc (compiled_code_p, &context_p->literal_pool, length); + printf ("\nByte code size: %d bytes\n", (int) length); + context_p->total_byte_code_size += (uint32_t) length; + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + if ((literal_p->type == LEXER_IDENT_LITERAL || literal_p->type == LEXER_STRING_LITERAL) + && !(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) + { + PARSER_FREE (literal_p->u.char_p); + } + } + } +#else + if (context_p->status_flags & PARSER_HAS_LATE_LIT_INIT) + { + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + if (literal_p->status_flags & LEXER_FLAG_LATE_INIT) + { + uint32_t source_data = literal_p->u.source_data; + const uint8_t *char_p = context_p->source_end_p - (source_data & 0xfffff); + lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (char_p, + source_data >> 20); + literal_pool_p[literal_p->prop.index] = rcs_cpointer_compress (lit); + } + } + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + if ((context_p->status_flags & PARSER_ARGUMENTS_NEEDED) + && !(context_p->status_flags & PARSER_IS_STRICT)) + { + parser_list_iterator_t literal_iterator; + uint16_t argument_count = 0; + + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + while (argument_count < context_p->argument_count) + { + lexer_literal_t *literal_p; + literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); + + JERRY_ASSERT (literal_p != NULL); + + if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) + { + continue; + } + + /* All arguments must be moved to initialized registers. */ + if (literal_p->type == LEXER_UNUSED_LITERAL) + { + if (literal_p->u.char_p == NULL) + { + literal_pool_p[argument_count] = rcs_cpointer_null_cp (); + argument_count++; + continue; + } + + literal_p = PARSER_GET_LITERAL (literal_p->prop.index); + + JERRY_ASSERT (literal_p != NULL); + } + + JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL + && (literal_p->status_flags & LEXER_FLAG_VAR)); + + JERRY_ASSERT (argument_count < literal_p->prop.index); + + literal_pool_p[argument_count] = literal_pool_p[literal_p->prop.index]; + argument_count++; + } + } + + if (context_p->status_flags & PARSER_NAMED_FUNCTION_EXP) + { + ECMA_SET_NON_NULL_POINTER (literal_pool_p[const_literal_end].value.base_cp, + compiled_code_p); + } + +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + + if (snapshot_report_byte_code_compilation) + { + snapshot_add_compiled_code (compiled_code_p, NULL, (uint32_t) total_size); + } + +#endif + + return compiled_code_p; +} /* parser_post_processing */ + +#undef PARSER_NEXT_BYTE +#undef PARSER_NEXT_BYTE_UPDATE + +/** + * Free identifiers and literals. + */ +static void +parser_free_literals (parser_list_t *literal_pool_p) /**< literals */ +{ + parser_list_iterator_t literal_iterator; + lexer_literal_t *literal_p; + + parser_list_iterator_init (literal_pool_p, &literal_iterator); + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + util_free_literal (literal_p); + } + + parser_list_free (literal_pool_p); +} /* parser_free_literals */ + +/** + * Parse and compile EcmaScript source code + * + * @return compiled code + */ +ecma_compiled_code_t * +parser_parse_script (const uint8_t *source_p, /**< valid UTF-8 source code */ + size_t size, /**< size of the source code */ + int strict_mode, /**< strict mode */ + parser_error_location *error_location) /**< error location */ +{ + parser_context_t context; + ecma_compiled_code_t *compiled_code; + + context.error = PARSER_ERR_NO_ERROR; + context.allocated_buffer_p = NULL; + + if (error_location != NULL) + { + error_location->error = PARSER_ERR_NO_ERROR; + } + + context.status_flags = PARSER_NO_REG_STORE | PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NOT_NEEDED; + context.stack_depth = 0; + context.stack_limit = 0; + context.last_context_p = NULL; + context.last_statement.current_p = NULL; + + if (strict_mode) + { + context.status_flags |= PARSER_IS_STRICT; + } + + context.source_p = source_p; + context.source_end_p = source_p + size; + context.line = 1; + context.column = 1; + + context.last_cbc_opcode = PARSER_CBC_UNAVAILABLE; + + context.argument_count = 0; + context.register_count = 0; + context.literal_count = 0; + + parser_cbc_stream_init (&context.byte_code); + context.byte_code_size = 0; + parser_list_init (&context.literal_pool, + sizeof (lexer_literal_t), + (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_literal_t))); + parser_stack_init (&context); + +#ifdef PARSER_DEBUG + context.context_stack_depth = 0; +#endif /* PARSER_DEBUG */ + +#ifdef PARSER_DUMP_BYTE_CODE + context.is_show_opcodes = parser_show_instrs; + context.total_byte_code_size = 0; + + if (context.is_show_opcodes) + { + printf ("\n--- Script parsing start ---\n\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + PARSER_TRY (context.try_buffer) + { + /* Pushing a dummy value ensures the stack is never empty. + * This simplifies the stack management routines. */ + parser_stack_push_uint8 (&context, CBC_MAXIMUM_BYTE_VALUE); + /* The next token must always be present to make decisions + * in the parser. Therefore when a token is consumed, the + * lexer_next_token() must be immediately called. */ + lexer_next_token (&context); + + parser_parse_statements (&context); + + /* When the parsing is successful, only the + * dummy value can be remained on the stack. */ + JERRY_ASSERT (context.stack_top_uint8 == CBC_MAXIMUM_BYTE_VALUE + && context.stack.last_position == 1 + && context.stack.first_p != NULL + && context.stack.first_p->next_p == NULL + && context.stack.last_p == NULL); + JERRY_ASSERT (context.last_statement.current_p == NULL); + + JERRY_ASSERT (context.last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + JERRY_ASSERT (context.allocated_buffer_p == NULL); + + compiled_code = parser_post_processing (&context); + parser_list_free (&context.literal_pool); + +#ifdef PARSER_DUMP_BYTE_CODE + if (context.is_show_opcodes) + { + printf ("\nScript parsing successfully completed. Total byte code size: %d bytes\n", + (int) context.total_byte_code_size); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + } + PARSER_CATCH + { + if (context.last_statement.current_p != NULL) + { + parser_free_jumps (context.last_statement); + } + + if (context.allocated_buffer_p != NULL) + { + parser_free_local (context.allocated_buffer_p); + } + + if (error_location != NULL) + { + error_location->error = context.error; + error_location->line = context.token.line; + error_location->column = context.token.column; + } + + compiled_code = NULL; + parser_free_literals (&context.literal_pool); + parser_cbc_stream_free (&context.byte_code); + } + PARSER_TRY_END + +#ifdef PARSER_DUMP_BYTE_CODE + if (context.is_show_opcodes) + { + printf ("\n--- Script parsing end ---\n\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + parser_stack_free (&context); + + return compiled_code; +} /* parser_parse_script */ + +/** + * Parse function code + * + * @return compiled code + */ +ecma_compiled_code_t * +parser_parse_function (parser_context_t *context_p, /**< context */ + uint32_t status_flags) /**< extra status flags */ +{ + parser_saved_context_t saved_context; + ecma_compiled_code_t *compiled_code_p; + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + + /* Save private part of the context. */ + + saved_context.status_flags = context_p->status_flags; + saved_context.stack_depth = context_p->stack_depth; + saved_context.stack_limit = context_p->stack_limit; + saved_context.prev_context_p = context_p->last_context_p; + saved_context.last_statement = context_p->last_statement; + + saved_context.argument_count = context_p->argument_count; + saved_context.register_count = context_p->register_count; + saved_context.literal_count = context_p->literal_count; + + saved_context.byte_code = context_p->byte_code; + saved_context.byte_code_size = context_p->byte_code_size; + saved_context.literal_pool_data = context_p->literal_pool.data; + +#ifdef PARSER_DEBUG + saved_context.context_stack_depth = context_p->context_stack_depth; +#endif + + /* Reset private part of the context. */ + + JERRY_ASSERT (status_flags & PARSER_IS_FUNCTION); + + context_p->status_flags &= PARSER_IS_STRICT; + context_p->status_flags |= status_flags; + context_p->stack_depth = 0; + context_p->stack_limit = 0; + context_p->last_context_p = &saved_context; + context_p->last_statement.current_p = NULL; + + context_p->argument_count = 0; + context_p->register_count = 0; + context_p->literal_count = 0; + + parser_cbc_stream_init (&context_p->byte_code); + context_p->byte_code_size = 0; + parser_list_reset (&context_p->literal_pool); + +#ifdef PARSER_DEBUG + context_p->context_stack_depth = 0; +#endif /* PARSER_DEBUG */ + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + printf ("\n--- Function parsing start ---\n\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + lexer_next_token (context_p); + + if (context_p->status_flags & PARSER_IS_FUNC_EXPRESSION + && context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + LEXER_IDENT_LITERAL); + + /* The arguments object is created later than the binding to the + * function expression name, so there is no need to assign special flags. */ + if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ARGUMENTS) + { + uint8_t status_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_NAME; + context_p->lit_object.literal_p->status_flags |= status_flags; + } + + if (context_p->token.literal_is_reserved + || context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) + { + context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; + } + + lexer_next_token (context_p); + } + + if (context_p->token.type != LEXER_LEFT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); + } + + lexer_next_token (context_p); + + /* Argument parsing. */ + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + while (PARSER_TRUE) + { + uint16_t literal_count = context_p->literal_count; + + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + LEXER_IDENT_LITERAL); + + if (literal_count == context_p->literal_count + || context_p->token.literal_is_reserved + || context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) + { + context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; + } + + if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS) + { + uint8_t literal_status_flags = context_p->lit_object.literal_p->status_flags; + + literal_status_flags = (uint8_t) (literal_status_flags & ~LEXER_FLAG_NO_REG_STORE); + context_p->lit_object.literal_p->status_flags = literal_status_flags; + + context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; + context_p->status_flags &= ~(PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NEEDED); + } + + if (context_p->literal_count == literal_count) + { + lexer_literal_t *literal_p; + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + *literal_p = *context_p->lit_object.literal_p; + + literal_p->status_flags &= LEXER_FLAG_SOURCE_PTR; + literal_p->status_flags |= LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; + + context_p->literal_count++; + + /* There cannot be references from the byte code to these literals + * since no byte code has been emitted yet. Therefore there is no + * need to set the index field. */ + context_p->lit_object.literal_p->type = LEXER_UNUSED_LITERAL; + + /* Only the LEXER_FLAG_FUNCTION_ARGUMENT flag is kept. */ + context_p->lit_object.literal_p->status_flags &= LEXER_FLAG_FUNCTION_ARGUMENT; + context_p->lit_object.literal_p->u.char_p = NULL; + } + else + { + uint8_t status_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; + context_p->lit_object.literal_p->status_flags |= status_flags; + } + + context_p->argument_count++; + if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + parser_raise_error (context_p, PARSER_ERR_REGISTER_LIMIT_REACHED); + } + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_COMMA) + { + break; + } + + lexer_next_token (context_p); + } + } + + if (context_p->token.type != LEXER_RIGHT_PAREN) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + + context_p->register_count = context_p->argument_count; + + if ((context_p->status_flags & PARSER_IS_PROPERTY_GETTER) + && context_p->argument_count != 0) + { + parser_raise_error (context_p, PARSER_ERR_NO_ARGUMENTS_EXPECTED); + } + + if ((context_p->status_flags & PARSER_IS_PROPERTY_SETTER) + && context_p->argument_count != 1) + { + parser_raise_error (context_p, PARSER_ERR_ONE_ARGUMENT_EXPECTED); + } + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes + && (context_p->status_flags & PARSER_HAS_NON_STRICT_ARG)) + { + printf (" Note: legacy (non-strict) argument definition\n\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + + lexer_next_token (context_p); + parser_parse_statements (context_p); + compiled_code_p = parser_post_processing (context_p); + +#ifdef PARSER_DUMP_BYTE_CODE + if (context_p->is_show_opcodes) + { + printf ("\n--- Function parsing end ---\n\n"); + } +#endif /* PARSER_DUMP_BYTE_CODE */ + + parser_list_free (&context_p->literal_pool); + + /* Restore private part of the context. */ + + JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); + + context_p->status_flags = saved_context.status_flags; + context_p->stack_depth = saved_context.stack_depth; + context_p->stack_limit = saved_context.stack_limit; + context_p->last_context_p = saved_context.prev_context_p; + context_p->last_statement = saved_context.last_statement; + + context_p->argument_count = saved_context.argument_count; + context_p->register_count = saved_context.register_count; + context_p->literal_count = saved_context.literal_count; + + context_p->byte_code = saved_context.byte_code; + context_p->byte_code_size = saved_context.byte_code_size; + context_p->literal_pool.data = saved_context.literal_pool_data; + +#ifdef PARSER_DEBUG + context_p->context_stack_depth = saved_context.context_stack_depth; +#endif + + return compiled_code_p; +} /* parser_parse_function */ + +/** + * Raise a parse error + */ +void +parser_raise_error (parser_context_t *context_p, /**< context */ + parser_error_t error) /**< error code */ +{ + parser_saved_context_t *saved_context_p = context_p->last_context_p; + + while (saved_context_p != NULL) + { + parser_cbc_stream_free (&saved_context_p->byte_code); + + /* First the current literal pool is freed, and then it is replaced + * by the literal pool coming from the saved context. Since literals + * are not used anymore, this is a valid replacement. The last pool + * is freed by parser_parse_script. */ + + parser_free_literals (&context_p->literal_pool); + context_p->literal_pool.data = saved_context_p->literal_pool_data; + + if (saved_context_p->last_statement.current_p != NULL) + { + parser_free_jumps (saved_context_p->last_statement); + } + + saved_context_p = saved_context_p->prev_context_p; + } + + context_p->error = error; + PARSER_THROW (context_p->try_buffer); + /* Should never been reached. */ + JERRY_ASSERT (0); +} /* parser_raise_error */ + +/** + * Tell parser whether to dump bytecode + */ +void +parser_set_show_instrs (int show_instrs) /**< flag indicating whether to dump bytecode */ +{ +#ifdef PARSER_DUMP_BYTE_CODE + parser_show_instrs = show_instrs; +#else + (void) show_instrs; +#endif /* PARSER_DUMP_BYTE_CODE */ +} /* parser_set_show_instrs */ + +/** + * @} + * @} + * @} + */ diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h new file mode 100644 index 0000000000..f7d746945f --- /dev/null +++ b/jerry-core/parser/js/js-parser.h @@ -0,0 +1,143 @@ +/* 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. + */ + +#ifndef JS_PARSER_H +#define JS_PARSER_H + +#include "byte-code.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser JavaScript + * @{ + * + * \addtogroup jsparser_parser Parser + * @{ + */ + +/** + * Error codes. + */ +typedef enum +{ + PARSER_ERR_NO_ERROR, /**< no error */ + + PARSER_ERR_OUT_OF_MEMORY, /**< out of memory */ + PARSER_ERR_LITERAL_LIMIT_REACHED, /**< maximum number of literals reached */ + PARSER_ERR_ARGUMENT_LIMIT_REACHED, /**< maximum number of function arguments reached */ + PARSER_ERR_STACK_LIMIT_REACHED, /**< maximum function stack size reached */ + PARSER_ERR_REGISTER_LIMIT_REACHED, /**< maximum register size reached */ + + PARSER_ERR_INVALID_CHARACTER, /**< unexpected character */ + PARSER_ERR_INVALID_HEX_DIGIT, /**< invalid hexadecimal digit */ + PARSER_ERR_INVALID_ESCAPE_SEQUENCE, /**< invalid escape sequence */ + PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE, /**< invalid unicode escape sequence */ + PARSER_ERR_INVALID_IDENTIFIER_START, /**< character cannot be start of an identifier */ + PARSER_ERR_INVALID_IDENTIFIER_PART, /**< character cannot be part of an identifier */ + + PARSER_ERR_INVALID_NUMBER, /**< invalid number literal */ + PARSER_ERR_MISSING_EXPONENT, /**< missing exponent */ + PARSER_ERR_IDENTIFIER_AFTER_NUMBER, /**< identifier start after number */ + + PARSER_ERR_INVALID_REGEXP, /**< invalid regular expression */ + PARSER_ERR_UNKNOWN_REGEXP_FLAG, /**< unknown regexp flag */ + PARSER_ERR_DUPLICATED_REGEXP_FLAG, /**< duplicated regexp flag */ + PARSER_ERR_UNSUPPORTED_REGEXP, /**< regular expression is not supported in compact profile */ + + PARSER_ERR_IDENTIFIER_TOO_LONG, /**< too long identifier */ + PARSER_ERR_STRING_TOO_LONG, /**< too long string literal */ + PARSER_ERR_NUMBER_TOO_LONG, /**< too long number literal */ + PARSER_ERR_REGEXP_TOO_LONG, /**< too long regexp literal */ + + PARSER_ERR_UNTERMINATED_MULTILINE_COMMENT, /**< unterminated multiline comment */ + PARSER_ERR_UNTERMINATED_STRING, /**< unterminated string literal */ + PARSER_ERR_UNTERMINATED_REGEXP, /**< unterminated regexp literal */ + + PARSER_ERR_NEWLINE_NOT_ALLOWED, /**< newline is not allowed */ + PARSER_ERR_OCTAL_NUMBER_NOT_ALLOWED, /**< octal numbers are not allowed in strict mode */ + PARSER_ERR_OCTAL_ESCAPE_NOT_ALLOWED, /**< octal escape sequences are not allowed in strict mode */ + PARSER_ERR_STRICT_IDENT_NOT_ALLOWED, /**< identifier name is reserved in strict mode */ + PARSER_ERR_EVAL_NOT_ALLOWED, /**< eval is not allowed here in strict mode */ + PARSER_ERR_ARGUMENTS_NOT_ALLOWED, /**< arguments is not allowed here in strict mode */ + PARSER_ERR_DELETE_IDENT_NOT_ALLOWED, /**< identifier delete is not allowed in strict mode */ + PARSER_ERR_EVAL_CANNOT_ASSIGNED, /**< eval cannot be assigned in strict mode */ + PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED, /**< arguments cannot be assigned in strict mode */ + PARSER_ERR_WITH_NOT_ALLOWED, /**< with statement is not allowed in strict mode */ + PARSER_ERR_MULTIPLE_DEFAULTS_NOT_ALLOWED, /**< multiple default cases are not allowed */ + PARSER_ERR_DEFAULT_NOT_IN_SWITCH, /**< default statement is not in switch block */ + PARSER_ERR_CASE_NOT_IN_SWITCH, /**< case statement is not in switch block */ + + PARSER_ERR_LEFT_PAREN_EXPECTED, /**< left paren expected */ + PARSER_ERR_LEFT_BRACE_EXPECTED, /**< left brace expected */ + PARSER_ERR_RIGHT_PAREN_EXPECTED, /**< right paren expected */ + PARSER_ERR_RIGHT_SQUARE_EXPECTED, /**< right square expected */ + PARSER_ERR_COLON_EXPECTED, /**< colon expected */ + PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED, /**< colon expected for conditional expression */ + PARSER_ERR_SEMICOLON_EXPECTED, /**< semicolon expected */ + PARSER_ERR_IN_EXPECTED, /**< in keyword expected */ + PARSER_ERR_WHILE_EXPECTED, /**< while expected for do-while loop */ + PARSER_ERR_CATCH_FINALLY_EXPECTED, /**< catch or finally expected */ + PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED, /**< array item separator expected */ + PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED, /**< object item separator expected */ + PARSER_ERR_IDENTIFIER_EXPECTED, /**< identifier expected */ + PARSER_ERR_EXPRESSION_EXPECTED, /**< expression expected */ + PARSER_ERR_PRIMARY_EXP_EXPECTED, /**< primary expression expected */ + PARSER_ERR_STATEMENT_EXPECTED, /**< statement expected */ + PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED, /**< property identifier expected */ + PARSER_ERR_ARGUMENT_LIST_EXPECTED, /**< argument list expected */ + PARSER_ERR_NO_ARGUMENTS_EXPECTED, /**< property getters must have no arguments */ + PARSER_ERR_ONE_ARGUMENT_EXPECTED, /**< property setters must have one argument */ + + PARSER_ERR_INVALID_EXPRESSION, /**< invalid expression */ + PARSER_ERR_INVALID_SWITCH, /**< invalid switch body */ + PARSER_ERR_INVALID_BREAK, /**< break must be inside a loop or switch */ + PARSER_ERR_INVALID_BREAK_LABEL, /**< break target not found */ + PARSER_ERR_INVALID_CONTINUE, /**< continue must be inside a loop */ + PARSER_ERR_INVALID_CONTINUE_LABEL, /**< continue target not found */ + PARSER_ERR_INVALID_RETURN, /**< return must be inside a function */ + PARSER_ERR_INVALID_RIGHT_SQUARE, /**< right square must terminate a block */ + PARSER_ERR_DUPLICATED_LABEL, /**< duplicated label */ + PARSER_ERR_OBJECT_PROPERTY_REDEFINED, /**< property of object literal redefined */ + PARSER_ERR_NON_STRICT_ARG_DEFINITION /**< non-strict argument definition */ +} parser_error_t; + +/* Source code line counter type. */ +typedef uint32_t parser_line_counter_t; + +/** + * Error code location. + */ +typedef struct +{ + parser_error_t error; /**< error code */ + parser_line_counter_t line; /**< line where the error occured */ + parser_line_counter_t column; /**< column where the error occured */ +} parser_error_location; + +/* Note: source must be a valid UTF-8 string. */ +ecma_compiled_code_t * parser_parse_script (const uint8_t *, size_t, int, parser_error_location *); +const char *parser_error_to_string (parser_error_t); + +extern void parser_set_show_instrs (int); + +/** + * @} + * @} + * @} + */ + +#endif /* !JS_PARSER_H */ diff --git a/jerry-core/parser/js/jsp-early-error.cpp b/jerry-core/parser/js/jsp-early-error.cpp deleted file mode 100644 index 1361fa10f1..0000000000 --- a/jerry-core/parser/js/jsp-early-error.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jsp-early-error.h" -#include "stack.h" -#include "jrt.h" -#include "parser.h" -#include "jrt-libc-includes.h" -#include "ecma-helpers.h" -#include "lit-magic-strings.h" - -/** - * Early error longjmp label, used to finish parse upon an early error occurence - * - * See also: - * jsp_early_error_get_early_error_longjmp_label - * jsp_early_error_raise_error - */ -static jmp_buf jsp_early_error_label; - -/** - * Type of early error occured, or JSP_EARLY_ERROR__NO_ERROR - */ -jsp_early_error_t jsp_early_error_type; - -typedef struct -{ - prop_type type; - lit_literal_t lit; -} prop_literal; - -enum -{ - props_global_size -}; -STATIC_STACK (props, prop_literal) - -enum -{ - size_t_stack_global_size -}; -STATIC_STACK (size_t_stack, size_t) - -/** - * Get buffer for SyntaxError longjmp label - * - * @return pointer to jmp_buf - */ -jmp_buf * -jsp_early_error_get_early_error_longjmp_label (void) -{ - return &jsp_early_error_label; -} /* jsp_early_error_get_early_error_longjmp_label */ - -/** - * Raise an early error of specified type - * - * Note: - * Performs longjmp to early error longjmp label - * - * See also: - * parser_parse_program - */ -void __attribute__((noreturn)) -jsp_early_error_raise_error (jsp_early_error_t type) /**< type of error to raise */ -{ - JERRY_ASSERT (jsp_early_error_type == JSP_EARLY_ERROR__NO_ERROR); - - jsp_early_error_type = type; - - longjmp (jsp_early_error_label, 1); -} /* jsp_early_error_raise_error */ - -/** - * Get type of occured early error - * - * @return type - */ -jsp_early_error_t -jsp_early_error_get_type (void) -{ - JERRY_ASSERT (jsp_early_error_type != JSP_EARLY_ERROR__NO_ERROR); - - return jsp_early_error_type; -} /* jsp_early_error_get_type */ - -static prop_literal -create_prop_literal (lit_literal_t lit, prop_type type) -{ - prop_literal ret; - - ret.type = type; - ret.lit = lit; - - return ret; -} - -void -jsp_early_error_start_checking_of_prop_names (void) -{ - STACK_PUSH (size_t_stack, STACK_SIZE (props)); -} - -void -jsp_early_error_add_prop_name (jsp_operand_t op, prop_type pt) -{ - JERRY_ASSERT (jsp_is_string_lit_operand (op)); - STACK_PUSH (props, create_prop_literal (lit_get_literal_by_cp (jsp_operand_get_literal (op)), pt)); -} - -void -jsp_early_error_check_for_duplication_of_prop_names (bool is_strict, locus loc __attr_unused___) -{ - if (STACK_SIZE (props) - STACK_TOP (size_t_stack) < 2) - { - STACK_DROP (props, (size_t) (STACK_SIZE (props) - STACK_TOP (size_t_stack))); - STACK_DROP (size_t_stack, 1); - return; - } - - for (size_t i = (STACK_TOP (size_t_stack) + 1); - i < STACK_SIZE (props); - i++) - { - const prop_literal previous = STACK_ELEMENT (props, i); - JERRY_ASSERT (previous.type == PROP_DATA - || previous.type == PROP_GET - || previous.type == PROP_SET); - - for (size_t j = STACK_TOP (size_t_stack); j < i; j = j + 1) - { - /*4*/ - const prop_literal current = STACK_ELEMENT (props, j); - JERRY_ASSERT (current.type == PROP_DATA - || current.type == PROP_GET - || current.type == PROP_SET); - - if (lit_literal_equal (previous.lit, current.lit)) - { - /*a*/ - if (is_strict && previous.type == PROP_DATA && current.type == PROP_DATA) - { - PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Duplication of parameter name '%s' in ObjectDeclaration is not allowed in strict mode", - loc, lit_literal_to_str_internal_buf (current.lit)); - } - /*b*/ - if (previous.type == PROP_DATA - && (current.type == PROP_SET || current.type == PROP_GET)) - { - PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Parameter name '%s' in ObjectDeclaration may not be both data and accessor", - loc, lit_literal_to_str_internal_buf (current.lit)); - } - /*c*/ - if (current.type == PROP_DATA - && (previous.type == PROP_SET || previous.type == PROP_GET)) - { - PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Parameter name '%s' in ObjectDeclaration may not be both data and accessor", - loc, lit_literal_to_str_internal_buf (current.lit)); - } - /*d*/ - if ((previous.type == PROP_SET && current.type == PROP_SET) - || (previous.type == PROP_GET && current.type == PROP_GET)) - { - PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Parameter name '%s' in ObjectDeclaration may not be accessor of same type", - loc, lit_literal_to_str_internal_buf (current.lit)); - } - } - } - } - - STACK_DROP (props, (size_t) (STACK_SIZE (props) - STACK_TOP (size_t_stack))); - STACK_DROP (size_t_stack, 1); -} - -void -jsp_early_error_emit_error_on_eval_and_arguments (lit_literal_t lit, /**< literal to check */ - locus loc) /**< location of the literal in source code */ -{ - if (lit_literal_equal_type_utf8 (lit, - lit_get_magic_string_utf8 (LIT_MAGIC_STRING_ARGUMENTS), - lit_get_magic_string_size (LIT_MAGIC_STRING_ARGUMENTS)) - || lit_literal_equal_type_utf8 (lit, - lit_get_magic_string_utf8 (LIT_MAGIC_STRING_EVAL), - lit_get_magic_string_size (LIT_MAGIC_STRING_EVAL))) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "'eval' and 'arguments' are not allowed here in strict mode", loc); - } -} - -void -jsp_early_error_check_for_eval_and_arguments_in_strict_mode (jsp_operand_t op, bool is_strict, locus loc) -{ - if (is_strict) - { - lit_cpointer_t lit_cp; - - if (jsp_is_string_lit_operand (op) - || jsp_is_number_lit_operand (op)) - { - lit_cp = jsp_operand_get_literal (op); - } - else if (jsp_is_identifier_operand (op)) - { - lit_cp = jsp_operand_get_identifier_name (op); - } - else - { - return; - } - - jsp_early_error_emit_error_on_eval_and_arguments (lit_get_literal_by_cp (lit_cp), loc); - } -} - -void -jsp_early_error_check_delete (bool is_strict, locus loc __attr_unused___) -{ - if (is_strict) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "'delete' operator shall not apply on identifier in strict mode.", loc); - } -} - -void -jsp_early_error_init (void) -{ - jsp_early_error_type = JSP_EARLY_ERROR__NO_ERROR; - - STACK_INIT (props); - STACK_INIT (size_t_stack); -} - -void -jsp_early_error_free (void) -{ - STACK_FREE (size_t_stack); - STACK_FREE (props); -} - diff --git a/jerry-core/parser/js/jsp-early-error.h b/jerry-core/parser/js/jsp-early-error.h deleted file mode 100644 index 9ce692c6f8..0000000000 --- a/jerry-core/parser/js/jsp-early-error.h +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef JSP_EARLY_ERROR_H -#define JSP_EARLY_ERROR_H - -#include "jrt-libc-includes.h" -#include "lexer.h" -#include "opcodes-dumper.h" - -#ifndef JERRY_NDEBUG -#define PARSE_ERROR_PRINT_PLACE(TYPE, LOCUS) do { \ - size_t line, column; \ - lexer_locus_to_line_and_column ((LOCUS), &line, &column); \ - lexer_dump_line (line); \ - printf ("\n"); \ - for (size_t i = 0; i < column; i++) { \ - jerry_port_putchar (' '); \ - } \ - printf ("^\n"); \ - printf ("%s: Ln %lu, Col %lu: ", TYPE, (unsigned long) (line + 1), (unsigned long) (column + 1)); \ -} while (0) -#define PARSE_ERROR(type, MESSAGE, LOCUS) do { \ - locus __loc = LOCUS; \ - PARSE_ERROR_PRINT_PLACE ("ERROR", __loc); \ - printf ("%s\n", MESSAGE); \ - jsp_early_error_raise_error (type); \ -} while (0) -#define PARSE_ERROR_VARG(type, MESSAGE, LOCUS, ...) do { \ - locus __loc = LOCUS; \ - PARSE_ERROR_PRINT_PLACE ("ERROR", __loc); \ - printf (MESSAGE, __VA_ARGS__); \ - printf ("\n"); \ - jsp_early_error_raise_error (type); \ -} while (0) -#else /* JERRY_NDEBUG */ -#define PARSE_ERROR(type, MESSAGE, LOCUS) do { \ - locus __attr_unused___ unused_value = LOCUS; jsp_early_error_raise_error (type); \ -} while (0) -#define PARSE_ERROR_VARG(type, MESSAGE, LOCUS, ...) do { \ - locus __attr_unused___ unused_value = LOCUS; jsp_early_error_raise_error (type); \ -} while (0) -#endif /* JERRY_NDEBUG */ - -typedef enum __attr_packed___ -{ - PROP_DATA, - PROP_SET, - PROP_GET -} prop_type; - -/** - * Early error types (ECMA-262 v5, 16) - */ -typedef enum -{ - JSP_EARLY_ERROR__NO_ERROR, /** initializer value (indicates that no error occured) */ - JSP_EARLY_ERROR_SYNTAX, /**< SyntaxError */ - JSP_EARLY_ERROR_REFERENCE /**< ReferenceError */ -} jsp_early_error_t; - -void jsp_early_error_init (void); -void jsp_early_error_free (void); - -void jsp_early_error_start_checking_of_prop_names (void); -void jsp_early_error_add_prop_name (jsp_operand_t, prop_type); -void jsp_early_error_check_for_duplication_of_prop_names (bool, locus); - -void jsp_early_error_emit_error_on_eval_and_arguments (lit_literal_t, locus); -void jsp_early_error_check_for_eval_and_arguments_in_strict_mode (jsp_operand_t, bool, locus); - -void jsp_early_error_check_delete (bool, locus); - -jmp_buf *jsp_early_error_get_early_error_longjmp_label (void); -void __attribute__((noreturn)) jsp_early_error_raise_error (jsp_early_error_t); -jsp_early_error_t jsp_early_error_get_type (void); - -#endif /* JSP_EARLY_ERROR_H */ diff --git a/jerry-core/parser/js/jsp-internal.h b/jerry-core/parser/js/jsp-internal.h deleted file mode 100644 index c1cdb5ae1c..0000000000 --- a/jerry-core/parser/js/jsp-internal.h +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef JSP_INTERNAL_H -#define JSP_INTERNAL_H - -#include "scopes-tree.h" - -/** - * Parse stage - */ -typedef enum -{ - PREPARSE, /**< preparse stage */ - DUMP /**< dump stage */ -} jsp_parse_mode_t; - -/** - * Size of the temporary literal set - */ -#define SCOPE_TMP_LIT_SET_SIZE 32 - -/** - * Parser context - */ -typedef struct -{ - struct jsp_state_t *state_stack_p; /**< parser stack */ - - jsp_parse_mode_t mode; /**< parse stage */ - scope_type_t scope_type; /**< type of currently parsed scope */ - - uint16_t processed_child_scopes_counter; /**< number of processed - * child scopes of the - * current scope */ - - union - { - /** - * Preparse stage information - */ - struct - { - /** - * Currently parsed scope - */ - scopes_tree current_scope_p; - - /** - * Container of the temporary literal set - * - * Temporary literal set is used for estimation of number of unique literals - * in a byte-code instructions block (BLOCK_SIZE). The calculated number - * is always equal or larger than actual number of unique literals. - * - * The set is emptied upon: - * - reaching a bytecode block border; - * - changing scope, to which instructions are dumped. - * - * Emptying the set in second case is necessary, as the set should contain - * unique literals of a bytecode block, and, upon switching to another scope, - * current bytecode block is also switched, correspondingly. However, this - * could only lead to overestimation of unique literals number, relatively - * to the actual number. - */ - lit_cpointer_t tmp_lit_set[SCOPE_TMP_LIT_SET_SIZE]; - - /** - * Number of items in the temporary literal set - */ - uint8_t tmp_lit_set_num; - } preparse_stage; - - /** - * Dump stage information - */ - struct - { - /** - * Current scope's byte-code header - */ - bytecode_data_header_t *current_bc_header_p; - - /** - * Pointer to the scope that would be parsed next - */ - scopes_tree next_scope_to_dump_p; - } dump_stage; - } u; -} jsp_ctx_t; - -void jsp_init_ctx (jsp_ctx_t *, scope_type_t); -bool jsp_is_dump_mode (jsp_ctx_t *); -void jsp_switch_to_dump_mode (jsp_ctx_t *, scopes_tree); -bool jsp_is_strict_mode (jsp_ctx_t *); -void jsp_set_strict_mode (jsp_ctx_t *); -scope_type_t jsp_get_scope_type (jsp_ctx_t *); -void jsp_set_scope_type (jsp_ctx_t *, scope_type_t); -uint16_t jsp_get_processed_child_scopes_counter (jsp_ctx_t *); -void jsp_set_processed_child_scopes_counter (jsp_ctx_t *, uint16_t); -uint16_t jsp_get_and_inc_processed_child_scopes_counter (jsp_ctx_t *); -scopes_tree jsp_get_next_scopes_tree_node_to_dump (jsp_ctx_t *); -scopes_tree jsp_get_current_scopes_tree_node (jsp_ctx_t *); -void jsp_set_current_scopes_tree_node (jsp_ctx_t *, scopes_tree); -bytecode_data_header_t *jsp_get_current_bytecode_header (jsp_ctx_t *); -void jsp_set_current_bytecode_header (jsp_ctx_t *, bytecode_data_header_t *); -void jsp_empty_tmp_literal_set (jsp_ctx_t *); -void jsp_account_next_bytecode_to_literal_reference (jsp_ctx_t *, lit_cpointer_t); - -#endif /* !JSP_INTERNAL_H */ diff --git a/jerry-core/parser/js/jsp-mm.cpp b/jerry-core/parser/js/jsp-mm.cpp deleted file mode 100644 index 47967576fc..0000000000 --- a/jerry-core/parser/js/jsp-mm.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jrt.h" -#include "jsp-mm.h" -#include "mem-allocator.h" -#include "mem-heap.h" - -/** \addtogroup jsparser ECMAScript parser - * @{ - * - * \addtogroup managedmem Managed memory allocation - * @{ - */ - -/** - * Header of a managed block, allocated by parser - */ -typedef struct -{ - mem_cpointer_t prev_block_cp; /**< previous managed block */ - mem_cpointer_t next_block_cp; /**< next managed block */ - - uint32_t padding; /**< padding for alignment */ -} jsp_mm_header_t; - -/** - * Check that alignment of jsp_mm_header_t is MEM_ALIGNMENT - */ -JERRY_STATIC_ASSERT ((sizeof (jsp_mm_header_t) % MEM_ALIGNMENT) == 0); - -/** - * List used for tracking memory blocks - */ -jsp_mm_header_t *jsp_mm_blocks_p = NULL; - -/** - * Initialize managed memory allocator - */ -void -jsp_mm_init (void) -{ - JERRY_ASSERT (jsp_mm_blocks_p == NULL); -} /* jsp_mm_init */ - -/** - * Finalize managed memory allocator - */ -void -jsp_mm_finalize (void) -{ - JERRY_ASSERT (jsp_mm_blocks_p == NULL); -} /* jsp_mm_finalize */ - -/** - * Recommend allocation size - * - * Note: - * The interface is used by collection allocators - * for storage of data that takes the specified - * amount of bytes upon allocation, but probably - * would require more. - * - * To reduce probability of reallocation in future, - * the allocators can request more space in first - * allocation request. - * - * The interface helps to choose appropriate space - * to allocate, considering amount of heap space, - * that would be waste if allocation size - * would not be increased. - * - * @return recommended allocation size - */ -size_t -jsp_mm_recommend_size (size_t minimum_size) /**< minimum required size */ -{ - size_t block_and_header_size = mem_heap_recommend_allocation_size (minimum_size + sizeof (jsp_mm_header_t)); - return block_and_header_size - sizeof (jsp_mm_header_t); -} /* jsp_mm_recommend_size */ - -/** - * Allocate a managed memory block of specified size - * - * @return pointer to data space of allocated block - */ -void* -jsp_mm_alloc (size_t size) /**< size of block to allocate */ -{ - void *ptr_p = mem_heap_alloc_block (size + sizeof (jsp_mm_header_t), MEM_HEAP_ALLOC_SHORT_TERM); - - jsp_mm_header_t *tmem_header_p = (jsp_mm_header_t*) ptr_p; - - tmem_header_p->prev_block_cp = MEM_CP_NULL; - MEM_CP_SET_POINTER (tmem_header_p->next_block_cp, jsp_mm_blocks_p); - - if (jsp_mm_blocks_p != NULL) - { - MEM_CP_SET_POINTER (jsp_mm_blocks_p->prev_block_cp, tmem_header_p); - } - - jsp_mm_blocks_p = tmem_header_p; - - return (void *) (tmem_header_p + 1); -} /* jsp_mm_alloc */ - -/** - * Free a managed memory block - */ -void -jsp_mm_free (void *ptr) /**< pointer to data space of allocated block */ -{ - jsp_mm_header_t *tmem_header_p = ((jsp_mm_header_t *) ptr) - 1; - - jsp_mm_header_t *prev_block_p = MEM_CP_GET_POINTER (jsp_mm_header_t, - tmem_header_p->prev_block_cp); - jsp_mm_header_t *next_block_p = MEM_CP_GET_POINTER (jsp_mm_header_t, - tmem_header_p->next_block_cp); - - if (prev_block_p != NULL) - { - prev_block_p->next_block_cp = tmem_header_p->next_block_cp; - } - else - { - JERRY_ASSERT (jsp_mm_blocks_p == tmem_header_p); - jsp_mm_blocks_p = next_block_p; - } - - if (next_block_p != NULL) - { - next_block_p->prev_block_cp = tmem_header_p->prev_block_cp; - } - - mem_heap_free_block (tmem_header_p); -} /* jsp_mm_free */ - -/** - * Free all currently allocated managed memory blocks - */ -void -jsp_mm_free_all (void) -{ - while (jsp_mm_blocks_p != NULL) - { - jsp_mm_header_t *next_block_p = MEM_CP_GET_POINTER (jsp_mm_header_t, - jsp_mm_blocks_p->next_block_cp); - - mem_heap_free_block (jsp_mm_blocks_p); - - jsp_mm_blocks_p = next_block_p; - } -} /* jsp_mm_free_all */ - -/** - * @} - * @} - */ diff --git a/jerry-core/parser/js/jsp-mm.h b/jerry-core/parser/js/jsp-mm.h deleted file mode 100644 index aee6ffc042..0000000000 --- a/jerry-core/parser/js/jsp-mm.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef JSP_MM_H -#define JSP_MM_H - -#include "jrt-libc-includes.h" - -/** \addtogroup jsparser ECMAScript parser - * @{ - * - * \addtogroup managedmem Managed memory allocation - * @{ - */ - -extern void jsp_mm_init (void); -extern void jsp_mm_finalize (void); -extern size_t jsp_mm_recommend_size (size_t); -extern void *jsp_mm_alloc (size_t); -extern void jsp_mm_free (void *); -extern void jsp_mm_free_all (void); - -/** - * @} - * @} - */ - -#endif /* !JSP_MM_H */ diff --git a/jerry-core/parser/js/lexer.cpp b/jerry-core/parser/js/lexer.cpp deleted file mode 100644 index 6674048b8f..0000000000 --- a/jerry-core/parser/js/lexer.cpp +++ /dev/null @@ -1,1942 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * Copyright 2015 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 "ecma-helpers.h" -#include "jrt-libc-includes.h" -#include "jsp-mm.h" -#include "lexer.h" -#include "lit-char-helpers.h" -#include "lit-magic-strings.h" -#include "lit-strings.h" -#include "jsp-early-error.h" -#include "rcs-records.h" - -static token saved_token, prev_token, sent_token, empty_token; - -static bool allow_dump_lines = false; -static size_t buffer_size = 0; - -/* - * FIXME: - * jerry_api_char_t should not be used outside of API implementation - */ - -/* Represents the contents of a script. */ -static const jerry_api_char_t *buffer_start = NULL; -static lit_utf8_iterator_pos_t token_start_pos; -static bool is_token_parse_in_progress = false; - -static lit_utf8_iterator_t src_iter; - -#define LA(I) (get_char (I)) -#define TOK_START() (src_iter.buf_p + token_start_pos.offset) -#define TOK_SIZE() ((lit_utf8_size_t) (src_iter.buf_pos.offset - token_start_pos.offset)) - -static bool -is_empty (token tok) -{ - return lexer_get_token_type (tok) == TOK_EMPTY; -} - -static locus -current_locus (void) -{ - if (is_token_parse_in_progress) - { - return token_start_pos; - } - else - { - return lit_utf8_iterator_get_pos (&src_iter); - } -} - -static ecma_char_t -get_char (size_t i) -{ - lit_utf8_iterator_t iter = src_iter; - ecma_char_t code_unit; - do - { - if (lit_utf8_iterator_is_eos (&iter)) - { - code_unit = LIT_CHAR_NULL; - break; - } - - code_unit = lit_utf8_iterator_read_next (&iter); - } - while (i--); - - return code_unit; -} - -/** - * Dump current line - */ -static void -dump_current_line (void) -{ - if (!allow_dump_lines) - { - return; - } - - printf ("// "); - - lit_utf8_iterator_t iter = src_iter; - - while (!lit_utf8_iterator_is_eos (&iter)) - { - ecma_char_t code_unit = lit_utf8_iterator_read_next (&iter); - if (lit_char_is_line_terminator (code_unit)) - { - if (code_unit == LIT_CHAR_CR - && !lit_utf8_iterator_is_eos (&iter) - && lit_utf8_iterator_peek_next (&iter) == LIT_CHAR_LF) - { - lit_utf8_iterator_incr (&iter); - } - break; - } - - lit_put_ecma_char (code_unit); - } - - lit_put_ecma_char (LIT_CHAR_LF); -} /* dump_current_line */ - -static token -create_token_from_lit (jsp_token_type_t type, lit_literal_t lit) -{ - token ret; - - ret.type = type; - ret.loc = current_locus (); - ret.uid = rcs_cpointer_compress (lit).packed_value; - - return ret; -} - -/** - * Create token of specified type - * - * @return token descriptor - */ -static token -create_token (jsp_token_type_t type, /**< type of token */ - uint16_t uid) /**< uid of token */ -{ - token ret; - - ret.type = type; - ret.loc = current_locus (); - ret.uid = uid; - - return ret; -} /* create_token */ - -/** - * Create token of specified type from charset - * - * @return token descriptor - */ -static token -lexer_create_token_for_charset (jsp_token_type_t tt, /**< token type */ - const lit_utf8_byte_t *charset_p, /**< charset buffer */ - lit_utf8_size_t size) /**< size of the charset */ -{ - JERRY_ASSERT (charset_p != NULL); - - lit_utf8_iterator_t iter = lit_utf8_iterator_create (charset_p, (lit_utf8_size_t) size); - lit_utf8_size_t new_size = 0; - lit_utf8_size_t new_length = 0; - bool should_convert = false; - - while (!lit_utf8_iterator_is_eos (&iter)) - { - if (iter.buf_pos.is_non_bmp_middle) - { - should_convert = true; - } - lit_utf8_iterator_incr (&iter); - new_size += LIT_CESU8_MAX_BYTES_IN_CODE_UNIT; - } - - lit_utf8_byte_t *converted_str_p; - - if (unlikely (should_convert)) - { - lit_utf8_iterator_seek_bos (&iter); - converted_str_p = (lit_utf8_byte_t *) jsp_mm_alloc (new_size); - - while (!lit_utf8_iterator_is_eos (&iter)) - { - ecma_char_t ch = lit_utf8_iterator_read_next (&iter); - new_length += lit_code_unit_to_utf8 (ch, converted_str_p + new_length); - } - } - else - { - converted_str_p = (lit_utf8_byte_t *) charset_p; - new_length = size; - JERRY_ASSERT (lit_is_cesu8_string_valid (converted_str_p, new_length)); - } - - lit_literal_t lit = lit_find_literal_by_utf8_string (converted_str_p, new_length); - if (lit != NULL) - { - if (unlikely (should_convert)) - { - jsp_mm_free (converted_str_p); - } - - return create_token_from_lit (tt, lit); - } - lit = lit_create_literal_from_utf8_string (converted_str_p, new_length); - rcs_record_type_t type = rcs_record_get_type (lit); - - JERRY_ASSERT (RCS_RECORD_TYPE_IS_CHARSET (type) - || RCS_RECORD_TYPE_IS_MAGIC_STR (type) - || RCS_RECORD_TYPE_IS_MAGIC_STR_EX (type)); - - if (unlikely (should_convert)) - { - jsp_mm_free (converted_str_p); - } - - return create_token_from_lit (tt, lit); -} /* lexer_create_token_for_charset */ - -/** - * Check if the character falls into IdentifierStart group (ECMA-262 v5, 7.6) - * - * @return true / false - */ -static bool -lexer_is_char_can_be_identifier_start (ecma_char_t c) /**< a character */ -{ - return (lit_char_is_unicode_letter (c) - || c == LIT_CHAR_DOLLAR_SIGN - || c == LIT_CHAR_UNDERSCORE - || c == LIT_CHAR_BACKSLASH); -} /* lexer_is_char_can_be_identifier_start */ - -/** - * Check if the character falls into IdentifierPart group (ECMA-262 v5, 7.6) - * - * @return true / false - */ -static bool -lexer_is_char_can_be_identifier_part (ecma_char_t c) /**< a character */ -{ - return (lexer_is_char_can_be_identifier_start (c) - || lit_char_is_unicode_combining_mark (c) - || lit_char_is_unicode_digit (c) - || lit_char_is_unicode_connector_punctuation (c)); -} /* lexer_is_char_can_be_identifier_part */ - -/** - * Try to decode specified character as SingleEscapeCharacter (ECMA-262, v5, 7.8.4) - * - * If specified character is a SingleEscapeCharacter, convert it according to ECMA-262 v5, Table 4. - * Otherwise, output it as is. - * - * @return true - if specified character is a SingleEscapeCharacter, - * false - otherwise. - */ -static bool -lexer_convert_single_escape_character (ecma_char_t c, /**< character to decode */ - ecma_char_t *out_converted_char_p) /**< out: decoded character */ -{ - ecma_char_t converted_char; - bool is_single_escape_character = true; - - switch (c) - { - case LIT_CHAR_LOWERCASE_B: - { - converted_char = LIT_CHAR_BS; - break; - } - - case LIT_CHAR_LOWERCASE_T: - { - converted_char = LIT_CHAR_TAB; - break; - } - - case LIT_CHAR_LOWERCASE_N: - { - converted_char = LIT_CHAR_LF; - break; - } - - case LIT_CHAR_LOWERCASE_V: - { - converted_char = LIT_CHAR_VTAB; - break; - } - - case LIT_CHAR_LOWERCASE_F: - { - converted_char = LIT_CHAR_FF; - break; - } - - case LIT_CHAR_LOWERCASE_R: - { - converted_char = LIT_CHAR_CR; - break; - } - - case LIT_CHAR_DOUBLE_QUOTE: - case LIT_CHAR_SINGLE_QUOTE: - case LIT_CHAR_BACKSLASH: - { - converted_char = c; - break; - } - - default: - { - converted_char = c; - is_single_escape_character = false; - break; - } - } - - if (out_converted_char_p != NULL) - { - *out_converted_char_p = converted_char; - } - - return is_single_escape_character; -} /* lexer_convert_single_escape_character */ - -/** - * Transform specified number of hexadecimal digits pointed by string iterator to character code - * - * @return true - upon successful conversion, - * false - otherwise (characters, pointed by iterator, are not hexadecimal digits, - * or number of characters until end of string is less than specified). - */ -static bool -lexer_convert_escape_sequence_digits_to_char (lit_utf8_iterator_t *src_iter_p, /**< string iterator */ - bool is_unicode_escape_sequence, /**< UnicodeEscapeSequence (true) - * or HexEscapeSequence (false) */ - ecma_char_t *out_converted_char_p) /**< out: converted character */ -{ - uint16_t char_code = 0; - - const uint32_t digits_num = is_unicode_escape_sequence ? 4 : 2; - - for (uint32_t i = 0; i < digits_num; i++) - { - if (lit_utf8_iterator_is_eos (src_iter_p)) - { - return false; - } - - const ecma_char_t next_char = lit_utf8_iterator_read_next (src_iter_p); - - if (!lit_char_is_hex_digit (next_char)) - { - return false; - } - else - { - /* - * Check that highest 4 bits are zero, so the value would not overflow. - */ - JERRY_ASSERT ((char_code & 0xF000u) == 0); - - char_code = (uint16_t) (char_code << 4u); - char_code = (uint16_t) (char_code + lit_char_hex_to_int (next_char)); - } - } - - *out_converted_char_p = (ecma_char_t) char_code; - - return true; -} /* lexer_convert_escape_sequence_digits_to_char */ - -/** - * Transforming escape sequences in the charset, outputting converted string to specified buffer - * - * Note: - * Size of string with transformed escape sequences is always - * less or equal to size of corresponding source string. - * - * @return size of converted string - */ -static lit_utf8_size_t -lexer_transform_escape_sequences (const jerry_api_char_t *source_str_p, /**< string to convert, - * located in source buffer */ - lit_utf8_size_t source_str_size, /**< size of the string and of the output buffer */ - jerry_api_char_t *output_str_buf_p) /**< output buffer for converted string */ -{ - if (source_str_size == 0) - { - return 0; - } - else - { - JERRY_ASSERT (source_str_p != NULL); - } - - lit_utf8_byte_t *output_str_buf_iter_p = output_str_buf_p; - const size_t output_str_buf_size = source_str_size; - bool is_correct_sequence = true; - - lit_utf8_iterator_t source_str_iter = lit_utf8_iterator_create (source_str_p, - source_str_size); - - ecma_char_t prev_converted_char = LIT_CHAR_NULL; - - while (!lit_utf8_iterator_is_eos (&source_str_iter)) - { - ecma_char_t converted_char; - - const ecma_char_t next_char = lit_utf8_iterator_read_next (&source_str_iter); - - if (next_char == LIT_CHAR_BACKSLASH) - { - if (lit_utf8_iterator_is_eos (&source_str_iter)) - { - is_correct_sequence = false; - break; - } - - const ecma_char_t char_after_next = lit_utf8_iterator_read_next (&source_str_iter); - - if (lit_char_is_decimal_digit (char_after_next)) - { - if (lit_char_is_octal_digit (char_after_next)) - { - if (char_after_next == LIT_CHAR_0 - && (lit_utf8_iterator_is_eos (&source_str_iter) - || !lit_char_is_octal_digit (lit_utf8_iterator_peek_next (&source_str_iter)))) - { - converted_char = LIT_CHAR_NULL; - } - else - { - /* Implementation-defined (ECMA-262 v5, B.1.2): octal escape sequences are not implemented */ - is_correct_sequence = false; - break; - } - } - else - { - converted_char = char_after_next; - } - } - else if (char_after_next == LIT_CHAR_LOWERCASE_U - || char_after_next == LIT_CHAR_LOWERCASE_X) - { - if (!lexer_convert_escape_sequence_digits_to_char (&source_str_iter, - char_after_next == LIT_CHAR_LOWERCASE_U, - &converted_char)) - { - is_correct_sequence = false; - break; - } - } - else if (lit_char_is_line_terminator (char_after_next)) - { - /* Skip \, followed by a LineTerminatorSequence (ECMA-262, v5, 7.3) */ - if (char_after_next == LIT_CHAR_CR - && !lit_utf8_iterator_is_eos (&source_str_iter) - && lit_utf8_iterator_peek_next (&source_str_iter) == LIT_CHAR_LF) - { - lit_utf8_iterator_incr (&source_str_iter); - } - - continue; - } - else - { - lexer_convert_single_escape_character (char_after_next, &converted_char); - } - } - else - { - converted_char = next_char; - } - - if (lit_is_code_point_utf16_high_surrogate (prev_converted_char) - && lit_is_code_point_utf16_low_surrogate (converted_char)) - { - output_str_buf_iter_p -= LIT_UTF8_MAX_BYTES_IN_CODE_UNIT; - - lit_code_point_t code_point = lit_convert_surrogate_pair_to_code_point (prev_converted_char, - converted_char); - output_str_buf_iter_p += lit_code_point_to_utf8 (code_point, output_str_buf_iter_p); - } - else - { - output_str_buf_iter_p += lit_code_unit_to_utf8 (converted_char, output_str_buf_iter_p); - JERRY_ASSERT (output_str_buf_iter_p <= output_str_buf_p + output_str_buf_size); - } - - prev_converted_char = converted_char; - } - - if (is_correct_sequence) - { - return (lit_utf8_size_t) (output_str_buf_iter_p - output_str_buf_p); - } - else - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Illegal escape sequence", token_start_pos); - } -} /* lexer_transform_escape_sequences */ - -/** - * Create token of specified type from charset, transforming escape sequences. - * - * @return token descriptor - */ -static token -lexer_create_token_for_charset_transform_escape_sequences (jsp_token_type_t tt, /**< token type */ - const lit_utf8_byte_t *charset_p, /**< charset buffer */ - lit_utf8_size_t size) /**< size of the charset */ -{ - lit_utf8_byte_t *converted_str_p = (lit_utf8_byte_t *) jsp_mm_alloc (size); - - lit_utf8_size_t converted_size = lexer_transform_escape_sequences (charset_p, - size, - converted_str_p); - - token ret = lexer_create_token_for_charset (tt, - converted_str_p, - converted_size); - - jsp_mm_free (converted_str_p); - - return ret; -} /* lexer_create_token_for_charset_transform_escape_sequences */ - -/** - * Try to decode specified string as ReservedWord (ECMA-262 v5, 7.6.1) - * - * @return TOK_KW_* - for Keyword or FutureReservedWord, - * TOK_NULL - for NullLiteral, - * TOK_BOOL - for BooleanLiteral, - * TOK_EMPTY - for other tokens. - */ -static token -lexer_parse_reserved_word (const lit_utf8_byte_t *str_p, /**< characters buffer */ - lit_utf8_size_t str_size, /**< string's length */ - bool is_strict) /**< flag, indicating whether current code is in strict mode code */ -{ - typedef struct - { - const char *keyword_p; - jsp_token_type_t keyword_id; - } kw_descr_t; - - const kw_descr_t keywords[] = - { -#define KW_DESCR(literal, keyword_id) { literal, keyword_id } - KW_DESCR ("break", TOK_KW_BREAK), - KW_DESCR ("case", TOK_KW_CASE), - KW_DESCR ("catch", TOK_KW_CATCH), - KW_DESCR ("class", TOK_KW_CLASS), - KW_DESCR ("const", TOK_KW_CONST), - KW_DESCR ("continue", TOK_KW_CONTINUE), - KW_DESCR ("debugger", TOK_KW_DEBUGGER), - KW_DESCR ("default", TOK_KW_DEFAULT), - KW_DESCR ("delete", TOK_KW_DELETE), - KW_DESCR ("do", TOK_KW_DO), - KW_DESCR ("else", TOK_KW_ELSE), - KW_DESCR ("enum", TOK_KW_ENUM), - KW_DESCR ("export", TOK_KW_EXPORT), - KW_DESCR ("extends", TOK_KW_EXTENDS), - KW_DESCR ("finally", TOK_KW_FINALLY), - KW_DESCR ("for", TOK_KW_FOR), - KW_DESCR ("function", TOK_KW_FUNCTION), - KW_DESCR ("if", TOK_KW_IF), - KW_DESCR ("in", TOK_KW_IN), - KW_DESCR ("instanceof", TOK_KW_INSTANCEOF), - KW_DESCR ("interface", TOK_KW_INTERFACE), - KW_DESCR ("import", TOK_KW_IMPORT), - KW_DESCR ("implements", TOK_KW_IMPLEMENTS), - KW_DESCR ("let", TOK_KW_LET), - KW_DESCR ("new", TOK_KW_NEW), - KW_DESCR ("package", TOK_KW_PACKAGE), - KW_DESCR ("private", TOK_KW_PRIVATE), - KW_DESCR ("protected", TOK_KW_PROTECTED), - KW_DESCR ("public", TOK_KW_PUBLIC), - KW_DESCR ("return", TOK_KW_RETURN), - KW_DESCR ("static", TOK_KW_STATIC), - KW_DESCR ("super", TOK_KW_SUPER), - KW_DESCR ("switch", TOK_KW_SWITCH), - KW_DESCR ("this", TOK_KW_THIS), - KW_DESCR ("throw", TOK_KW_THROW), - KW_DESCR ("try", TOK_KW_TRY), - KW_DESCR ("typeof", TOK_KW_TYPEOF), - KW_DESCR ("var", TOK_KW_VAR), - KW_DESCR ("void", TOK_KW_VOID), - KW_DESCR ("while", TOK_KW_WHILE), - KW_DESCR ("with", TOK_KW_WITH), - KW_DESCR ("yield", TOK_KW_YIELD) -#undef KW_DESCR - }; - - jsp_token_type_t kw = TOK_EMPTY; - - for (uint32_t i = 0; i < sizeof (keywords) / sizeof (kw_descr_t); i++) - { - if (lit_compare_utf8_strings (str_p, - str_size, - (lit_utf8_byte_t *) keywords[i].keyword_p, - (lit_utf8_size_t) strlen (keywords[i].keyword_p))) - { - kw = keywords[i].keyword_id; - break; - } - } - - if (!is_strict) - { - switch (kw) - { - case TOK_KW_INTERFACE: - case TOK_KW_IMPLEMENTS: - case TOK_KW_LET: - case TOK_KW_PACKAGE: - case TOK_KW_PRIVATE: - case TOK_KW_PROTECTED: - case TOK_KW_PUBLIC: - case TOK_KW_STATIC: - case TOK_KW_YIELD: - { - return empty_token; - } - - default: - { - break; - } - } - } - - if (kw != TOK_EMPTY) - { - return create_token (kw, 0); - } - else - { - if (lit_compare_utf8_string_and_magic_string (str_p, str_size, LIT_MAGIC_STRING_FALSE)) - { - return create_token (TOK_BOOL, false); - } - else if (lit_compare_utf8_string_and_magic_string (str_p, str_size, LIT_MAGIC_STRING_TRUE)) - { - return create_token (TOK_BOOL, true); - } - else if (lit_compare_utf8_string_and_magic_string (str_p, str_size, LIT_MAGIC_STRING_NULL)) - { - return create_token (TOK_NULL, 0); - } - else - { - return empty_token; - } - } -} /* lexer_parse_reserved_word */ - -static token -convert_seen_num_to_token (ecma_number_t num) -{ - lit_literal_t lit = lit_find_literal_by_num (num); - if (lit != NULL) - { - return create_token_from_lit (TOK_NUMBER, lit); - } - - return create_token_from_lit (TOK_NUMBER, lit_create_literal_from_num (num)); -} - -static void -new_token (void) -{ - token_start_pos = lit_utf8_iterator_get_pos (&src_iter); - JERRY_ASSERT (!token_start_pos.is_non_bmp_middle); - is_token_parse_in_progress = true; -} - -static void -consume_char (void) -{ - if (!lit_utf8_iterator_is_eos (&src_iter)) - { - lit_utf8_iterator_incr (&src_iter); - } -} - -#define RETURN_PUNC_EX(TOK, NUM) \ - do \ - { \ - token tok = create_token (TOK, 0); \ - lit_utf8_iterator_advance (&src_iter, NUM); \ - return tok; \ - } \ - while (0) - -#define RETURN_PUNC(TOK) RETURN_PUNC_EX(TOK, 1) - -#define IF_LA_N_IS(CHAR, THEN_TOK, ELSE_TOK, NUM) \ - do \ - { \ - if (LA (NUM) == CHAR) \ - { \ - RETURN_PUNC_EX (THEN_TOK, NUM + 1); \ - } \ - else \ - { \ - RETURN_PUNC_EX (ELSE_TOK, NUM); \ - } \ - } \ - while (0) - -#define IF_LA_IS(CHAR, THEN_TOK, ELSE_TOK) \ - IF_LA_N_IS (CHAR, THEN_TOK, ELSE_TOK, 1) - -#define IF_LA_IS_OR(CHAR1, THEN1_TOK, CHAR2, THEN2_TOK, ELSE_TOK) \ - do \ - { \ - if (LA (1) == CHAR1) \ - { \ - RETURN_PUNC_EX (THEN1_TOK, 2); \ - } \ - else if (LA (1) == CHAR2) \ - { \ - RETURN_PUNC_EX (THEN2_TOK, 2); \ - } \ - else \ - { \ - RETURN_PUNC (ELSE_TOK); \ - } \ - } \ - while (0) - -/** - * Parse Identifier (ECMA-262 v5, 7.6) or ReservedWord (7.6.1; 7.8.1; 7.8.2). - * - * @return TOK_NAME - for Identifier, - * TOK_KW_* - for Keyword or FutureReservedWord, - * TOK_NULL - for NullLiteral, - * TOK_BOOL - for BooleanLiteral - */ -static token -lexer_parse_identifier_or_keyword (bool is_strict) /**< flag, indicating whether current code is in strict mode code */ -{ - ecma_char_t c = LA (0); - - JERRY_ASSERT (lexer_is_char_can_be_identifier_start (c)); - - new_token (); - - bool is_correct_identifier_name = true; - bool is_escape_sequence_occured = false; - bool is_all_chars_were_lowercase_ascii = true; - - while (true) - { - c = LA (0); - - if (c == LIT_CHAR_BACKSLASH) - { - consume_char (); - - is_escape_sequence_occured = true; - - bool is_unicode_escape_sequence = (LA (0) == LIT_CHAR_LOWERCASE_U); - consume_char (); - - if (is_unicode_escape_sequence) - { - /* UnicodeEscapeSequence */ - - if (!lexer_convert_escape_sequence_digits_to_char (&src_iter, - true, - &c)) - { - is_correct_identifier_name = false; - break; - } - else - { - /* c now contains character, encoded in the UnicodeEscapeSequence */ - - // Check character, converted from UnicodeEscapeSequence - if (!lexer_is_char_can_be_identifier_part (c)) - { - is_correct_identifier_name = false; - break; - } - } - } - else - { - is_correct_identifier_name = false; - break; - } - } - else if (!lexer_is_char_can_be_identifier_part (c)) - { - break; - } - else - { - if (!(c >= LIT_CHAR_ASCII_LOWERCASE_LETTERS_BEGIN - && c <= LIT_CHAR_ASCII_LOWERCASE_LETTERS_END)) - { - is_all_chars_were_lowercase_ascii = false; - } - - consume_char (); - } - } - - if (!is_correct_identifier_name) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Illegal identifier name", lit_utf8_iterator_get_pos (&src_iter)); - } - - const lit_utf8_size_t charset_size = TOK_SIZE (); - - token ret = empty_token; - - if (!is_escape_sequence_occured - && is_all_chars_were_lowercase_ascii) - { - /* Keyword or FutureReservedWord (TOK_KW_*), or boolean literal (TOK_BOOL), or null literal (TOK_NULL) */ - ret = lexer_parse_reserved_word (TOK_START (), charset_size, is_strict); - } - - if (is_empty (ret)) - { - /* Identifier (TOK_NAME) */ - - if (!is_escape_sequence_occured) - { - ret = lexer_create_token_for_charset (TOK_NAME, - TOK_START (), - charset_size); - } - else - { - ret = lexer_create_token_for_charset_transform_escape_sequences (TOK_NAME, - TOK_START (), - charset_size); - } - } - - is_token_parse_in_progress = false; - - return ret; -} /* lexer_parse_identifier_or_keyword */ - -/** - * Parse numeric literal (ECMA-262, v5, 7.8.3) - * - * @return token of TOK_SMALL_INT or TOK_NUMBER types - */ -static token -lexer_parse_number (bool is_strict) /**< flag, indicating whether current code is in strict mode code */ -{ - ecma_char_t c = LA (0); - bool is_hex = false; - bool is_fp = false; - ecma_number_t fp_res = .0; - size_t tok_length = 0, i; - token known_token; - - JERRY_ASSERT (lit_char_is_decimal_digit (c) - || c == LIT_CHAR_DOT); - - if (c == LIT_CHAR_0) - { - if (LA (1) == LIT_CHAR_LOWERCASE_X - || LA (1) == LIT_CHAR_UPPERCASE_X) - { - is_hex = true; - } - } - else if (c == LIT_CHAR_DOT) - { - JERRY_ASSERT (lit_char_is_decimal_digit (LA (1))); - is_fp = true; - } - - if (is_hex) - { - new_token (); - - // Eat up '0x' - consume_char (); - consume_char (); - - c = LA (0); - if (!lit_char_is_hex_digit (c)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid HexIntegerLiteral", lit_utf8_iterator_get_pos (&src_iter)); - } - - do - { - consume_char (); - c = LA (0); - } - while (lit_char_is_hex_digit (c)); - - if (lexer_is_char_can_be_identifier_start (c)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "Identifier just after integer literal", - lit_utf8_iterator_get_pos (&src_iter)); - } - - tok_length = (size_t) (TOK_SIZE ()); - - const size_t length_of_zero_and_x_str = 2u; - const lit_utf8_byte_t *fp_buf_p = TOK_START () + length_of_zero_and_x_str; - - /* token is constructed at end of function */ - for (i = 0; i < tok_length - length_of_zero_and_x_str; i++) - { - fp_res = fp_res * 16 + (ecma_number_t) lit_char_hex_to_int (fp_buf_p[i]); - } - } - else - { - bool is_exp = false; - - new_token (); - - // Eat up '.' - if (is_fp) - { - consume_char (); - } - - while (true) - { - c = LA (0); - - if (c == LIT_CHAR_DOT) - { - if (is_fp || is_exp) - { - /* token is constructed at end of function */ - break; - } - else - { - is_fp = true; - consume_char (); - - continue; - } - } - else if (c == LIT_CHAR_LOWERCASE_E - || c == LIT_CHAR_UPPERCASE_E) - { - if (is_exp) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "Numeric literal shall not contain more than exponential marker ('e' or 'E')", - lit_utf8_iterator_get_pos (&src_iter)); - } - else - { - is_exp = true; - consume_char (); - - if (LA (0) == LIT_CHAR_MINUS - || LA (0) == LIT_CHAR_PLUS) - { - consume_char (); - } - - if (!lit_char_is_decimal_digit (LA (0))) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "Exponential mark in a numeric literal should followed by a singed interger", - lit_utf8_iterator_get_pos (&src_iter)); - } - - continue; - } - } - else if (!lit_char_is_decimal_digit (c)) - { - if (lexer_is_char_can_be_identifier_start (c)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "Numeric literal shall not contain non-numeric characters", - lit_utf8_iterator_get_pos (&src_iter)); - } - - /* token is constructed at end of function */ - break; - } - - consume_char (); - } - - tok_length = (size_t) (TOK_SIZE ()); - - if (is_fp || is_exp) - { - ecma_number_t res = ecma_utf8_string_to_number (TOK_START (), (jerry_api_size_t) tok_length); - JERRY_ASSERT (!ecma_number_is_nan (res)); - - known_token = convert_seen_num_to_token (res); - is_token_parse_in_progress = NULL; - - return known_token; - } - else if (*TOK_START () == LIT_CHAR_0 - && tok_length != 1) - { - /* Octal integer literals */ - if (is_strict) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Octal integer literals are not allowed in strict mode", token_start_pos); - } - else - { - /* token is constructed at end of function */ - const lit_utf8_byte_t *fp_buf_p = TOK_START (); - - for (i = 0; i < tok_length; i++) - { - fp_res = fp_res * 8 + (ecma_number_t) lit_char_hex_to_int (fp_buf_p[i]); - } - } - } - else - { - const lit_utf8_byte_t *fp_buf_p = TOK_START (); - /* token is constructed at end of function */ - - ecma_number_t mult = 1.0f; - for (i = tok_length; i > 0; i--, mult *= 10) - { - fp_res += (ecma_number_t) lit_char_hex_to_int (fp_buf_p[i - 1]) * mult; - } - } - } - - if (fp_res >= 0 && fp_res <= 255 && (uint8_t) fp_res == fp_res) - { - known_token = create_token (TOK_SMALL_INT, (uint8_t) fp_res); - is_token_parse_in_progress = NULL; - return known_token; - } - else - { - known_token = convert_seen_num_to_token (fp_res); - is_token_parse_in_progress = NULL; - return known_token; - } -} /* lexer_parse_number */ - -/** - * Parse string literal (ECMA-262 v5, 7.8.4) - */ -static token -lexer_parse_string (void) -{ - ecma_char_t c = (ecma_char_t) LA (0); - JERRY_ASSERT (c == LIT_CHAR_SINGLE_QUOTE - || c == LIT_CHAR_DOUBLE_QUOTE); - - - new_token (); - - /* Consume quote character */ - consume_char (); - - const ecma_char_t end_char = c; - - bool is_escape_sequence_occured = false; - - do - { - c = LA (0); - consume_char (); - - if (lit_char_is_line_terminator (c)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "String literal shall not contain newline character", token_start_pos); - } - else if (c == LIT_CHAR_BACKSLASH) - { - is_escape_sequence_occured = true; - - ecma_char_t nc = (ecma_char_t) LA (0); - consume_char (); - - if (nc == LIT_CHAR_CR) - { - nc = (ecma_char_t) LA (0); - - if (nc == LIT_CHAR_LF) - { - consume_char (); - } - } - } - } - while (c != end_char && !lit_utf8_iterator_is_eos (&src_iter)); - - if (c == LIT_CHAR_NULL) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unclosed string", token_start_pos); - } - - const lit_utf8_size_t charset_size = TOK_SIZE () - 2; - - token ret; - - if (!is_escape_sequence_occured) - { - ret = lexer_create_token_for_charset (TOK_STRING, - TOK_START () + 1, - charset_size); - } - else - { - ret = lexer_create_token_for_charset_transform_escape_sequences (TOK_STRING, - TOK_START () + 1, - charset_size); - } - - is_token_parse_in_progress = false; - - return ret; -} /* lexer_parse_string */ - -/** - * Parse string literal (ECMA-262 v5, 7.8.5) - */ -static token -lexer_parse_regexp (void) -{ - token result; - bool is_char_class = false; - - JERRY_ASSERT (LA (0) == LIT_CHAR_SLASH); - new_token (); - - /* Eat up '/' */ - consume_char (); - - while (true) - { - if (lit_utf8_iterator_is_eos (&src_iter)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unterminated RegExp literal", token_start_pos); - } - - ecma_char_t c = (ecma_char_t) LA (0); - if (lit_char_is_line_terminator (c)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "RegExp literal should not contain newline character", token_start_pos); - } - else if (c == LIT_CHAR_BACKSLASH) - { - consume_char (); - if (lit_char_is_line_terminator (LA (0))) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "RegExp literal backslash sequence should not contain newline character", - token_start_pos); - } - } - else if (c == LIT_CHAR_LEFT_SQUARE) - { - is_char_class = true; - } - else if (c == LIT_CHAR_RIGHT_SQUARE) - { - is_char_class = false; - } - else if (c == LIT_CHAR_SLASH && !is_char_class) - { - /* Eat up '/' */ - consume_char (); - break; - } - - consume_char (); - } - - /* Try to parse RegExp flags */ - while (true) - { - ecma_char_t c = (ecma_char_t) LA (0); - - if (!lit_char_is_word_char (c) || lit_char_is_line_terminator (c)) - { - break; - } - - consume_char (); - } - - result = lexer_create_token_for_charset (TOK_REGEXP, TOK_START () + 1, TOK_SIZE () - 1); - - is_token_parse_in_progress = false; - return result; -} /* lexer_parse_regexp */ - -/** - * Parse a comment - * - * @return true if newline was met during parsing - * false - otherwise - */ -static bool -lexer_parse_comment (void) -{ - ecma_char_t c = LA (0); - bool multiline; - bool was_newlines = false; - - JERRY_ASSERT (LA (0) == LIT_CHAR_SLASH); - JERRY_ASSERT (LA (1) == LIT_CHAR_SLASH || LA (1) == LIT_CHAR_ASTERISK); - - multiline = (LA (1) == LIT_CHAR_ASTERISK); - - consume_char (); - consume_char (); - - while (!lit_utf8_iterator_is_eos (&src_iter)) - { - c = LA (0); - - if (!multiline) - { - if (lit_char_is_line_terminator (c)) - { - return true; - } - } - else - { - if (c == LIT_CHAR_ASTERISK - && LA (1) == LIT_CHAR_SLASH) - { - consume_char (); - consume_char (); - - return was_newlines; - } - else if (lit_char_is_line_terminator (c)) - { - was_newlines = true; - } - } - - consume_char (); - } - - if (multiline) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unclosed multiline comment", lit_utf8_iterator_get_pos (&src_iter)); - } - - return false; -} /* lexer_parse_comment */ - -/** - * Skip any whitespace and comment tokens - * - * @return true - if a newline token was skipped, - * false - otherwise - */ -static bool -lexer_skip_whitespace_and_comments (void) -{ - bool new_lines_occurred = false; - - while (true) - { - ecma_char_t c = LA (0); - - if (lit_char_is_white_space (c)) - { - do - { - consume_char (); - - c = LA (0); - } - while (lit_char_is_white_space (c)); - } - else if (lit_char_is_line_terminator (c)) - { - dump_current_line (); - - new_lines_occurred = true; - - do - { - consume_char (); - - c = LA (0); - } - while (lit_char_is_line_terminator (c)); - } - else if (c == LIT_CHAR_SLASH - && (LA (1) == LIT_CHAR_SLASH - || LA (1) == LIT_CHAR_ASTERISK)) - { - /* ECMA-262 v5, 7.4, SingleLineComment or MultiLineComment */ - - if (lexer_parse_comment ()) - { - new_lines_occurred = true; - } - } - else - { - break; - } - } - - return new_lines_occurred; -} /* lexer_skip_whitespace_and_comments */ - -/** - * Parse and construct lexer token - * - * Note: - * Currently, lexer token doesn't fully correspond to Token, defined in ECMA-262, v5, 7.5. - * For example, there is no new-line token type in the token definition of ECMA-262 v5. - * - * Note: - * For Lexer alone, it is hard to find out a token is whether a regexp or a division. - * Parser must set maybe_regexp to true if a regexp is expected. - * Otherwise, a division is expected. - * - * @return constructed token - */ -static token -lexer_parse_token (bool maybe_regexp, /**< read '/' as regexp? */ - bool *out_is_preceed_by_new_lines_p, /**< out: is constructed token preceded by newlines? */ - bool is_strict) /**< flag, indicating whether current code is in strict mode code */ - -{ - JERRY_ASSERT (is_token_parse_in_progress == false); - - *out_is_preceed_by_new_lines_p = lexer_skip_whitespace_and_comments (); - - ecma_char_t c = LA (0); - - /* ECMA-262 v5, 7.6, Identifier */ - if (lexer_is_char_can_be_identifier_start (c)) - { - return lexer_parse_identifier_or_keyword (is_strict); - } - - /* ECMA-262 v5, 7.8.3, Numeric literal */ - if (lit_char_is_decimal_digit (c) - || (c == LIT_CHAR_DOT - && lit_char_is_decimal_digit (LA (1)))) - { - return lexer_parse_number (is_strict); - } - - if (c == LIT_CHAR_NULL) - { - return create_token (TOK_EOF, 0); - } - - if (c == LIT_CHAR_SINGLE_QUOTE - || c == LIT_CHAR_DOUBLE_QUOTE) - { - return lexer_parse_string (); - } - - if (c == LIT_CHAR_SLASH && maybe_regexp) - { - return lexer_parse_regexp (); - } - - /* ECMA-262 v5, 7.7, Punctuator */ - switch (c) - { - case LIT_CHAR_LEFT_BRACE: - { - RETURN_PUNC (TOK_OPEN_BRACE); - break; - } - case LIT_CHAR_RIGHT_BRACE: - { - RETURN_PUNC (TOK_CLOSE_BRACE); - break; - } - case LIT_CHAR_LEFT_PAREN: - { - RETURN_PUNC (TOK_OPEN_PAREN); - break; - } - case LIT_CHAR_RIGHT_PAREN: - { - RETURN_PUNC (TOK_CLOSE_PAREN); - break; - } - case LIT_CHAR_LEFT_SQUARE: - { - RETURN_PUNC (TOK_OPEN_SQUARE); - break; - } - case LIT_CHAR_RIGHT_SQUARE: - { - RETURN_PUNC (TOK_CLOSE_SQUARE); - break; - } - case LIT_CHAR_DOT: - { - RETURN_PUNC (TOK_DOT); - break; - } - case LIT_CHAR_SEMICOLON: - { - RETURN_PUNC (TOK_SEMICOLON); - break; - } - case LIT_CHAR_COMMA: - { - RETURN_PUNC (TOK_COMMA); - break; - } - case LIT_CHAR_TILDE: - { - RETURN_PUNC (TOK_COMPL); - break; - } - case LIT_CHAR_COLON: - { - RETURN_PUNC (TOK_COLON); - break; - } - case LIT_CHAR_QUESTION: - { - RETURN_PUNC (TOK_QUERY); - break; - } - - case LIT_CHAR_ASTERISK: - { - IF_LA_IS (LIT_CHAR_EQUALS, TOK_MULT_EQ, TOK_MULT); - break; - } - case LIT_CHAR_SLASH: - { - IF_LA_IS (LIT_CHAR_EQUALS, TOK_DIV_EQ, TOK_DIV); - break; - } - case LIT_CHAR_CIRCUMFLEX: - { - IF_LA_IS (LIT_CHAR_EQUALS, TOK_XOR_EQ, TOK_XOR); - break; - } - case LIT_CHAR_PERCENT: - { - IF_LA_IS (LIT_CHAR_EQUALS, TOK_MOD_EQ, TOK_MOD); - break; - } - case LIT_CHAR_PLUS: - { - IF_LA_IS_OR (LIT_CHAR_PLUS, TOK_DOUBLE_PLUS, LIT_CHAR_EQUALS, TOK_PLUS_EQ, TOK_PLUS); - break; - } - case LIT_CHAR_MINUS: - { - IF_LA_IS_OR (LIT_CHAR_MINUS, TOK_DOUBLE_MINUS, LIT_CHAR_EQUALS, TOK_MINUS_EQ, TOK_MINUS); - break; - } - case LIT_CHAR_AMPERSAND: - { - IF_LA_IS_OR (LIT_CHAR_AMPERSAND, TOK_DOUBLE_AND, LIT_CHAR_EQUALS, TOK_AND_EQ, TOK_AND); - break; - } - case LIT_CHAR_VLINE: - { - IF_LA_IS_OR (LIT_CHAR_VLINE, TOK_DOUBLE_OR, LIT_CHAR_EQUALS, TOK_OR_EQ, TOK_OR); - break; - } - case LIT_CHAR_LESS_THAN: - { - switch (LA (1)) - { - case LIT_CHAR_LESS_THAN: IF_LA_N_IS (LIT_CHAR_EQUALS, TOK_LSHIFT_EQ, TOK_LSHIFT, 2); break; - case LIT_CHAR_EQUALS: RETURN_PUNC_EX (TOK_LESS_EQ, 2); break; - default: RETURN_PUNC (TOK_LESS); - } - break; - } - case LIT_CHAR_GREATER_THAN: - { - switch (LA (1)) - { - case LIT_CHAR_GREATER_THAN: - { - switch (LA (2)) - { - case LIT_CHAR_GREATER_THAN: IF_LA_N_IS (LIT_CHAR_EQUALS, TOK_RSHIFT_EX_EQ, TOK_RSHIFT_EX, 3); break; - case LIT_CHAR_EQUALS: RETURN_PUNC_EX (TOK_RSHIFT_EQ, 3); break; - default: RETURN_PUNC_EX (TOK_RSHIFT, 2); - } - break; - } - case LIT_CHAR_EQUALS: RETURN_PUNC_EX (TOK_GREATER_EQ, 2); break; - default: RETURN_PUNC (TOK_GREATER); - } - break; - } - case LIT_CHAR_EQUALS: - { - if (LA (1) == LIT_CHAR_EQUALS) - { - IF_LA_N_IS (LIT_CHAR_EQUALS, TOK_TRIPLE_EQ, TOK_DOUBLE_EQ, 2); - } - else - { - RETURN_PUNC (TOK_EQ); - } - break; - } - case LIT_CHAR_EXCLAMATION: - { - if (LA (1) == LIT_CHAR_EQUALS) - { - IF_LA_N_IS (LIT_CHAR_EQUALS, TOK_NOT_DOUBLE_EQ, TOK_NOT_EQ, 2); - } - else - { - RETURN_PUNC (TOK_NOT); - } - break; - } - } - - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Illegal character", lit_utf8_iterator_get_pos (&src_iter)); -} /* lexer_parse_token */ - -/** - * Construct next token from current source code position and increment the position - * - * @return the constructed token - */ -token -lexer_next_token (bool maybe_regexp, /**< read '/' as regexp? */ - bool is_strict) /**< strict mode is on (true) / off (false) */ -{ - lit_utf8_iterator_pos_t src_pos = lit_utf8_iterator_get_pos (&src_iter); - if (src_pos.offset == 0 && !src_pos.is_non_bmp_middle) - { - dump_current_line (); - } - - if (!is_empty (saved_token)) - { - sent_token = saved_token; - saved_token = empty_token; - } - else - { - /** - * FIXME: - * The way to raise syntax errors for unexpected EOF - * should be reworked so that EOF would be checked by - * caller of the routine, and the following condition - * would be checked as assertion in the routine. - */ - if (lexer_get_token_type (prev_token) == TOK_EOF - && lexer_get_token_type (sent_token) == TOK_EOF) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unexpected EOF", lit_utf8_iterator_get_pos (&src_iter)); - } - - prev_token = sent_token; - - jsp_token_flag_t flags = JSP_TOKEN_FLAG__NO_FLAGS; - - bool is_preceded_by_new_lines; - sent_token = lexer_parse_token (maybe_regexp, &is_preceded_by_new_lines, is_strict); - - if (is_preceded_by_new_lines) - { - flags = (jsp_token_flag_t) (flags | JSP_TOKEN_FLAG_PRECEDED_BY_NEWLINES); - } - - sent_token.flags = flags; - } - - return sent_token; -} /* lexer_next_token */ - -/** - * Set lexer's iteraror over source file to the specified position - */ -void -lexer_seek (lit_utf8_iterator_pos_t locus) /**< position in the source buffer */ -{ - JERRY_ASSERT (is_token_parse_in_progress == false); - - lit_utf8_iterator_seek (&src_iter, locus); - saved_token = empty_token; - prev_token = empty_token; -} /* lexer_seek */ - -/** - * Convert locus to line and column - */ -void -lexer_locus_to_line_and_column (lit_utf8_iterator_pos_t locus, /**< iterator position in the source script */ - size_t *line, /**< @out: line number */ - size_t *column) /**< @out: column number */ -{ - JERRY_ASSERT ((lit_utf8_size_t) (locus.offset + locus.is_non_bmp_middle) <= buffer_size); - - lit_utf8_iterator_t iter = lit_utf8_iterator_create (buffer_start, (lit_utf8_size_t) buffer_size); - lit_utf8_iterator_pos_t iter_pos = lit_utf8_iterator_get_pos (&iter); - - size_t l = 0, c = 0; - while (!lit_utf8_iterator_is_eos (&iter) && lit_utf8_iterator_pos_cmp (iter_pos, locus) < 0) - { - ecma_char_t code_unit = lit_utf8_iterator_read_next (&iter); - iter_pos = lit_utf8_iterator_get_pos (&iter); - - if (lit_char_is_line_terminator (code_unit)) - { - if (code_unit == LIT_CHAR_CR - && !lit_utf8_iterator_is_eos (&iter) - && lit_utf8_iterator_peek_next (&iter) == LIT_CHAR_LF) - { - lit_utf8_iterator_incr (&iter); - } - c = 0; - l++; - continue; - } - - c++; - } - - if (line) - { - *line = l; - } - if (column) - { - *column = c; - } -} /* lexer_locus_to_line_and_column */ - -/** - * Dump specified line of the source script - */ -void -lexer_dump_line (size_t line) /**< line number */ -{ - size_t l = 0; - lit_utf8_iterator_t iter = src_iter; - - lit_utf8_iterator_seek_bos (&iter); - - while (!lit_utf8_iterator_is_eos (&iter)) - { - ecma_char_t code_unit; - - if (l == line) - { - while (!lit_utf8_iterator_is_eos (&iter)) - { - code_unit = lit_utf8_iterator_read_next (&iter); - if (lit_char_is_line_terminator (code_unit)) - { - break; - } - lit_put_ecma_char (code_unit); - } - return; - } - - code_unit = lit_utf8_iterator_read_next (&iter); - - if (lit_char_is_line_terminator (code_unit)) - { - l++; - if (code_unit == LIT_CHAR_CR - && !lit_utf8_iterator_is_eos (&iter) - && lit_utf8_iterator_peek_next (&iter) == LIT_CHAR_LF) - { - lit_utf8_iterator_incr (&iter); - } - } - } -} /* lexer_dump_line */ - -/** - * Convert token type to string - * - * @return string, describing token type - */ -const char * -lexer_token_type_to_string (jsp_token_type_t tt) -{ - switch (tt) - { - case TOK_EOF: return "End of file"; - case TOK_NAME: return "Identifier"; - case TOK_SMALL_INT: /* FALLTHRU */ - case TOK_NUMBER: return "Number"; - case TOK_REGEXP: return "RegExp"; - - case TOK_NULL: return "null"; - case TOK_BOOL: return "bool"; - case TOK_STRING: return "string"; - case TOK_OPEN_BRACE: return "{"; - - case TOK_CLOSE_BRACE: return "}"; - case TOK_OPEN_PAREN: return "("; - case TOK_CLOSE_PAREN: return ")"; - case TOK_OPEN_SQUARE: return "["; - case TOK_CLOSE_SQUARE: return "]"; - - case TOK_DOT: return "."; - case TOK_SEMICOLON: return ";"; - case TOK_COMMA: return ","; - case TOK_LESS: return "<"; - case TOK_GREATER: return ">"; - - case TOK_LESS_EQ: return "<="; - case TOK_GREATER_EQ: return "<="; - case TOK_DOUBLE_EQ: return "=="; - case TOK_NOT_EQ: return "!="; - case TOK_TRIPLE_EQ: return "==="; - - case TOK_NOT_DOUBLE_EQ: return "!=="; - case TOK_PLUS: return "+"; - case TOK_MINUS: return "-"; - case TOK_MULT: return "*"; - case TOK_MOD: return "%%"; - - case TOK_DOUBLE_PLUS: return "++"; - case TOK_DOUBLE_MINUS: return "--"; - case TOK_LSHIFT: return "<<"; - case TOK_RSHIFT: return ">>"; - case TOK_RSHIFT_EX: return ">>>"; - - case TOK_AND: return "&"; - case TOK_OR: return "|"; - case TOK_XOR: return "^"; - case TOK_NOT: return "!"; - case TOK_COMPL: return "~"; - - case TOK_DOUBLE_AND: return "&&"; - case TOK_DOUBLE_OR: return "||"; - case TOK_QUERY: return "?"; - case TOK_COLON: return ":"; - case TOK_EQ: return "="; - - case TOK_PLUS_EQ: return "+="; - case TOK_MINUS_EQ: return "-="; - case TOK_MULT_EQ: return "*="; - case TOK_MOD_EQ: return "%%="; - case TOK_LSHIFT_EQ: return "<<="; - - case TOK_RSHIFT_EQ: return ">>="; - case TOK_RSHIFT_EX_EQ: return ">>>="; - case TOK_AND_EQ: return "&="; - case TOK_OR_EQ: return "|="; - case TOK_XOR_EQ: return "^="; - - case TOK_DIV: return "/"; - case TOK_DIV_EQ: return "/="; - case TOK_KW_BREAK: return "break"; - case TOK_KW_CASE: return "case"; - case TOK_KW_CATCH: return "catch"; - case TOK_KW_CLASS: return "class"; - - case TOK_KW_CONST: return "const"; - case TOK_KW_CONTINUE: return "continue"; - case TOK_KW_DEBUGGER: return "debugger"; - case TOK_KW_DEFAULT: return "default"; - case TOK_KW_DELETE: return "delete"; - - case TOK_KW_DO: return "do"; - case TOK_KW_ELSE: return "else"; - case TOK_KW_ENUM: return "enum"; - case TOK_KW_EXPORT: return "export"; - case TOK_KW_EXTENDS: return "extends"; - - case TOK_KW_FINALLY: return "finally"; - case TOK_KW_FOR: return "for"; - case TOK_KW_FUNCTION: return "function"; - case TOK_KW_IF: return "if"; - case TOK_KW_IN: return "in"; - - case TOK_KW_INSTANCEOF: return "instanceof"; - case TOK_KW_INTERFACE: return "interface"; - case TOK_KW_IMPORT: return "import"; - case TOK_KW_IMPLEMENTS: return "implements"; - case TOK_KW_LET: return "let"; - - case TOK_KW_NEW: return "new"; - case TOK_KW_PACKAGE: return "package"; - case TOK_KW_PRIVATE: return "private"; - case TOK_KW_PROTECTED: return "protected"; - case TOK_KW_PUBLIC: return "public"; - - case TOK_KW_RETURN: return "return"; - case TOK_KW_STATIC: return "static"; - case TOK_KW_SUPER: return "super"; - case TOK_KW_SWITCH: return "switch"; - case TOK_KW_THIS: return "this"; - - case TOK_KW_THROW: return "throw"; - case TOK_KW_TRY: return "try"; - case TOK_KW_TYPEOF: return "typeof"; - case TOK_KW_VAR: return "var"; - case TOK_KW_VOID: return "void"; - - case TOK_KW_WHILE: return "while"; - case TOK_KW_WITH: return "with"; - case TOK_KW_YIELD: return "yield"; - default: JERRY_UNREACHABLE (); - } -} /* lexer_token_type_to_string */ - -/** - * Get type of specified token - * - * @return token type - */ -jsp_token_type_t __attr_always_inline___ -lexer_get_token_type (token t) /**< the token */ -{ - JERRY_ASSERT (t.type >= TOKEN_TYPE__BEGIN && t.type <= TOKEN_TYPE__END); - - return (jsp_token_type_t) t.type; -} /* lexer_get_token_type */ - -bool __attr_always_inline___ -lexer_is_preceded_by_newlines (token t) -{ - return ((t.flags & JSP_TOKEN_FLAG_PRECEDED_BY_NEWLINES) != 0); -} /* lexer_is_preceded_by_newlines */ - -/** - * Check whether the identifier tokens represent the same identifiers - * - * Note: - * As all literals represent unique strings, - * it is sufficient to just check if literal indices - * in the tokens are equal. - * - * @return true / false - */ -bool -lexer_are_tokens_with_same_identifier (token id1, /**< identifier token (TOK_NAME) */ - token id2) /**< identifier token (TOK_NAME) */ -{ - JERRY_ASSERT (lexer_get_token_type (id1) == TOK_NAME); - JERRY_ASSERT (lexer_get_token_type (id2) == TOK_NAME); - - return (id1.uid == id2.uid); -} /* lexer_are_tokens_with_same_identifier */ - -/** - * Checks that TOK_STRING doesn't contain EscapeSequence or LineContinuation - * - * @return true, if token's string in source buffer doesn't contain backslash - * false, otherwise - */ -bool -lexer_is_no_escape_sequences_in_token_string (token tok) /**< token of type TOK_STRING */ -{ - JERRY_ASSERT (lexer_get_token_type (tok) == TOK_STRING); - - lit_utf8_iterator_t iter = src_iter; - lit_utf8_iterator_seek (&iter, tok.loc); - - JERRY_ASSERT (!lit_utf8_iterator_is_eos (&iter)); - ecma_char_t c = lit_utf8_iterator_read_next (&iter); - JERRY_ASSERT (c == LIT_CHAR_SINGLE_QUOTE - || c == LIT_CHAR_DOUBLE_QUOTE); - - const ecma_char_t end_char = c; - - do - { - JERRY_ASSERT (!lit_utf8_iterator_is_eos (&iter)); - c = lit_utf8_iterator_read_next (&iter); - - if (c == LIT_CHAR_BACKSLASH) - { - return false; - } - } - while (c != end_char); - - return true; -} /* lexer_is_no_escape_sequences_in_token_string */ - -/** - * Initialize lexer to start parsing of a new source - */ -void -lexer_init (const jerry_api_char_t *source, /**< script source */ - size_t source_size, /**< script source size in bytes */ - bool is_print_source_code) /**< flag indicating whether to dump - * processed source code */ -{ - empty_token.type = TOK_EMPTY; - empty_token.uid = 0; - empty_token.loc = LIT_ITERATOR_POS_ZERO; - - saved_token = prev_token = sent_token = empty_token; - - if (!lit_is_utf8_string_valid (source, (lit_utf8_size_t) source_size)) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid source encoding", LIT_ITERATOR_POS_ZERO); - } - - src_iter = lit_utf8_iterator_create (source, (lit_utf8_size_t) source_size); - - buffer_size = source_size; - buffer_start = source; - is_token_parse_in_progress = false; - -#ifndef JERRY_NDEBUG - allow_dump_lines = is_print_source_code; -#else /* JERRY_NDEBUG */ - (void) is_print_source_code; - allow_dump_lines = false; -#endif /* JERRY_NDEBUG */ -} /* lexer_init */ diff --git a/jerry-core/parser/js/lexer.h b/jerry-core/parser/js/lexer.h deleted file mode 100644 index 922f8bedfb..0000000000 --- a/jerry-core/parser/js/lexer.h +++ /dev/null @@ -1,282 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LEXER_H -#define LEXER_H - -#include "lit-literal.h" -#include "lit-strings.h" - -#define INVALID_LITERAL (rcs_cpointer_null_cp ()) - -/* Keywords. */ -typedef enum __attr_packed___ -{ - /* Not a keyword. */ - KW_BREAK, - KW_CASE, - KW_CATCH, - KW_CLASS, - - KW_CONST, - KW_CONTINUE, - KW_DEBUGGER, - KW_DEFAULT, - KW_DELETE, - - KW_DO, - KW_ELSE, - KW_ENUM, - KW_EXPORT, - KW_EXTENDS, - - KW_FINALLY, - KW_FOR, - KW_FUNCTION, - KW_IF, - KW_IN, - - KW_INSTANCEOF, - KW_INTERFACE, - KW_IMPORT, - KW_IMPLEMENTS, - KW_LET, - - KW_NEW, - KW_PACKAGE, - KW_PRIVATE, - KW_PROTECTED, - KW_PUBLIC, - - KW_RETURN, - KW_STATIC, - KW_SUPER, - KW_SWITCH, - KW_THIS, - - KW_THROW, - KW_TRY, - KW_TYPEOF, - KW_VAR, - KW_VOID, - - KW_WHILE, - KW_WITH, - KW_YIELD -} keyword; - - -/* Type of tokens. */ -typedef enum __attr_packed___ -{ - TOKEN_TYPE__BEGIN = 1, - - TOK_EOF = TOKEN_TYPE__BEGIN, // End of file - TOK_NAME, // Identifier - TOK_SMALL_INT, - TOK_NUMBER, - - TOK_NULL, - TOK_BOOL, - TOK_STRING, - TOK_OPEN_BRACE, // { - - TOK_CLOSE_BRACE, // } - TOK_OPEN_PAREN, // ( - TOK_CLOSE_PAREN, //) - TOK_OPEN_SQUARE, // [ - TOK_CLOSE_SQUARE, // ] - - TOK_DOT, // . - TOK_SEMICOLON, // ; - TOK_COMMA, // , - - TOKEN_TYPE__UNARY_BEGIN, - TOKEN_TYPE__ADDITIVE_BEGIN = TOKEN_TYPE__UNARY_BEGIN, - - TOK_PLUS = TOKEN_TYPE__ADDITIVE_BEGIN, // + - TOK_MINUS, // - - - TOKEN_TYPE__ADDITIVE_END = TOK_MINUS, - - TOK_DOUBLE_PLUS, // ++ - TOK_DOUBLE_MINUS, // -- - TOK_NOT, // ! - TOK_COMPL, // ~ - - TOKEN_TYPE__UNARY_END = TOK_COMPL, /* keywords are not listed - * in the range */ - - TOKEN_TYPE__MULTIPLICATIVE_BEGIN, - - TOK_MULT = TOKEN_TYPE__MULTIPLICATIVE_BEGIN, // * - TOK_MOD, // % - TOK_DIV, // / - - TOKEN_TYPE__MULTIPLICATIVE_END = TOK_DIV, - - TOKEN_TYPE__SHIFT_BEGIN, - - TOK_LSHIFT = TOKEN_TYPE__SHIFT_BEGIN, // << - TOK_RSHIFT, // >> - TOK_RSHIFT_EX, // >>> - - TOKEN_TYPE__SHIFT_END = TOK_RSHIFT_EX, - - TOKEN_TYPE__RELATIONAL_BEGIN, - - TOK_LESS = TOKEN_TYPE__RELATIONAL_BEGIN, // < - TOK_GREATER, // > - TOK_LESS_EQ, // <= - TOK_GREATER_EQ, // <= - - TOKEN_TYPE__RELATIONAL_END = TOK_GREATER_EQ, - - TOKEN_TYPE__EQUALITY_BEGIN, - - TOK_DOUBLE_EQ = TOKEN_TYPE__EQUALITY_BEGIN, // == - TOK_NOT_EQ, // != - TOK_TRIPLE_EQ, // === - TOK_NOT_DOUBLE_EQ, // !== - - TOKEN_TYPE__EQUALITY_END = TOK_NOT_DOUBLE_EQ, - - TOK_AND, // & - TOK_OR, // | - TOK_XOR, // ^ - - TOK_DOUBLE_AND, // && - TOK_DOUBLE_OR, // || - TOK_QUERY, // ? - TOK_COLON, // : - - TOKEN_TYPE__ASSIGNMENTS_BEGIN, - - TOK_EQ = TOKEN_TYPE__ASSIGNMENTS_BEGIN, // = - TOK_PLUS_EQ, // += - TOK_MINUS_EQ, // -= - TOK_MULT_EQ, // *= - TOK_MOD_EQ, // %= - TOK_LSHIFT_EQ, // <<= - TOK_RSHIFT_EQ, // >>= - TOK_RSHIFT_EX_EQ, // >>>= - TOK_AND_EQ, // &= - TOK_OR_EQ, // |= - TOK_XOR_EQ, // ^= - TOK_DIV_EQ, // /= - - TOKEN_TYPE__ASSIGNMENTS_END = TOK_DIV_EQ, - - TOK_EMPTY, - TOK_REGEXP, // RegularExpressionLiteral (/.../gim) - - TOKEN_TYPE__KEYWORD_BEGIN, - - TOK_KW_BREAK, - TOK_KW_CASE, - TOK_KW_CATCH, - TOK_KW_CLASS, - - TOK_KW_CONST, - TOK_KW_CONTINUE, - TOK_KW_DEBUGGER, - TOK_KW_DEFAULT, - TOK_KW_DELETE, - - TOK_KW_DO, - TOK_KW_ELSE, - TOK_KW_ENUM, - TOK_KW_EXPORT, - TOK_KW_EXTENDS, - - TOK_KW_FINALLY, - TOK_KW_FOR, - TOK_KW_FUNCTION, - TOK_KW_IF, - TOK_KW_IN, - - TOK_KW_INSTANCEOF, - TOK_KW_INTERFACE, - TOK_KW_IMPORT, - TOK_KW_IMPLEMENTS, - TOK_KW_LET, - - TOK_KW_NEW, - TOK_KW_PACKAGE, - TOK_KW_PRIVATE, - TOK_KW_PROTECTED, - TOK_KW_PUBLIC, - - TOK_KW_RETURN, - TOK_KW_STATIC, - TOK_KW_SUPER, - TOK_KW_SWITCH, - TOK_KW_THIS, - - TOK_KW_THROW, - TOK_KW_TRY, - TOK_KW_TYPEOF, - TOK_KW_VAR, - TOK_KW_VOID, - - TOK_KW_WHILE, - TOK_KW_WITH, - TOK_KW_YIELD, - - TOKEN_TYPE__KEYWORD_END = TOK_KW_YIELD, - - TOKEN_TYPE__END = TOKEN_TYPE__KEYWORD_END -} jsp_token_type_t; - -/* Flags, describing token properties */ -typedef enum -{ - JSP_TOKEN_FLAG__NO_FLAGS = 0x00, /* default flag */ - JSP_TOKEN_FLAG_PRECEDED_BY_NEWLINES = 0x01 /* designates that newline precedes the token */ -} jsp_token_flag_t; - -typedef lit_utf8_iterator_pos_t locus; - -/* Represents the contents of a token. */ -typedef struct -{ - locus loc; /**< token location */ - uint16_t uid; /**< encodes token's value, depending on token type */ - uint8_t type; /**< token type */ - uint8_t flags; /**< token flags */ -} token; - -/** - * Initializer for empty token - */ -#define TOKEN_EMPTY_INITIALIZER {LIT_ITERATOR_POS_ZERO, 0, TOK_EMPTY, JSP_TOKEN_FLAG__NO_FLAGS} - -void lexer_init (const jerry_api_char_t *, size_t, bool); - -token lexer_next_token (bool, bool); - -void lexer_seek (locus); -void lexer_locus_to_line_and_column (locus, size_t *, size_t *); -void lexer_dump_line (size_t); -const char *lexer_token_type_to_string (jsp_token_type_t); - -jsp_token_type_t lexer_get_token_type (token); -bool lexer_is_preceded_by_newlines (token); - -extern bool lexer_are_tokens_with_same_identifier (token, token); - -extern bool lexer_is_no_escape_sequences_in_token_string (token); - -#endif diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp deleted file mode 100644 index 5cc3839ee9..0000000000 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ /dev/null @@ -1,2760 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jsp-early-error.h" -#include "opcodes-dumper.h" -#include "pretty-printer.h" -#include "bytecode-data.h" - -/** - * Register allocator's counter - */ -static vm_idx_t jsp_reg_next; - -/** - * Maximum identifier of a register, allocated for intermediate value storage - * - * See also: - * dumper_save_reg_alloc_ctx, dumper_restore_reg_alloc_ctx - */ -static vm_idx_t jsp_reg_max_for_temps; - -/** - * Maximum identifier of a register, allocated for storage of a variable value. - * - * The value can be VM_IDX_EMPTY, indicating that no registers were allocated for variable values. - * - * Note: - * Registers for variable values are always allocated after registers for temporary values, - * so the value, if not equal to VM_IDX_EMPTY, is always greater than jsp_reg_max_for_temps. - * - * See also: - * dumper_try_replace_identifier_name_with_reg - */ -static vm_idx_t jsp_reg_max_for_local_var; - -/** - * Maximum identifier of a register, allocated for storage of an argument value. - * - * The value can be VM_IDX_EMPTY, indicating that no registers were allocated for argument values. - * - * Note: - * Registers for argument values are always allocated after registers for variable values, - * so the value, if not equal to VM_IDX_EMPTY, is always greater than jsp_reg_max_for_local_var. - * - * See also: - * dumper_try_replace_identifier_name_with_reg - */ -static vm_idx_t jsp_reg_max_for_args; - -/** - * Flag, indicating if instructions should be printed - */ -bool is_print_instrs = false; - -/** - * Construct uninitialized operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_uninitialized_operand (void) -{ - jsp_operand_t ret; - ret.type = JSP_OPERAND_TYPE_UNINITIALIZED; - - return ret; -} /* jsp_make_uninitialized_operand */ - -/** - * Construct empty operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_empty_operand (void) -{ - jsp_operand_t ret; - ret.type = JSP_OPERAND_TYPE_EMPTY; - - return ret; -} /* jsp_make_empty_operand */ - -/** - * Construct ThisBinding operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_this_operand (void) -{ - jsp_operand_t ret; - ret.type = JSP_OPERAND_TYPE_THIS_BINDING; - - return ret; -} /* jsp_make_this_operand */ - -/** - * Construct unknown operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_unknown_operand (void) -{ - jsp_operand_t ret; - ret.type = JSP_OPERAND_TYPE_UNKNOWN; - - return ret; -} /* jsp_make_unknown_operand */ - -/** - * Construct idx-constant operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_idx_const_operand (vm_idx_t cnst) /**< integer in vm_idx_t range */ -{ - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_IDX_CONST; - ret.data.idx_const = cnst; - - return ret; -} /* jsp_make_idx_const_operand */ - -/** - * Construct small integer operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_smallint_operand (uint8_t integer_value) /**< small integer value */ -{ - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_SMALLINT; - ret.data.smallint_value = integer_value; - - return ret; -} /* jsp_make_smallint_operand */ - -/** - * Construct simple ecma value operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_simple_value_operand (ecma_simple_value_t simple_value) /**< simple ecma value */ -{ - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_SIMPLE_VALUE; - ret.data.simple_value = simple_value; - - return ret; -} /* jsp_make_simple_value_operand */ - -/** - * Construct string literal operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_string_lit_operand (lit_cpointer_t lit_id) /**< literal identifier */ -{ - JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); - -#ifndef JERRY_NDEBUG - lit_literal_t lit = lit_get_literal_by_cp (lit_id); - - JERRY_ASSERT (RCS_RECORD_IS_CHARSET (lit) - || RCS_RECORD_IS_MAGIC_STR (lit) - || RCS_RECORD_IS_MAGIC_STR_EX (lit)); -#endif /* !JERRY_NDEBUG */ - - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_STRING_LITERAL; - ret.data.lit_id = lit_id; - - return ret; -} /* jsp_make_string_lit_operand */ - -/** - * Construct RegExp literal operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_regexp_lit_operand (lit_cpointer_t lit_id) /**< literal identifier */ -{ - JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); - -#ifndef JERRY_NDEBUG - lit_literal_t lit = lit_get_literal_by_cp (lit_id); - - JERRY_ASSERT (RCS_RECORD_IS_CHARSET (lit) - || RCS_RECORD_IS_MAGIC_STR (lit) - || RCS_RECORD_IS_MAGIC_STR_EX (lit)); -#endif /* !JERRY_NDEBUG */ - - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_REGEXP_LITERAL; - ret.data.lit_id = lit_id; - - return ret; -} /* jsp_make_regexp_lit_operand */ - -/** - * Construct number literal operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_number_lit_operand (lit_cpointer_t lit_id) /**< literal identifier */ -{ - JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); - -#ifndef JERRY_NDEBUG - lit_literal_t lit = lit_get_literal_by_cp (lit_id); - - JERRY_ASSERT (RCS_RECORD_IS_NUMBER (lit)); -#endif /* !JERRY_NDEBUG */ - - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_NUMBER_LITERAL; - ret.data.lit_id = lit_id; - - return ret; -} /* jsp_make_number_lit_operand */ - -/** - * Construct identifier reference operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_identifier_operand (lit_cpointer_t lit_id) /**< literal identifier */ -{ - JERRY_ASSERT (lit_id.packed_value != NOT_A_LITERAL.packed_value); - - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_IDENTIFIER; - ret.data.identifier = lit_id; - - return ret; -} /* jsp_make_identifier_operand */ - -/** - * Construct register operand - * - * @return constructed operand - */ -jsp_operand_t -jsp_make_reg_operand (vm_idx_t reg_index) /**< register index */ -{ - /* - * The following check currently leads to 'comparison is always true - * due to limited range of data type' warning, so it is turned off. - * - * If VM_IDX_GENERAL_VALUE_FIRST is changed to value greater than 0, - * the check should be restored. - */ - // JERRY_ASSERT (reg_index >= VM_IDX_GENERAL_VALUE_FIRST); - JERRY_STATIC_ASSERT (VM_IDX_GENERAL_VALUE_FIRST == 0); - - JERRY_ASSERT (reg_index <= VM_IDX_GENERAL_VALUE_LAST); - - jsp_operand_t ret; - - ret.type = JSP_OPERAND_TYPE_TMP; - ret.data.uid = reg_index; - - return ret; -} /* jsp_make_reg_operand */ - -/** - * Is it empty operand? - * - * @return true / false - */ -bool -jsp_is_empty_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_EMPTY); -} /* jsp_is_empty_operand */ - -/** - * Is it ThisBinding operand? - * - * @return true / false - */ -bool -jsp_is_this_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_THIS_BINDING); -} /* jsp_is_this_operand */ - -/** - * Is it unknown operand? - * - * @return true / false - */ -bool -jsp_is_unknown_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_UNKNOWN); -} /* jsp_is_unknown_operand */ - -/** - * Is it idx-constant operand? - * - * @return true / false - */ -bool -jsp_is_idx_const_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_IDX_CONST); -} /* jsp_is_idx_const_operand */ - -/** - * Is it byte-code register operand? - * - * @return true / false - */ -bool -jsp_is_register_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_TMP); -} /* jsp_is_register_operand */ - -/** - * Is it simple ecma value operand? - * - * @return true / false - */ -bool -jsp_is_simple_value_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_SIMPLE_VALUE); -} /* jsp_is_simple_value_operand */ - -/** - * Is it small integer operand? - * - * @return true / false - */ -bool -jsp_is_smallint_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_SMALLINT); -} /* jsp_is_smallint_operand */ - -/** - * Is it number literal operand? - * - * @return true / false - */ -bool -jsp_is_number_lit_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_NUMBER_LITERAL); -} /* jsp_is_number_lit_operand */ - -/** - * Is it string literal operand? - * - * @return true / false - */ -bool -jsp_is_string_lit_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_STRING_LITERAL); -} /* jsp_is_string_lit_operand */ - -/** - * Is it RegExp literal operand? - * - * @return true / false - */ -bool -jsp_is_regexp_lit_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_REGEXP_LITERAL); -} /* jsp_is_regexp_lit_operand */ - -/** - * Is it identifier reference operand? - * - * @return true / false - */ -bool -jsp_is_identifier_operand (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - return (operand.type == JSP_OPERAND_TYPE_IDENTIFIER); -} /* jsp_is_identifier_operand */ - -/** - * Get string literal - name of Identifier reference - * - * @return literal identifier - */ -lit_cpointer_t -jsp_operand_get_identifier_name (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (jsp_is_identifier_operand (operand)); - - return (operand.data.identifier); -} /* jsp_operand_get_identifier_name */ - -/** - * Get idx for operand - * - * @return VM_IDX_REWRITE_LITERAL_UID (for LITERAL), - * or register index (for TMP). - */ -vm_idx_t -jsp_operand_get_idx (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - if (operand.type == JSP_OPERAND_TYPE_TMP) - { - return operand.data.uid; - } - - if (operand.type == JSP_OPERAND_TYPE_STRING_LITERAL || operand.type == JSP_OPERAND_TYPE_NUMBER_LITERAL) - { - return VM_IDX_REWRITE_LITERAL_UID; - } - - if (operand.type == JSP_OPERAND_TYPE_THIS_BINDING) - { - return VM_REG_SPECIAL_THIS_BINDING; - } - - JERRY_ASSERT (operand.type == JSP_OPERAND_TYPE_EMPTY); - return VM_IDX_EMPTY; -} /* jsp_operand_get_idx */ - -/** - * Get literal from operand - * - * @return literal identifier (for LITERAL), - * or NOT_A_LITERAL (for TMP). - */ -lit_cpointer_t -jsp_operand_get_literal (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (operand.type != JSP_OPERAND_TYPE_UNINITIALIZED); - - if (operand.type == JSP_OPERAND_TYPE_TMP) - { - return NOT_A_LITERAL; - } - - if (operand.type == JSP_OPERAND_TYPE_STRING_LITERAL - || operand.type == JSP_OPERAND_TYPE_NUMBER_LITERAL - || operand.type == JSP_OPERAND_TYPE_REGEXP_LITERAL) - { - return operand.data.lit_id; - } - - JERRY_ASSERT (operand.type == JSP_OPERAND_TYPE_EMPTY); - return NOT_A_LITERAL; -} /* jsp_operand_get_literal */ - -/** - * Get constant from idx-constant operand - * - * @return an integer - */ -vm_idx_t -jsp_operand_get_idx_const (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (jsp_is_idx_const_operand (operand)); - - return operand.data.idx_const; -} /* jsp_operand_get_idx_const */ - -/** - * Get small integer constant from operand - * - * @return an integer - */ -uint8_t -jsp_operand_get_smallint_value (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (jsp_is_smallint_operand (operand)); - - return operand.data.smallint_value; -} /* jsp_operand_get_smallint_value */ - -/** - * Get simple value from operand - * - * @return a simple ecma value - */ -ecma_simple_value_t -jsp_operand_get_simple_value (jsp_operand_t operand) /**< operand */ -{ - JERRY_ASSERT (jsp_is_simple_value_operand (operand)); - - return (ecma_simple_value_t) operand.data.simple_value; -} /* jsp_operand_get_simple_value */ - -/** - * Determine if operand with specified index could be encoded as a literal - * - * @return true, if operand could be literal - * false, otherwise - */ -static bool -is_possible_literal (uint16_t mask, /**< mask encoding which operands could be literals */ - uint8_t index) /**< index of operand */ -{ - int res; - switch (index) - { - case 0: - { - res = mask >> 8; - break; - } - case 1: - { - res = (mask & 0xF0) >> 4; - break; - } - default: - { - JERRY_ASSERT (index = 2); - res = mask & 0x0F; - } - } - JERRY_ASSERT (res == 0 || res == 1); - return res == 1; -} /* is_possible_literal */ - -/** - * Get specified operand value from instruction - * - * @return operand value - */ -static vm_idx_t -get_uid (op_meta *op, /**< intermediate instruction description, from which operand should be extracted */ - size_t i) /**< index of the operand */ -{ - JERRY_ASSERT (i < 3); - return op->op.data.raw_args[i]; -} /* get_uid */ - -/** - * Insert literals from instruction to array of seen literals - * This is needed for prediction of bytecode's hash table size - */ -static void -insert_uids_to_lit_id_map (jsp_ctx_t *ctx_p, /**< parser context */ - op_meta *om, /**< intermediate instruction description */ - uint16_t mask) /**< mask of possibe literal operands */ -{ - for (uint8_t i = 0; i < 3; i++) - { - if (is_possible_literal (mask, i)) - { - if (get_uid (om, i) == VM_IDX_REWRITE_LITERAL_UID) - { - JERRY_ASSERT (om->lit_id[i].packed_value != MEM_CP_NULL); - - jsp_account_next_bytecode_to_literal_reference (ctx_p, om->lit_id[i]); - } - else - { - JERRY_ASSERT (om->lit_id[i].packed_value == MEM_CP_NULL); - } - } - else - { - JERRY_ASSERT (om->lit_id[i].packed_value == MEM_CP_NULL); - } - } -} - -/** - * Count number of literals in instruction which were not seen previously - */ -static void -count_new_literals_in_instr (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t instr_pos, /**< position of instruction */ - op_meta *om_p) /**< instruction */ -{ - if (instr_pos % BLOCK_SIZE == 0) - { - jsp_empty_tmp_literal_set (ctx_p); - } - - switch (om_p->op.op_idx) - { - case VM_OP_PROP_GETTER: - case VM_OP_PROP_SETTER: - case VM_OP_DELETE_PROP: - case VM_OP_B_SHIFT_LEFT: - case VM_OP_B_SHIFT_RIGHT: - case VM_OP_B_SHIFT_URIGHT: - case VM_OP_B_AND: - case VM_OP_B_OR: - case VM_OP_B_XOR: - case VM_OP_EQUAL_VALUE: - case VM_OP_NOT_EQUAL_VALUE: - case VM_OP_EQUAL_VALUE_TYPE: - case VM_OP_NOT_EQUAL_VALUE_TYPE: - case VM_OP_LESS_THAN: - case VM_OP_GREATER_THAN: - case VM_OP_LESS_OR_EQUAL_THAN: - case VM_OP_GREATER_OR_EQUAL_THAN: - case VM_OP_INSTANCEOF: - case VM_OP_IN: - case VM_OP_ADDITION: - case VM_OP_SUBSTRACTION: - case VM_OP_DIVISION: - case VM_OP_MULTIPLICATION: - case VM_OP_REMAINDER: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x111); - break; - } - case VM_OP_CALL_N: - case VM_OP_CONSTRUCT_N: - case VM_OP_FUNC_EXPR_N: - case VM_OP_DELETE_VAR: - case VM_OP_TYPEOF: - case VM_OP_B_NOT: - case VM_OP_LOGICAL_NOT: - case VM_OP_POST_INCR: - case VM_OP_POST_DECR: - case VM_OP_PRE_INCR: - case VM_OP_PRE_DECR: - case VM_OP_UNARY_PLUS: - case VM_OP_UNARY_MINUS: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x110); - break; - } - case VM_OP_ASSIGNMENT: - { - switch (om_p->op.data.assignment.type_value_right) - { - case OPCODE_ARG_TYPE_SIMPLE: - case OPCODE_ARG_TYPE_SMALLINT: - case OPCODE_ARG_TYPE_SMALLINT_NEGATE: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x100); - break; - } - case OPCODE_ARG_TYPE_NUMBER: - case OPCODE_ARG_TYPE_NUMBER_NEGATE: - case OPCODE_ARG_TYPE_REGEXP: - case OPCODE_ARG_TYPE_STRING: - case OPCODE_ARG_TYPE_VARIABLE: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x101); - break; - } - } - break; - } - case VM_OP_FUNC_DECL_N: - case VM_OP_FUNC_EXPR_REF: - case VM_OP_FOR_IN: - case VM_OP_ARRAY_DECL: - case VM_OP_OBJ_DECL: - case VM_OP_WITH: - case VM_OP_THROW_VALUE: - case VM_OP_IS_TRUE_JMP_UP: - case VM_OP_IS_TRUE_JMP_DOWN: - case VM_OP_IS_FALSE_JMP_UP: - case VM_OP_IS_FALSE_JMP_DOWN: - case VM_OP_VAR_DECL: - case VM_OP_RETVAL: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x100); - break; - } - case VM_OP_RET: - case VM_OP_TRY_BLOCK: - case VM_OP_JMP_UP: - case VM_OP_JMP_DOWN: - case VM_OP_JMP_BREAK_CONTINUE: - case VM_OP_REG_VAR_DECL: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x000); - break; - } - case VM_OP_META: - { - switch (om_p->op.data.meta.type) - { - case OPCODE_META_TYPE_VARG_PROP_DATA: - case OPCODE_META_TYPE_VARG_PROP_GETTER: - case OPCODE_META_TYPE_VARG_PROP_SETTER: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x011); - break; - } - case OPCODE_META_TYPE_VARG: - case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x010); - break; - } - case OPCODE_META_TYPE_UNDEFINED: - case OPCODE_META_TYPE_END_WITH: - case OPCODE_META_TYPE_FUNCTION_END: - case OPCODE_META_TYPE_CATCH: - case OPCODE_META_TYPE_FINALLY: - case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: - case OPCODE_META_TYPE_CALL_SITE_INFO: - { - insert_uids_to_lit_id_map (ctx_p, om_p, 0x000); - break; - } - } - break; - } - - default: - { - JERRY_UNREACHABLE (); - } - } -} /* count_new_literals_in_instr */ - -/** - * Allocate next register for intermediate value - * - * @return identifier of the allocated register - */ -static vm_idx_t -jsp_alloc_reg_for_temp (void) -{ - JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); - JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); - - vm_idx_t next_reg = jsp_reg_next++; - - if (next_reg > VM_REG_GENERAL_LAST) - { - /* - * FIXME: - * Implement mechanism, allowing reusage of register variables - */ - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Not enough register variables", LIT_ITERATOR_POS_ZERO); - } - - if (jsp_reg_max_for_temps < next_reg) - { - jsp_reg_max_for_temps = next_reg; - } - - return next_reg; -} /* jsp_alloc_reg_for_temp */ - -/** - * Get current instruction counter - * - * @return number of currently generated instructions - */ -vm_instr_counter_t -dumper_get_current_instr_counter (jsp_ctx_t *ctx_p) /**< parser context */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - return bc_header_p->instrs_count; - } - else - { - return scopes_tree_instrs_num (jsp_get_current_scopes_tree_node (ctx_p)); - } -} /* dumper_get_current_instr_counter */ - -/** - * Convert instruction at the specified position to intermediate instruction description - * - * @return intermediate instruction description - */ -op_meta -dumper_get_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos) /**< instruction position */ -{ - op_meta opm; - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - JERRY_ASSERT (pos < bc_header_p->instrs_count); - - vm_instr_t *instr_p = bc_header_p->instrs_p + pos; - - opm.op = *instr_p; - - uint16_t mask; - - switch (opm.op.op_idx) - { - case VM_OP_PROP_GETTER: - case VM_OP_PROP_SETTER: - case VM_OP_DELETE_PROP: - case VM_OP_B_SHIFT_LEFT: - case VM_OP_B_SHIFT_RIGHT: - case VM_OP_B_SHIFT_URIGHT: - case VM_OP_B_AND: - case VM_OP_B_OR: - case VM_OP_B_XOR: - case VM_OP_EQUAL_VALUE: - case VM_OP_NOT_EQUAL_VALUE: - case VM_OP_EQUAL_VALUE_TYPE: - case VM_OP_NOT_EQUAL_VALUE_TYPE: - case VM_OP_LESS_THAN: - case VM_OP_GREATER_THAN: - case VM_OP_LESS_OR_EQUAL_THAN: - case VM_OP_GREATER_OR_EQUAL_THAN: - case VM_OP_INSTANCEOF: - case VM_OP_IN: - case VM_OP_ADDITION: - case VM_OP_SUBSTRACTION: - case VM_OP_DIVISION: - case VM_OP_MULTIPLICATION: - case VM_OP_REMAINDER: - { - mask = 0x111; - break; - } - case VM_OP_CALL_N: - case VM_OP_CONSTRUCT_N: - case VM_OP_FUNC_EXPR_N: - case VM_OP_DELETE_VAR: - case VM_OP_TYPEOF: - case VM_OP_B_NOT: - case VM_OP_LOGICAL_NOT: - case VM_OP_POST_INCR: - case VM_OP_POST_DECR: - case VM_OP_PRE_INCR: - case VM_OP_PRE_DECR: - case VM_OP_UNARY_PLUS: - case VM_OP_UNARY_MINUS: - { - mask = 0x110; - break; - } - case VM_OP_ASSIGNMENT: - { - switch (opm.op.data.assignment.type_value_right) - { - case OPCODE_ARG_TYPE_SIMPLE: - case OPCODE_ARG_TYPE_SMALLINT: - case OPCODE_ARG_TYPE_SMALLINT_NEGATE: - { - mask = 0x100; - break; - } - case OPCODE_ARG_TYPE_NUMBER: - case OPCODE_ARG_TYPE_NUMBER_NEGATE: - case OPCODE_ARG_TYPE_REGEXP: - case OPCODE_ARG_TYPE_STRING: - case OPCODE_ARG_TYPE_VARIABLE: - { - mask = 0x101; - break; - } - default: - { - JERRY_UNREACHABLE (); - } - } - break; - } - case VM_OP_FUNC_DECL_N: - case VM_OP_FUNC_EXPR_REF: - case VM_OP_FOR_IN: - case VM_OP_ARRAY_DECL: - case VM_OP_OBJ_DECL: - case VM_OP_WITH: - case VM_OP_THROW_VALUE: - case VM_OP_IS_TRUE_JMP_UP: - case VM_OP_IS_TRUE_JMP_DOWN: - case VM_OP_IS_FALSE_JMP_UP: - case VM_OP_IS_FALSE_JMP_DOWN: - case VM_OP_VAR_DECL: - case VM_OP_RETVAL: - { - mask = 0x100; - break; - } - case VM_OP_RET: - case VM_OP_TRY_BLOCK: - case VM_OP_JMP_UP: - case VM_OP_JMP_DOWN: - case VM_OP_JMP_BREAK_CONTINUE: - case VM_OP_REG_VAR_DECL: - { - mask = 0x000; - break; - } - case VM_OP_META: - { - switch (opm.op.data.meta.type) - { - case OPCODE_META_TYPE_VARG_PROP_DATA: - case OPCODE_META_TYPE_VARG_PROP_GETTER: - case OPCODE_META_TYPE_VARG_PROP_SETTER: - { - mask = 0x011; - break; - } - case OPCODE_META_TYPE_VARG: - case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: - { - mask = 0x010; - break; - } - case OPCODE_META_TYPE_UNDEFINED: - case OPCODE_META_TYPE_END_WITH: - case OPCODE_META_TYPE_FUNCTION_END: - case OPCODE_META_TYPE_CATCH: - case OPCODE_META_TYPE_FINALLY: - case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: - case OPCODE_META_TYPE_END_FOR_IN: - case OPCODE_META_TYPE_CALL_SITE_INFO: - { - mask = 0x000; - break; - } - default: - { - JERRY_UNREACHABLE (); - } - } - break; - } - - default: - { - JERRY_UNREACHABLE (); - } - } - - for (uint8_t i = 0; i < 3; i++) - { - JERRY_STATIC_ASSERT (VM_IDX_LITERAL_FIRST == 0); - - if (is_possible_literal (mask, i) - && opm.op.data.raw_args[i] <= VM_IDX_LITERAL_LAST) - { - opm.lit_id[i] = bc_get_literal_cp_by_uid (opm.op.data.raw_args[i], bc_header_p, pos); - } - else - { - opm.lit_id[i] = NOT_A_LITERAL; - } - } - } - - return opm; -} /* dumper_get_op_meta */ - -/** - * Dump instruction - */ -static void -dumper_dump_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ - op_meta opm) /**< intermediate instruction description */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - JERRY_ASSERT (bc_header_p->instrs_count < MAX_OPCODES); - -#ifdef JERRY_ENABLE_PRETTY_PRINTER - if (is_print_instrs) - { - pp_op_meta (bc_header_p, bc_header_p->instrs_count, opm, false); - } -#endif /* JERRY_ENABLE_PRETTY_PRINTER */ - - vm_instr_t *new_instr_place_p = bc_header_p->instrs_p + bc_header_p->instrs_count; - - bc_header_p->instrs_count++; - - *new_instr_place_p = opm.op; - } - else - { - scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); - - count_new_literals_in_instr (ctx_p, scope_p->instrs_count, &opm); - - scope_p->instrs_count++; - } -} /* dumper_dump_op_meta */ - -/** - * Rewrite instruction at specified offset - */ -void -dumper_rewrite_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ - const vm_instr_counter_t loc, /**< instruction location */ - op_meta opm) /**< intermediate instruction description */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - JERRY_ASSERT (loc < bc_header_p->instrs_count); - - vm_instr_t *instr_p = bc_header_p->instrs_p + loc; - - *instr_p = opm.op; - -#ifdef JERRY_ENABLE_PRETTY_PRINTER - if (is_print_instrs) - { - pp_op_meta (bc_header_p, loc, opm, true); - } -#endif /* JERRY_ENABLE_PRETTY_PRINTER */ - } -} /* dumper_rewrite_op_meta */ - -#ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER -/** - * Start move of variable values to registers optimization pass - */ -void -dumper_start_move_of_vars_to_regs (jsp_ctx_t *ctx_p __attr_unused___) -{ - JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); - JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); - - jsp_reg_max_for_local_var = jsp_reg_max_for_temps; -} /* dumper_start_move_of_vars_to_regs */ - -/** - * Start move of argument values to registers optimization pass - * - * @return true - if optimization can be performed successfully (i.e. there are enough free registers), - * false - otherwise. - */ -bool -dumper_start_move_of_args_to_regs (jsp_ctx_t *ctx_p __attr_unused___, - uint32_t args_num) /**< number of arguments */ -{ - JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); - - if (jsp_reg_max_for_local_var == VM_IDX_EMPTY) - { - if (args_num + jsp_reg_max_for_temps >= VM_REG_GENERAL_LAST) - { - return false; - } - - jsp_reg_max_for_args = jsp_reg_max_for_temps; - } - else - { - if (args_num + jsp_reg_max_for_local_var >= VM_REG_GENERAL_LAST) - { - return false; - } - - jsp_reg_max_for_args = jsp_reg_max_for_local_var; - } - - return true; -} /* dumper_start_move_of_args_to_regs */ - -/** - * Try to move local variable to a register - * - * Note: - * First instruction of the scope should be either func_decl_n or func_expr_n, as the scope is function scope, - * and the optimization is not applied to 'new Function ()'-like constructed functions. - * - * See also: - * parse_source_element_list - * parser_parse_program - * - * @return true, if optimization performed successfully, i.e.: - * - there is a free register to use; - * - the variable name is not equal to any of the function's argument names; - * false - otherwise. - */ -bool -dumper_try_replace_identifier_name_with_reg (jsp_ctx_t *ctx_p, /**< parser context */ - bytecode_data_header_t *bc_header_p, /**< a byte-code data header, - * created for function declaration - * or function expresssion */ - op_meta *om_p) /**< operation meta of corresponding - * variable declaration */ -{ - lit_cpointer_t lit_cp; - bool is_arg; - - if (om_p->op.op_idx == VM_OP_VAR_DECL) - { - JERRY_ASSERT (om_p->lit_id[0].packed_value != NOT_A_LITERAL.packed_value); - JERRY_ASSERT (om_p->lit_id[1].packed_value == NOT_A_LITERAL.packed_value); - JERRY_ASSERT (om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); - - lit_cp = om_p->lit_id[0]; - - is_arg = false; - } - else - { - JERRY_ASSERT (om_p->op.op_idx == VM_OP_META); - - JERRY_ASSERT (om_p->op.data.meta.type == OPCODE_META_TYPE_VARG); - JERRY_ASSERT (om_p->lit_id[0].packed_value == NOT_A_LITERAL.packed_value); - JERRY_ASSERT (om_p->lit_id[1].packed_value != NOT_A_LITERAL.packed_value); - JERRY_ASSERT (om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); - - lit_cp = om_p->lit_id[1]; - - is_arg = true; - } - - vm_idx_t reg; - - if (is_arg) - { - JERRY_ASSERT (jsp_reg_max_for_args != VM_IDX_EMPTY); - JERRY_ASSERT (jsp_reg_max_for_args < VM_REG_GENERAL_LAST); - - reg = ++jsp_reg_max_for_args; - } - else - { - JERRY_ASSERT (jsp_reg_max_for_local_var != VM_IDX_EMPTY); - - if (jsp_reg_max_for_local_var == VM_REG_GENERAL_LAST) - { - /* not enough registers */ - return false; - } - JERRY_ASSERT (jsp_reg_max_for_local_var < VM_REG_GENERAL_LAST); - - reg = ++jsp_reg_max_for_local_var; - } - - for (vm_instr_counter_t instr_pos = 0; - instr_pos < bc_header_p->instrs_count; - instr_pos++) - { - op_meta om = dumper_get_op_meta (ctx_p, instr_pos); - - vm_op_t opcode = (vm_op_t) om.op.op_idx; - - int args_num = 0; - -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - if (opcode == VM_OP_ ## opcode_name_uppercase) \ - { \ - args_num = 0; \ - } -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - if (opcode == VM_OP_ ## opcode_name_uppercase) \ - { \ - JERRY_STATIC_ASSERT (((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ - args_num = 1; \ - } -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - if (opcode == VM_OP_ ## opcode_name_uppercase) \ - { \ - JERRY_STATIC_ASSERT (((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ - JERRY_STATIC_ASSERT (((arg2_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ - args_num = 2; \ - } -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - if (opcode == VM_OP_ ## opcode_name_uppercase) \ - { \ - /* - * See also: - * The loop below - */ \ - \ - JERRY_ASSERT ((opcode == VM_OP_ASSIGNMENT && (arg2_type) == VM_OP_ARG_TYPE_TYPE_OF_NEXT) \ - || (opcode != VM_OP_ASSIGNMENT && ((arg2_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0)); \ - JERRY_ASSERT ((opcode == VM_OP_META && ((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) != 0) \ - || (opcode != VM_OP_META && ((arg1_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0)); \ - JERRY_STATIC_ASSERT (((arg3_type) & VM_OP_ARG_TYPE_TYPE_OF_NEXT) == 0); \ - args_num = 3; \ - } - -#include "vm-opcodes.inc.h" - - for (int arg_index = 0; arg_index < args_num; arg_index++) - { - /* - * 'assignment' and 'meta' are the only opcodes with statically unspecified argument type - * (checked by assertions above) - */ - if (opcode == VM_OP_ASSIGNMENT - && arg_index == 1 - && om.op.data.assignment.type_value_right != VM_OP_ARG_TYPE_VARIABLE) - { - break; - } - - if (opcode == VM_OP_META - && (((om.op.data.meta.type == OPCODE_META_TYPE_VARG_PROP_DATA - || om.op.data.meta.type == OPCODE_META_TYPE_VARG_PROP_GETTER - || om.op.data.meta.type == OPCODE_META_TYPE_VARG_PROP_SETTER) - && arg_index == 1) - || om.op.data.meta.type == OPCODE_META_TYPE_END_WITH - || om.op.data.meta.type == OPCODE_META_TYPE_CATCH - || om.op.data.meta.type == OPCODE_META_TYPE_FINALLY - || om.op.data.meta.type == OPCODE_META_TYPE_END_TRY_CATCH_FINALLY - || om.op.data.meta.type == OPCODE_META_TYPE_END_FOR_IN)) - { - continue; - } - - if (om.lit_id[arg_index].packed_value == lit_cp.packed_value) - { - om.lit_id[arg_index] = NOT_A_LITERAL; - - om.op.data.raw_args[arg_index] = reg; - } - } - - dumper_rewrite_op_meta (ctx_p, instr_pos, om); - } - - return true; -} /* dumper_try_replace_identifier_name_with_reg */ -#endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ - -/** - * Just allocate register for argument that is never used due to duplicated argument names - */ -void -dumper_alloc_reg_for_unused_arg (jsp_ctx_t *ctx_p __attr_unused___) -{ - JERRY_ASSERT (jsp_reg_max_for_args != VM_IDX_EMPTY); - JERRY_ASSERT (jsp_reg_max_for_args < VM_REG_GENERAL_LAST); - - ++jsp_reg_max_for_args; -} /* dumper_alloc_reg_for_unused_arg */ - -/** - * Generate instruction with specified opcode and operands - * - * @return VM instruction - */ -static vm_instr_t -jsp_dmp_gen_instr (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< operation code */ - jsp_operand_t ops[], /**< operands */ - size_t ops_num) /**< operands number */ -{ - vm_instr_t instr; - - instr.op_idx = opcode; - - for (size_t i = 0; i < ops_num; i++) - { - if (jsp_is_empty_operand (ops[i])) - { - instr.data.raw_args[i] = VM_IDX_EMPTY; - } - else if (jsp_is_unknown_operand (ops[i])) - { - instr.data.raw_args[i] = VM_IDX_REWRITE_GENERAL_CASE; - } - else if (jsp_is_idx_const_operand (ops[i])) - { - instr.data.raw_args[i] = jsp_operand_get_idx_const (ops[i]); - } - else if (jsp_is_smallint_operand (ops[i])) - { - instr.data.raw_args[i] = jsp_operand_get_smallint_value (ops[i]); - } - else if (jsp_is_simple_value_operand (ops[i])) - { - instr.data.raw_args[i] = jsp_operand_get_simple_value (ops[i]); - } - else if (jsp_is_register_operand (ops[i]) || jsp_is_this_operand (ops[i])) - { - instr.data.raw_args[i] = jsp_operand_get_idx (ops[i]); - } - else - { - lit_cpointer_t lit_cp; - - if (jsp_is_identifier_operand (ops[i])) - { - lit_cp = jsp_operand_get_identifier_name (ops[i]); - } - else - { - JERRY_ASSERT (jsp_is_number_lit_operand (ops[i]) - || jsp_is_string_lit_operand (ops[i]) - || jsp_is_regexp_lit_operand (ops[i])); - - lit_cp = jsp_operand_get_literal (ops[i]); - } - - vm_idx_t idx = VM_IDX_REWRITE_LITERAL_UID; - - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - lit_id_hash_table *lit_id_hash_p = MEM_CP_GET_NON_NULL_POINTER (lit_id_hash_table, - bc_header_p->lit_id_hash_cp); - - idx = lit_id_hash_table_insert (lit_id_hash_p, bc_header_p->instrs_count, lit_cp); - } - - instr.data.raw_args[i] = idx; - } - } - - for (size_t i = ops_num; i < 3; i++) - { - instr.data.raw_args[i] = VM_IDX_EMPTY; - } - - return instr; -} /* jsp_dmp_gen_instr */ - -/** - * Create intermediate instruction description, containing pointers to literals, - * associated with the instruction's arguments, if there are any. - * - * @return intermediate operation description - */ -static op_meta -jsp_dmp_create_op_meta (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t ops[], /**< operands */ - size_t ops_num) /**< operands number */ -{ - op_meta ret; - - ret.op = jsp_dmp_gen_instr (ctx_p, opcode, ops, ops_num); - - for (size_t i = 0; i < ops_num; i++) - { - if (jsp_is_number_lit_operand (ops[i]) - || jsp_is_string_lit_operand (ops[i]) - || jsp_is_regexp_lit_operand (ops[i])) - { - ret.lit_id[i] = jsp_operand_get_literal (ops[i]); - } - else if (jsp_is_identifier_operand (ops[i])) - { - ret.lit_id[i] = jsp_operand_get_identifier_name (ops[i]); - } - else - { - ret.lit_id[i] = NOT_A_LITERAL; - } - } - - for (size_t i = ops_num; i < 3; i++) - { - ret.lit_id[i] = NOT_A_LITERAL; - } - - return ret; -} /* jsp_dmp_create_op_meta */ - -/** - * Create intermediate instruction description (for instructions without arguments) - * - * See also: - * jsp_dmp_create_op_meta - * - * @return intermediate instruction description - */ -static op_meta -jsp_dmp_create_op_meta_0 (jsp_ctx_t *ctx_p, - vm_op_t opcode) /**< opcode */ -{ - return jsp_dmp_create_op_meta (ctx_p, opcode, NULL, 0); -} /* jsp_dmp_create_op_meta_0 */ - -/** - * Create intermediate instruction description (for instructions with 1 argument) - * - * See also: - * jsp_dmp_create_op_meta - * - * @return intermediate instruction description - */ -static op_meta -jsp_dmp_create_op_meta_1 (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t operand1) /**< first operand */ -{ - return jsp_dmp_create_op_meta (ctx_p, opcode, &operand1, 1); -} /* jsp_dmp_create_op_meta_1 */ - -/** - * Create intermediate instruction description (for instructions with 2 arguments) - * - * See also: - * jsp_dmp_create_op_meta - * - * @return intermediate instruction description - */ -static op_meta -jsp_dmp_create_op_meta_2 (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t operand1, /**< first operand */ - jsp_operand_t operand2) /**< second operand */ -{ - jsp_operand_t ops[] = { operand1, operand2 }; - return jsp_dmp_create_op_meta (ctx_p, opcode, ops, 2); -} /* jsp_dmp_create_op_meta_2 */ - -/** - * Create intermediate instruction description (for instructions with 3 arguments) - * - * See also: - * jsp_dmp_create_op_meta - * - * @return intermediate instruction description - */ -static op_meta -jsp_dmp_create_op_meta_3 (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t operand1, /**< first operand */ - jsp_operand_t operand2, /**< second operand */ - jsp_operand_t operand3) /**< third operand */ -{ - jsp_operand_t ops[] = { operand1, operand2, operand3 }; - return jsp_dmp_create_op_meta (ctx_p, opcode, ops, 3); -} /* jsp_dmp_create_op_meta_3 */ - -/** - * Create temporary operand (alloc available temporary register) - */ -jsp_operand_t -tmp_operand (void) -{ - return jsp_make_reg_operand (jsp_alloc_reg_for_temp ()); -} /* tmp_operand */ - -/** - * Store instruction counter into two operands - */ -static void -split_instr_counter (vm_instr_counter_t oc, /**< instruction counter */ - vm_idx_t *id1, /**< pointer to the first operand */ - vm_idx_t *id2) /**< pointer to the second operand */ -{ - JERRY_ASSERT (id1 != NULL); - JERRY_ASSERT (id2 != NULL); - *id1 = (vm_idx_t) (oc >> JERRY_BITSINBYTE); - *id2 = (vm_idx_t) (oc & ((1 << JERRY_BITSINBYTE) - 1)); - JERRY_ASSERT (oc == vm_calc_instr_counter_from_idx_idx (*id1, *id2)); -} /* split_instr_counter */ - -/** - * Dump single address instruction - */ -static void -dump_single_address (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t op) /**< first operand */ -{ - dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_1 (ctx_p, opcode, op)); -} /* dump_single_address */ - -/* - * Dump double address instruction - */ -static void -dump_double_address (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t res, /**< result operand */ - jsp_operand_t obj) /**< argument operand */ -{ - dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_2 (ctx_p, opcode, res, obj)); -} /* dump_double_address */ - -/* - * Dump triple address instruction - */ -static void -dump_triple_address (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t res, /**< result operand */ - jsp_operand_t lhs, /**< first operand */ - jsp_operand_t rhs) /**< second operand */ -{ - dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_3 (ctx_p, opcode, res, lhs, rhs)); -} /* dump_triple_address */ - -/** - * Get offset from specified instruction (to calculate distance for jump) - * - * @return distance between specified and current instruction - */ -static vm_instr_counter_t -get_diff_from (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t oc) /**< position of instruction */ -{ - return (vm_instr_counter_t) (dumper_get_current_instr_counter (ctx_p) - oc); -} /* get_diff_from */ - -/** - * Create empty operand - */ -jsp_operand_t -empty_operand (void) -{ - return jsp_make_empty_operand (); -} /* empty_operand */ - -/** - * Check if operand is empty - */ -bool -operand_is_empty (jsp_operand_t op) /**< operand */ -{ - return jsp_is_empty_operand (op); -} /* operand_is_empty */ - -/** - * Start dump of a new statement (mark all temporary registers as unused) - */ -void -dumper_new_statement (jsp_ctx_t *ctx_p __attr_unused___) -{ - jsp_reg_next = VM_REG_GENERAL_FIRST; -} /* dumper_new_statement */ - -/** - * Save temporary registers context - */ -void -dumper_save_reg_alloc_ctx (jsp_ctx_t *ctx_p __attr_unused___, - vm_idx_t *out_saved_reg_next_p, /**< pointer to store index of the next free temporary - * register */ - vm_idx_t *out_saved_reg_max_for_temps_p) /**< pointer to store maximum identifier of a - * register, allocated for intermediate - * value storage */ -{ - JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); - JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); - - *out_saved_reg_next_p = jsp_reg_next; - *out_saved_reg_max_for_temps_p = jsp_reg_max_for_temps; - - jsp_reg_next = VM_REG_GENERAL_FIRST; - jsp_reg_max_for_temps = jsp_reg_next; -} /* dumper_save_reg_alloc_ctx */ - -/** - * Restore temporary registers context - */ -void -dumper_restore_reg_alloc_ctx (jsp_ctx_t *ctx_p __attr_unused___, - vm_idx_t saved_reg_next, /**< identifier of next free register */ - vm_idx_t saved_reg_max_for_temps, /**< maximum identifier of register, allocated for - * intermediate value storage */ - bool is_overwrite_max) /**< flag, indicating if maximum identifier of register, - * allocated for intermediate value storage, should be - * overwritten, or maximum of current and new value - * should be chosen */ -{ - JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); - JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); - - if (is_overwrite_max) - { - jsp_reg_max_for_temps = saved_reg_max_for_temps; - } - else - { - jsp_reg_max_for_temps = JERRY_MAX (jsp_reg_max_for_temps, saved_reg_max_for_temps); - } - - jsp_reg_next = saved_reg_next; -} /* dumper_restore_reg_alloc_ctx */ - -/** - * Save identifier of the next free register - * - * @return identifier of the next free register - */ -vm_idx_t -dumper_save_reg_alloc_counter (jsp_ctx_t *ctx_p __attr_unused___) -{ - return jsp_reg_next; -} /* dumper_save_reg_alloc_counter */ - -/** - * Restore value of tthe next free register - */ -void -dumper_restore_reg_alloc_counter (jsp_ctx_t *ctx_p __attr_unused___, - vm_idx_t reg_alloc_counter) /**< value, returned by corresponding - * dumper_save_reg_alloc_counter */ -{ - jsp_reg_next = reg_alloc_counter; -} /* dumper_restore_reg_alloc_counter */ - -/** - * Check that byte-code operand refers to 'eval' string - * - * @return true - if specified byte-code operand's type is literal, and value of corresponding - * literal is equal to LIT_MAGIC_STRING_EVAL string, - * false - otherwise. - */ -bool -dumper_is_eval_literal (jsp_operand_t obj) /**< byte-code operand */ -{ - /* - * FIXME: Switch to corresponding magic string - */ - if (jsp_is_identifier_operand (obj)) - { - lit_literal_t lit = lit_get_literal_by_cp (jsp_operand_get_identifier_name (obj)); - - return lit_literal_equal_type_cstr (lit, "eval"); - } - - return false; -} /* dumper_is_eval_literal */ - -/** - * Dump variable assignment - */ -void -dump_variable_assignment (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t res, /**< result operand */ - jsp_operand_t var) /**< operand, holding the value to assign */ -{ - jsp_operand_t type_operand; - - if (jsp_is_string_lit_operand (var)) - { - type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_STRING); - } - else if (jsp_is_number_lit_operand (var)) - { - type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_NUMBER); - } - else if (jsp_is_regexp_lit_operand (var)) - { - type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_REGEXP); - } - else if (jsp_is_smallint_operand (var)) - { - type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_SMALLINT); - } - else if (jsp_is_simple_value_operand (var)) - { - type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_SIMPLE); - } - else - { - JERRY_ASSERT (jsp_is_identifier_operand (var) - || jsp_is_register_operand (var) - || jsp_is_this_operand (var)); - - type_operand = jsp_make_idx_const_operand (OPCODE_ARG_TYPE_VARIABLE); - } - - dump_triple_address (ctx_p, VM_OP_ASSIGNMENT, res, type_operand, var); -} /* dump_variable_assignment */ - -/** - * Dump instruction, which implies variable number of arguments after it - */ -vm_instr_counter_t -dump_varg_header_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ - varg_list_type vlt, /**< type of instruction */ - jsp_operand_t res, /**< result operand */ - jsp_operand_t obj) /**< argument operand */ -{ - vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); - - switch (vlt) - { - case VARG_FUNC_EXPR: - { - dump_triple_address (ctx_p, - VM_OP_FUNC_EXPR_N, - res, - obj, - jsp_make_unknown_operand ()); - break; - } - case VARG_CONSTRUCT_EXPR: - { - dump_triple_address (ctx_p, - VM_OP_CONSTRUCT_N, - res, - obj, - jsp_make_unknown_operand ()); - break; - } - case VARG_CALL_EXPR: - { - dump_triple_address (ctx_p, - VM_OP_CALL_N, - res, - obj, - jsp_make_unknown_operand ()); - break; - } - case VARG_FUNC_DECL: - { - dump_double_address (ctx_p, - VM_OP_FUNC_DECL_N, - obj, - jsp_make_unknown_operand ()); - break; - } - case VARG_ARRAY_DECL: - { - dump_double_address (ctx_p, - VM_OP_ARRAY_DECL, - res, - jsp_make_unknown_operand ()); - break; - } - case VARG_OBJ_DECL: - { - dump_double_address (ctx_p, - VM_OP_OBJ_DECL, - res, - jsp_make_unknown_operand ()); - break; - } - } - - return pos; -} /* dump_varg_header_for_rewrite */ - -/** - * Enumeration of possible rewrite types - */ -typedef enum -{ - REWRITE_VARG_HEADER, - REWRITE_FUNCTION_END, - REWRITE_CONDITIONAL_CHECK, - REWRITE_JUMP_TO_END, - REWRITE_SIMPLE_OR_NESTED_JUMP, - REWRITE_CASE_CLAUSE, - REWRITE_DEFAULT_CLAUSE, - REWRITE_WITH, - REWRITE_FOR_IN, - REWRITE_TRY, - REWRITE_CATCH, - REWRITE_FINALLY, - REWRITE_SCOPE_CODE_FLAGS, - REWRITE_REG_VAR_DECL, -} rewrite_type_t; - -/** - * Assert operands in rewrite operation - */ -static void -dumper_assert_op_fields (jsp_ctx_t *ctx_p, /**< parser context */ - rewrite_type_t rewrite_type, /**< type of rewrite */ - op_meta meta) /**< intermediate instruction description */ -{ - if (!jsp_is_dump_mode (ctx_p)) - { - return; - } - - if (rewrite_type == REWRITE_FUNCTION_END) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_META); - JERRY_ASSERT (meta.op.data.meta.type == OPCODE_META_TYPE_FUNCTION_END); - JERRY_ASSERT (meta.op.data.meta.data_1 == VM_IDX_REWRITE_GENERAL_CASE); - JERRY_ASSERT (meta.op.data.meta.data_2 == VM_IDX_REWRITE_GENERAL_CASE); - } - else if (rewrite_type == REWRITE_CONDITIONAL_CHECK) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_IS_FALSE_JMP_DOWN); - } - else if (rewrite_type == REWRITE_JUMP_TO_END) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_JMP_DOWN); - } - else if (rewrite_type == REWRITE_CASE_CLAUSE) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_IS_TRUE_JMP_DOWN); - } - else if (rewrite_type == REWRITE_DEFAULT_CLAUSE) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_JMP_DOWN); - } - else if (rewrite_type == REWRITE_TRY) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_TRY_BLOCK); - } - else if (rewrite_type == REWRITE_CATCH) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_META - && meta.op.data.meta.type == OPCODE_META_TYPE_CATCH); - } - else if (rewrite_type == REWRITE_FINALLY) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_META - && meta.op.data.meta.type == OPCODE_META_TYPE_FINALLY); - } - else if (rewrite_type == REWRITE_SCOPE_CODE_FLAGS) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_META); - JERRY_ASSERT (meta.op.data.meta.data_1 == VM_IDX_REWRITE_GENERAL_CASE); - JERRY_ASSERT (meta.op.data.meta.data_2 == VM_IDX_EMPTY); - } - else if (rewrite_type == REWRITE_REG_VAR_DECL) - { - JERRY_ASSERT (meta.op.op_idx == VM_OP_REG_VAR_DECL); - } - else - { - JERRY_UNREACHABLE (); - } -} /* dumper_assert_op_fields */ - -/** - * Rewrite args_count field of an instruction - */ -void -rewrite_varg_header_set_args_count (jsp_ctx_t *ctx_p, /**< parser context */ - size_t args_count, /**< number of arguments */ - vm_instr_counter_t pos) /**< position of instruction to rewrite */ -{ - /* - * FIXME: - * Remove formal parameters / arguments number from instruction, - * after ecma-values collection would become extendable (issue #310). - * In the case, each 'varg' instruction would just append corresponding - * argument / formal parameter name to values collection. - */ - - if (!jsp_is_dump_mode (ctx_p)) - { - return; - } - - op_meta om = dumper_get_op_meta (ctx_p, pos); - - switch (om.op.op_idx) - { - case VM_OP_FUNC_EXPR_N: - case VM_OP_CONSTRUCT_N: - case VM_OP_CALL_N: - { - if (args_count > 255) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "No more than 255 formal parameters / arguments are currently supported", - LIT_ITERATOR_POS_ZERO); - } - om.op.data.func_expr_n.arg_list = (vm_idx_t) args_count; - dumper_rewrite_op_meta (ctx_p, pos, om); - break; - } - case VM_OP_FUNC_DECL_N: - { - if (args_count > 255) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "No more than 255 formal parameters are currently supported", - LIT_ITERATOR_POS_ZERO); - } - om.op.data.func_decl_n.arg_list = (vm_idx_t) args_count; - dumper_rewrite_op_meta (ctx_p, pos, om); - break; - } - case VM_OP_ARRAY_DECL: - case VM_OP_OBJ_DECL: - { - if (args_count > 65535) - { - PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, - "No more than 65535 formal parameters are currently supported", - LIT_ITERATOR_POS_ZERO); - } - om.op.data.obj_decl.list_1 = (vm_idx_t) (args_count >> 8); - om.op.data.obj_decl.list_2 = (vm_idx_t) (args_count & 0xffu); - dumper_rewrite_op_meta (ctx_p, pos, om); - break; - } - default: - { - JERRY_UNREACHABLE (); - } - } -} /* rewrite_varg_header_set_args_count */ - -/** - * Dump 'meta' instruction of 'call additional information' type, - * containing call flags and, optionally, 'this' argument - */ -void -dump_call_additional_info (jsp_ctx_t *ctx_p, /**< parser context */ - opcode_call_flags_t flags, /**< call flags */ - jsp_operand_t this_arg) /**< 'this' argument - if flags - * include OPCODE_CALL_FLAGS_HAVE_THIS_ARG, - * or empty operand - otherwise */ -{ - if (flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - JERRY_ASSERT (jsp_is_register_operand (this_arg) || jsp_is_this_operand (this_arg)); - JERRY_ASSERT (!operand_is_empty (this_arg)); - } - else - { - JERRY_ASSERT (operand_is_empty (this_arg)); - } - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_CALL_SITE_INFO), - jsp_make_idx_const_operand (flags), - this_arg); -} /* dump_call_additional_info */ - -/** - * Dump meta instruction, specifying the value of the argument - */ -void -dump_varg (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) /**< operand, holding the value of the argument */ -{ - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG), - op, - jsp_make_empty_operand ()); -} /* dump_varg */ - -void -dump_prop_name_and_value (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t name, /**< property name */ - jsp_operand_t value) /**< property value */ -{ - JERRY_ASSERT (jsp_is_string_lit_operand (name)); - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG_PROP_DATA), - name, - value); -} /* dump_prop_name_and_value */ - -void -dump_prop_getter_decl (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t name, /**< property name */ - jsp_operand_t func) /**< property getter */ -{ - JERRY_ASSERT (jsp_is_string_lit_operand (name)); - JERRY_ASSERT (jsp_is_register_operand (func)); - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG_PROP_GETTER), - name, - func); -} /* dump_prop_getter_decl */ - -void -dump_prop_setter_decl (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t name, /**< property name */ - jsp_operand_t func) /**< property setter */ -{ - JERRY_ASSERT (jsp_is_string_lit_operand (name)); - JERRY_ASSERT (jsp_is_register_operand (func)); - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_VARG_PROP_SETTER), - name, - func); -} /* dump_prop_setter_decl */ - -/** - * Dump property getter - */ -void -dump_prop_getter (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t obj, /**< result operand */ - jsp_operand_t base, /**< object, which property is being acquired */ - jsp_operand_t prop_name) /**< operand, holding property name */ -{ - dump_triple_address (ctx_p, VM_OP_PROP_GETTER, obj, base, prop_name); -} /* dump_prop_getter */ - -/** - * Dump property setter - */ -void -dump_prop_setter (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t base, /**< object, which property is being set */ - jsp_operand_t prop_name, /**< operand, holding property name */ - jsp_operand_t obj) /**< operand, holding value to store */ -{ - dump_triple_address (ctx_p, VM_OP_PROP_SETTER, base, prop_name, obj); -} /* dump_prop_setter */ - -/** - * Dump instruction, which deletes propery of an object - */ -void -dump_delete_prop (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t res, /**< operand to store result of delete oeprator */ - jsp_operand_t base, /**< operand, holding object, from which property is deleted */ - jsp_operand_t prop_name) /**< operand, holding property name */ -{ - dump_triple_address (ctx_p, VM_OP_DELETE_PROP, res, base, prop_name); -} /* dump_delete_prop */ - -/** - * Dump unary operation - */ -void -dump_unary_op (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, - jsp_operand_t res, - jsp_operand_t op) -{ - dump_double_address (ctx_p, opcode, res, op); -} /* dump_unary_op */ - -/** - * Dump binary operation - */ -void -dump_binary_op (jsp_ctx_t *ctx_p, /**< parser context */ - vm_op_t opcode, /**< opcode */ - jsp_operand_t res, /**< result operand */ - jsp_operand_t op1, /**< first operand */ - jsp_operand_t op2) /**< second operand */ -{ - dump_triple_address (ctx_p, opcode, res, op1, op2); -} /* dump_binary_op */ - -/** - * Dump conditional check, jump offset in which would be updated later - * - * @return position of dumped instruction - */ -vm_instr_counter_t -dump_conditional_check_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) /**< operand, holding the value to check */ -{ - vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); - - dump_triple_address (ctx_p, - VM_OP_IS_FALSE_JMP_DOWN, - op, - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return pos; -} /* dump_conditional_check_for_rewrite */ - -/** - * Rewrite jump offset in conditional check - */ -void -rewrite_conditional_check (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos) /**< position to jump to */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); - - op_meta jmp_op_meta = dumper_get_op_meta (ctx_p, pos); - dumper_assert_op_fields (ctx_p, REWRITE_CONDITIONAL_CHECK, jmp_op_meta); - - jmp_op_meta.op.data.is_false_jmp_down.oc_idx_1 = id1; - jmp_op_meta.op.data.is_false_jmp_down.oc_idx_2 = id2; - - dumper_rewrite_op_meta (ctx_p, pos, jmp_op_meta); -} /* rewrite_conditional_check */ - -/** - * Dump jump to end of the loop, jump offset would be updated by rewrite - */ -vm_instr_counter_t -dump_jump_to_end_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ -{ - vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); - - dump_double_address (ctx_p, - VM_OP_JMP_DOWN, - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return pos; -} /* dump_jump_to_end_for_rewrite */ - -/** - * Rewrite jump offset in jump instruction - */ -void -rewrite_jump_to_end (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos)/**< position to jump to */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); - - op_meta jmp_op_meta = dumper_get_op_meta (ctx_p, pos); - dumper_assert_op_fields (ctx_p, REWRITE_JUMP_TO_END, jmp_op_meta); - - jmp_op_meta.op.data.jmp_down.oc_idx_1 = id1; - jmp_op_meta.op.data.jmp_down.oc_idx_2 = id2; - - dumper_rewrite_op_meta (ctx_p, pos, jmp_op_meta); -} /* rewrite_jump_to_end */ - -/** - * Get current instruction counter to use as jump target for loop iteration - */ -vm_instr_counter_t -dumper_set_next_iteration_target (jsp_ctx_t *ctx_p) /**< parser context */ -{ - return dumper_get_current_instr_counter (ctx_p); -} /* dumper_set_next_iteration_target */ - -/** - * Dump conditional/unconditional jump to next iteration of the loop - */ -void -dump_continue_iterations_check (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos, /**< position to jump to */ - jsp_operand_t op)/**< operand to check in condition - * if operand is empty, unconditional jump is - * dumped */ -{ - const vm_instr_counter_t diff = (vm_instr_counter_t) (dumper_get_current_instr_counter (ctx_p) - pos); - vm_idx_t id1, id2; - split_instr_counter (diff, &id1, &id2); - - if (operand_is_empty (op)) - { - dump_double_address (ctx_p, - VM_OP_JMP_UP, - jsp_make_idx_const_operand (id1), - jsp_make_idx_const_operand (id2)); - } - else - { - dump_triple_address (ctx_p, - VM_OP_IS_TRUE_JMP_UP, - op, - jsp_make_idx_const_operand (id1), - jsp_make_idx_const_operand (id2)); - } -} - -/** - * Dump template of a jump instruction. - * - * Note: - * the instruction's flags field is written later (see also: rewrite_simple_or_nested_jump_get_next). - * - * @return position of dumped instruction - */ -vm_instr_counter_t -dump_simple_or_nested_jump_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ - bool is_nested, /**< flag, indicating whether nested (true) - * or simple (false) jump should be dumped */ - bool is_conditional, /**< flag, indicating whether conditional (true) - * or unconditional jump should be dumped */ - bool is_inverted_condition, /**< if is_conditional is set, this flag - * indicates whether to invert the condition */ - jsp_operand_t cond, /**< condition (for conditional jumps), - * empty operand - for non-conditional */ - vm_instr_counter_t next_jump_for_tgt_oc) /**< instr counter of next - * template targetted to - * the same target - if any, - * or MAX_OPCODES - otherwise */ -{ - vm_idx_t id1, id2; - split_instr_counter (next_jump_for_tgt_oc, &id1, &id2); - - vm_instr_counter_t ret = dumper_get_current_instr_counter (ctx_p); - - vm_op_t jmp_opcode; - - if (is_nested) - { - jmp_opcode = VM_OP_JMP_BREAK_CONTINUE; - } - else if (is_conditional) - { - if (is_inverted_condition) - { - jmp_opcode = VM_OP_IS_FALSE_JMP_DOWN; - } - else - { - jmp_opcode = VM_OP_IS_TRUE_JMP_DOWN; - } - } - else - { - jmp_opcode = VM_OP_JMP_DOWN; - } - - if (jmp_opcode == VM_OP_JMP_DOWN - || jmp_opcode == VM_OP_JMP_BREAK_CONTINUE) - { - JERRY_ASSERT (jsp_is_empty_operand (cond)); - - dump_double_address (ctx_p, - jmp_opcode, - jsp_make_idx_const_operand (id1), - jsp_make_idx_const_operand (id2)); - } - else - { - JERRY_ASSERT (!jsp_is_empty_operand (cond)); - - JERRY_ASSERT (jmp_opcode == VM_OP_IS_FALSE_JMP_DOWN - || jmp_opcode == VM_OP_IS_TRUE_JMP_DOWN); - - dump_triple_address (ctx_p, - jmp_opcode, - cond, - jsp_make_idx_const_operand (id1), - jsp_make_idx_const_operand (id2)); - } - - return ret; -} /* dump_simple_or_nested_jump_for_rewrite */ - -/** - * Write jump target position into previously dumped template of jump (simple or nested) instruction - * - * @return instr counter value that was encoded in the jump before rewrite - */ -vm_instr_counter_t -rewrite_simple_or_nested_jump_and_get_next (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t jump_oc, /**< position of jump to rewrite */ - vm_instr_counter_t target_oc) /**< the jump's target */ -{ - if (!jsp_is_dump_mode (ctx_p)) - { - return MAX_OPCODES; - } - - op_meta jump_op_meta = dumper_get_op_meta (ctx_p, jump_oc); - - vm_op_t jmp_opcode = (vm_op_t) jump_op_meta.op.op_idx; - - vm_idx_t id1, id2, id1_prev, id2_prev; - if (target_oc < jump_oc) - { - split_instr_counter ((vm_instr_counter_t) (jump_oc - target_oc), &id1, &id2); - } - else - { - split_instr_counter ((vm_instr_counter_t) (target_oc - jump_oc), &id1, &id2); - } - - if (jmp_opcode == VM_OP_JMP_DOWN) - { - if (target_oc < jump_oc) - { - jump_op_meta.op.op_idx = VM_OP_JMP_UP; - - id1_prev = jump_op_meta.op.data.jmp_up.oc_idx_1; - id2_prev = jump_op_meta.op.data.jmp_up.oc_idx_2; - - jump_op_meta.op.data.jmp_up.oc_idx_1 = id1; - jump_op_meta.op.data.jmp_up.oc_idx_2 = id2; - } - else - { - id1_prev = jump_op_meta.op.data.jmp_down.oc_idx_1; - id2_prev = jump_op_meta.op.data.jmp_down.oc_idx_2; - - jump_op_meta.op.data.jmp_down.oc_idx_1 = id1; - jump_op_meta.op.data.jmp_down.oc_idx_2 = id2; - } - } - else if (jmp_opcode == VM_OP_IS_TRUE_JMP_DOWN) - { - if (target_oc < jump_oc) - { - jump_op_meta.op.op_idx = VM_OP_IS_TRUE_JMP_UP; - - id1_prev = jump_op_meta.op.data.is_true_jmp_up.oc_idx_1; - id2_prev = jump_op_meta.op.data.is_true_jmp_up.oc_idx_2; - - jump_op_meta.op.data.is_true_jmp_up.oc_idx_1 = id1; - jump_op_meta.op.data.is_true_jmp_up.oc_idx_2 = id2; - } - else - { - id1_prev = jump_op_meta.op.data.is_true_jmp_down.oc_idx_1; - id2_prev = jump_op_meta.op.data.is_true_jmp_down.oc_idx_2; - - jump_op_meta.op.data.is_true_jmp_down.oc_idx_1 = id1; - jump_op_meta.op.data.is_true_jmp_down.oc_idx_2 = id2; - } - } - else if (jmp_opcode == VM_OP_IS_FALSE_JMP_DOWN) - { - if (target_oc < jump_oc) - { - jump_op_meta.op.op_idx = VM_OP_IS_FALSE_JMP_UP; - - id1_prev = jump_op_meta.op.data.is_false_jmp_up.oc_idx_1; - id2_prev = jump_op_meta.op.data.is_false_jmp_up.oc_idx_2; - - jump_op_meta.op.data.is_false_jmp_up.oc_idx_1 = id1; - jump_op_meta.op.data.is_false_jmp_up.oc_idx_2 = id2; - } - else - { - id1_prev = jump_op_meta.op.data.is_false_jmp_down.oc_idx_1; - id2_prev = jump_op_meta.op.data.is_false_jmp_down.oc_idx_2; - - jump_op_meta.op.data.is_false_jmp_down.oc_idx_1 = id1; - jump_op_meta.op.data.is_false_jmp_down.oc_idx_2 = id2; - } - } - else - { - JERRY_ASSERT (!jsp_is_dump_mode (ctx_p) || (jmp_opcode == VM_OP_JMP_BREAK_CONTINUE)); - - JERRY_ASSERT (target_oc > jump_oc); - - id1_prev = jump_op_meta.op.data.jmp_break_continue.oc_idx_1; - id2_prev = jump_op_meta.op.data.jmp_break_continue.oc_idx_2; - - jump_op_meta.op.data.jmp_break_continue.oc_idx_1 = id1; - jump_op_meta.op.data.jmp_break_continue.oc_idx_2 = id2; - } - - dumper_rewrite_op_meta (ctx_p, jump_oc, jump_op_meta); - - return vm_calc_instr_counter_from_idx_idx (id1_prev, id2_prev); -} /* rewrite_simple_or_nested_jump_get_next */ - -/** - * Dump template of 'with' instruction. - * - * Note: - * the instruction's flags field is written later (see also: rewrite_with). - * - * @return position of dumped instruction - */ -vm_instr_counter_t -dump_with_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) /**< jsp_operand_t - result of evaluating Expression - * in WithStatement */ -{ - vm_instr_counter_t oc = dumper_get_current_instr_counter (ctx_p); - - dump_triple_address (ctx_p, - VM_OP_WITH, - op, - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return oc; -} /* dump_with_for_rewrite */ - -/** - * Write position of 'with' block's end to specified 'with' instruction template, - * dumped earlier (see also: dump_with_for_rewrite). - */ -void -rewrite_with (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t oc) /**< instr counter of the instruction template */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, oc), &id1, &id2); - - op_meta with_op_meta = dumper_get_op_meta (ctx_p, oc); - - with_op_meta.op.data.with.oc_idx_1 = id1; - with_op_meta.op.data.with.oc_idx_2 = id2; - - dumper_rewrite_op_meta (ctx_p, oc, with_op_meta); -} /* rewrite_with */ - -/** - * Dump 'meta' instruction of 'end with' type - */ -void -dump_with_end (jsp_ctx_t *ctx_p) /**< parser context */ -{ - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_END_WITH), - jsp_make_empty_operand (), - jsp_make_empty_operand ()); -} /* dump_with_end */ - -/** - * Dump template of 'for_in' instruction. - * - * Note: - * the instruction's flags field is written later (see also: rewrite_for_in). - * - * @return position of dumped instruction - */ -vm_instr_counter_t -dump_for_in_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) /**< jsp_operand_t - result of evaluating Expression - * in for-in statement */ -{ - vm_instr_counter_t oc = dumper_get_current_instr_counter (ctx_p); - - dump_triple_address (ctx_p, - VM_OP_FOR_IN, - op, - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return oc; -} /* dump_for_in_for_rewrite */ - -/** - * Write position of 'for_in' block's end to specified 'for_in' instruction template, - * dumped earlier (see also: dump_for_in_for_rewrite). - */ -void -rewrite_for_in (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t oc) /**< instr counter of the instruction template */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, oc), &id1, &id2); - - op_meta for_in_op_meta = dumper_get_op_meta (ctx_p, oc); - - for_in_op_meta.op.data.for_in.oc_idx_1 = id1; - for_in_op_meta.op.data.for_in.oc_idx_2 = id2; - - dumper_rewrite_op_meta (ctx_p, oc, for_in_op_meta); -} /* rewrite_for_in */ - -/** - * Dump 'meta' instruction of 'end for_in' type - */ -void -dump_for_in_end (jsp_ctx_t *ctx_p) /**< parser context */ -{ - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_END_FOR_IN), - jsp_make_empty_operand (), - jsp_make_empty_operand ()); -} /* dump_for_in_end */ - -/** - * Dump instruction, designating start of the try block - * - * @return position of dumped instruction - */ -vm_instr_counter_t -dump_try_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ -{ - vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); - - dump_double_address (ctx_p, - VM_OP_TRY_BLOCK, - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return pos; -} /* dump_try_for_rewrite */ - -/** - * Rewrite jump offset in instruction, designating start of the try block - */ -void -rewrite_try (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos) /**< position of try block end */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); - - op_meta try_op_meta = dumper_get_op_meta (ctx_p, pos); - dumper_assert_op_fields (ctx_p, REWRITE_TRY, try_op_meta); - - try_op_meta.op.data.try_block.oc_idx_1 = id1; - try_op_meta.op.data.try_block.oc_idx_2 = id2; - - dumper_rewrite_op_meta (ctx_p, pos, try_op_meta); -} /* rewrite_try */ - -/** - * Dump instruction, designating start of the catch block - * - * @return position of the dumped instruction - */ -vm_instr_counter_t -dump_catch_for_rewrite (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) -{ - vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); - - JERRY_ASSERT (jsp_is_string_lit_operand (op)); - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_CATCH), - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER), - op, - jsp_make_empty_operand ()); - - return pos; -} /* dump_catch_for_rewrite */ - -/** - * Rewrite jump offset in instruction, designating start of the catch block - */ -void -rewrite_catch (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos) /**< position of the catch block end */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); - - op_meta catch_op_meta = dumper_get_op_meta (ctx_p, pos); - dumper_assert_op_fields (ctx_p, REWRITE_CATCH, catch_op_meta); - - catch_op_meta.op.data.meta.data_1 = id1; - catch_op_meta.op.data.meta.data_2 = id2; - - dumper_rewrite_op_meta (ctx_p, pos, catch_op_meta); -} /* rewrite_catch */ - -/** - * Dump instruction, designating start of the finally block - * - * @return position of the dumped instruction - */ -vm_instr_counter_t -dump_finally_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ -{ - vm_instr_counter_t pos = dumper_get_current_instr_counter (ctx_p); - - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_FINALLY), - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return pos; -} /* dump_finally_for_rewrite */ - -/** - * Rewrite jump offset in instruction, designating start of the finally block - */ -void -rewrite_finally (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t pos) /**< position of the finally block end */ -{ - vm_idx_t id1, id2; - split_instr_counter (get_diff_from (ctx_p, pos), &id1, &id2); - - op_meta finally_op_meta = dumper_get_op_meta (ctx_p, pos); - dumper_assert_op_fields (ctx_p, REWRITE_FINALLY, finally_op_meta); - - finally_op_meta.op.data.meta.data_1 = id1; - finally_op_meta.op.data.meta.data_2 = id2; - - dumper_rewrite_op_meta (ctx_p, pos, finally_op_meta); -} /* rewrite_finally */ - -/** - * Dump end of try-catch-finally block - */ -void -dump_end_try_catch_finally (jsp_ctx_t *ctx_p) /**< parser context */ -{ - dump_triple_address (ctx_p, - VM_OP_META, - jsp_make_idx_const_operand (OPCODE_META_TYPE_END_TRY_CATCH_FINALLY), - jsp_make_empty_operand (), - jsp_make_empty_operand ()); -} /* dump_end_try_catch_finally */ - -/** - * Dump throw instruction - */ -void -dump_throw (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) -{ - dump_single_address (ctx_p, VM_OP_THROW_VALUE, op); -} - -/** - * Dump instruction designating variable declaration - */ -void -dump_variable_declaration (jsp_ctx_t *ctx_p, /**< parser context */ - lit_cpointer_t lit_id) /**< literal which holds variable's name */ -{ - jsp_operand_t op_var_name = jsp_make_string_lit_operand (lit_id); - op_meta op = jsp_dmp_create_op_meta (ctx_p, VM_OP_VAR_DECL, &op_var_name, 1); - - JERRY_ASSERT (!jsp_is_dump_mode (ctx_p)); - scopes_tree_add_var_decl (jsp_get_current_scopes_tree_node (ctx_p), op); -} /* dump_variable_declaration */ - -/** - * Dump return instruction - */ -void -dump_ret (jsp_ctx_t *ctx_p) /**< parser context */ -{ - dumper_dump_op_meta (ctx_p, jsp_dmp_create_op_meta_0 (ctx_p, VM_OP_RET)); -} /* dump_ret */ - -/** - * Dump 'reg_var_decl' instruction template - * - * @return position of the dumped instruction - */ -vm_instr_counter_t -dump_reg_var_decl_for_rewrite (jsp_ctx_t *ctx_p) /**< parser context */ -{ - vm_instr_counter_t oc = dumper_get_current_instr_counter (ctx_p); - - dump_triple_address (ctx_p, - VM_OP_REG_VAR_DECL, - jsp_make_unknown_operand (), - jsp_make_unknown_operand (), - jsp_make_unknown_operand ()); - - return oc; -} /* dump_reg_var_decl_for_rewrite */ - -/** - * Rewrite 'reg_var_decl' instruction's template with current scope's register counts - */ -void -rewrite_reg_var_decl (jsp_ctx_t *ctx_p, /**< parser context */ - vm_instr_counter_t reg_var_decl_oc) /**< position of dumped 'reg_var_decl' template */ -{ - op_meta opm = dumper_get_op_meta (ctx_p, reg_var_decl_oc); - dumper_assert_op_fields (ctx_p, REWRITE_REG_VAR_DECL, opm); - - opm.op.data.reg_var_decl.tmp_regs_num = (vm_idx_t) (jsp_reg_max_for_temps - VM_REG_GENERAL_FIRST + 1); - - if (jsp_reg_max_for_local_var != VM_IDX_EMPTY) - { - JERRY_ASSERT (jsp_reg_max_for_local_var >= jsp_reg_max_for_temps); - opm.op.data.reg_var_decl.local_var_regs_num = (vm_idx_t) (jsp_reg_max_for_local_var - jsp_reg_max_for_temps); - - jsp_reg_max_for_local_var = VM_IDX_EMPTY; - } - else - { - opm.op.data.reg_var_decl.local_var_regs_num = 0; - } - - if (jsp_reg_max_for_args != VM_IDX_EMPTY) - { - if (jsp_reg_max_for_local_var != VM_IDX_EMPTY) - { - JERRY_ASSERT (jsp_reg_max_for_args >= jsp_reg_max_for_local_var); - opm.op.data.reg_var_decl.arg_regs_num = (vm_idx_t) (jsp_reg_max_for_args - jsp_reg_max_for_local_var); - } - else - { - JERRY_ASSERT (jsp_reg_max_for_args >= jsp_reg_max_for_temps); - opm.op.data.reg_var_decl.arg_regs_num = (vm_idx_t) (jsp_reg_max_for_args - jsp_reg_max_for_temps); - } - - jsp_reg_max_for_args = VM_IDX_EMPTY; - } - else - { - opm.op.data.reg_var_decl.arg_regs_num = 0; - } - - dumper_rewrite_op_meta (ctx_p, reg_var_decl_oc, opm); -} /* rewrite_reg_var_decl */ - -/** - * Dump return instruction - */ -void -dump_retval (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_operand_t op) /**< operand, holding a value to return */ -{ - dump_single_address (ctx_p, VM_OP_RETVAL, op); -} /* dump_retval */ - -/** - * Dumper initialization function - */ -void -dumper_init (jsp_ctx_t *ctx_p __attr_unused___, - bool show_instrs) -{ - is_print_instrs = show_instrs; - - jsp_reg_next = VM_REG_GENERAL_FIRST; - jsp_reg_max_for_temps = VM_REG_GENERAL_FIRST; - jsp_reg_max_for_local_var = VM_IDX_EMPTY; - jsp_reg_max_for_args = VM_IDX_EMPTY; -} /* dumper_init */ - diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h deleted file mode 100644 index 6d40e97cae..0000000000 --- a/jerry-core/parser/js/opcodes-dumper.h +++ /dev/null @@ -1,199 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OPCODES_DUMPER_H -#define OPCODES_DUMPER_H - -#include "ecma-globals.h" -#include "jsp-internal.h" -#include "lexer.h" -#include "lit-literal.h" -#include "opcodes.h" -#include "rcs-records.h" -#include "scopes-tree.h" - -/** - * Operand types - */ -typedef enum __attr_packed___ -{ - JSP_OPERAND_TYPE_EMPTY, /**< empty operand */ - JSP_OPERAND_TYPE_STRING_LITERAL, /**< operand contains string literal value */ - JSP_OPERAND_TYPE_NUMBER_LITERAL, /**< operand contains number literal value */ - JSP_OPERAND_TYPE_REGEXP_LITERAL, /**< operand contains regexp literal value */ - JSP_OPERAND_TYPE_SIMPLE_VALUE, /**< operand contains a simple ecma value */ - JSP_OPERAND_TYPE_SMALLINT, /**< operand contains small integer value (less than 256) */ - JSP_OPERAND_TYPE_IDENTIFIER, /**< Identifier reference */ - JSP_OPERAND_TYPE_THIS_BINDING, /**< ThisBinding operand */ - JSP_OPERAND_TYPE_TMP, /**< operand contains byte-code register index */ - JSP_OPERAND_TYPE_IDX_CONST, /**< operand contains an integer constant that fits vm_idx_t */ - JSP_OPERAND_TYPE_UNKNOWN, /**< operand, representing unknown value that would be rewritten later */ - JSP_OPERAND_TYPE_UNINITIALIZED /**< uninitialized operand - * - * Note: - * For use only in assertions to check that operands - * are initialized before actual usage */ -} jsp_operand_type_t; - -/** - * Operand (descriptor of value or reference in context of parser) - */ -typedef struct -{ - union - { - vm_idx_t idx_const; /**< idx constant value (for jsp_operand_t::IDX_CONST) */ - vm_idx_t uid; /**< register index (for jsp_operand_t::TMP) */ - lit_cpointer_t lit_id; /**< literal (for jsp_operand_t::LITERAL) */ - lit_cpointer_t identifier; /**< Identifier reference (is_value_based_ref flag not set) */ - uint8_t smallint_value; /**< small integer value */ - uint8_t simple_value; /**< simple ecma value */ - } data; - - jsp_operand_type_t type; /**< type of operand */ -} jsp_operand_t; - -extern jsp_operand_t jsp_make_uninitialized_operand (void); -extern jsp_operand_t jsp_make_empty_operand (void); -extern jsp_operand_t jsp_make_this_operand (void); -extern jsp_operand_t jsp_make_unknown_operand (void); -extern jsp_operand_t jsp_make_idx_const_operand (vm_idx_t); -extern jsp_operand_t jsp_make_smallint_operand (uint8_t); -extern jsp_operand_t jsp_make_simple_value_operand (ecma_simple_value_t); -extern jsp_operand_t jsp_make_string_lit_operand (lit_cpointer_t); -extern jsp_operand_t jsp_make_regexp_lit_operand (lit_cpointer_t); -extern jsp_operand_t jsp_make_number_lit_operand (lit_cpointer_t); -extern jsp_operand_t jsp_make_identifier_operand (lit_cpointer_t); -extern jsp_operand_t jsp_make_reg_operand (vm_idx_t); - -extern bool jsp_is_empty_operand (jsp_operand_t); -extern bool jsp_is_this_operand (jsp_operand_t); -extern bool jsp_is_unknown_operand (jsp_operand_t); -extern bool jsp_is_idx_const_operand (jsp_operand_t); -extern bool jsp_is_register_operand (jsp_operand_t); -extern bool jsp_is_simple_value_operand (jsp_operand_t); -extern bool jsp_is_smallint_operand (jsp_operand_t); -extern bool jsp_is_number_lit_operand (jsp_operand_t); -extern bool jsp_is_string_lit_operand (jsp_operand_t); -extern bool jsp_is_regexp_lit_operand (jsp_operand_t); -extern bool jsp_is_identifier_operand (jsp_operand_t); - -extern lit_cpointer_t jsp_operand_get_identifier_name (jsp_operand_t); -extern lit_cpointer_t jsp_operand_get_literal (jsp_operand_t); -extern vm_idx_t jsp_operand_get_idx (jsp_operand_t); -extern vm_idx_t jsp_operand_get_idx_const (jsp_operand_t); -extern ecma_simple_value_t jsp_operand_get_simple_value (jsp_operand_t); -extern uint8_t jsp_operand_get_smallint_value (jsp_operand_t); - -JERRY_STATIC_ASSERT (sizeof (jsp_operand_t) == 4); - -typedef enum __attr_packed___ -{ - VARG_FUNC_DECL, - VARG_FUNC_EXPR, - VARG_ARRAY_DECL, - VARG_OBJ_DECL, - VARG_CONSTRUCT_EXPR, - VARG_CALL_EXPR -} varg_list_type; - -extern jsp_operand_t empty_operand (void); -extern jsp_operand_t tmp_operand (void); -extern bool operand_is_empty (jsp_operand_t); - -extern void dumper_init (jsp_ctx_t *, bool); - -extern vm_instr_counter_t dumper_get_current_instr_counter (jsp_ctx_t *); - -extern void dumper_start_move_of_vars_to_regs (jsp_ctx_t *); -extern bool dumper_start_move_of_args_to_regs (jsp_ctx_t *, uint32_t args_num); -extern bool dumper_try_replace_identifier_name_with_reg (jsp_ctx_t *, bytecode_data_header_t *, op_meta *); -extern void dumper_alloc_reg_for_unused_arg (jsp_ctx_t *); - -extern void dumper_new_statement (jsp_ctx_t *); -extern void dumper_save_reg_alloc_ctx (jsp_ctx_t *, vm_idx_t *, vm_idx_t *); -extern void dumper_restore_reg_alloc_ctx (jsp_ctx_t *, vm_idx_t, vm_idx_t, bool); -extern vm_idx_t dumper_save_reg_alloc_counter (jsp_ctx_t *); -extern void dumper_restore_reg_alloc_counter (jsp_ctx_t *, vm_idx_t); - -extern bool dumper_is_eval_literal (jsp_operand_t); - -extern void dump_variable_assignment (jsp_ctx_t *, jsp_operand_t, jsp_operand_t); - -extern vm_instr_counter_t dump_varg_header_for_rewrite (jsp_ctx_t *, varg_list_type, jsp_operand_t, jsp_operand_t); -extern void rewrite_varg_header_set_args_count (jsp_ctx_t *, size_t, vm_instr_counter_t); -extern void dump_call_additional_info (jsp_ctx_t *, opcode_call_flags_t, jsp_operand_t); -extern void dump_varg (jsp_ctx_t *, jsp_operand_t); - -extern void dump_prop_name_and_value (jsp_ctx_t *, jsp_operand_t, jsp_operand_t); -extern void dump_prop_getter_decl (jsp_ctx_t *, jsp_operand_t, jsp_operand_t); -extern void dump_prop_setter_decl (jsp_ctx_t *, jsp_operand_t, jsp_operand_t); -extern void dump_prop_getter (jsp_ctx_t *, jsp_operand_t, jsp_operand_t, jsp_operand_t); -extern void dump_prop_setter (jsp_ctx_t *, jsp_operand_t, jsp_operand_t, jsp_operand_t); - -extern vm_instr_counter_t dump_conditional_check_for_rewrite (jsp_ctx_t *, jsp_operand_t); -extern void rewrite_conditional_check (jsp_ctx_t *, vm_instr_counter_t); -extern vm_instr_counter_t dump_jump_to_end_for_rewrite (jsp_ctx_t *); -extern void rewrite_jump_to_end (jsp_ctx_t *, vm_instr_counter_t); - -extern vm_instr_counter_t dumper_set_next_iteration_target (jsp_ctx_t *); -extern vm_instr_counter_t dump_simple_or_nested_jump_for_rewrite (jsp_ctx_t *, - bool, - bool, - bool, - jsp_operand_t, - vm_instr_counter_t); -extern vm_instr_counter_t rewrite_simple_or_nested_jump_and_get_next (jsp_ctx_t *, - vm_instr_counter_t, - vm_instr_counter_t); -extern void dump_continue_iterations_check (jsp_ctx_t *, vm_instr_counter_t, jsp_operand_t); - -extern void dump_delete (jsp_ctx_t *, jsp_operand_t, jsp_operand_t); -extern void dump_delete_prop (jsp_ctx_t *, jsp_operand_t, jsp_operand_t, jsp_operand_t); - -extern void dump_typeof (jsp_ctx_t *, jsp_operand_t, jsp_operand_t); - -extern void dump_unary_op (jsp_ctx_t *, vm_op_t, jsp_operand_t, jsp_operand_t); -extern void dump_binary_op (jsp_ctx_t *, vm_op_t, jsp_operand_t, jsp_operand_t, jsp_operand_t); - -extern vm_instr_counter_t dump_with_for_rewrite (jsp_ctx_t *, jsp_operand_t); -extern void rewrite_with (jsp_ctx_t *, vm_instr_counter_t); -extern void dump_with_end (jsp_ctx_t *); - -extern vm_instr_counter_t dump_for_in_for_rewrite (jsp_ctx_t *, jsp_operand_t); -extern void rewrite_for_in (jsp_ctx_t *, vm_instr_counter_t); -extern void dump_for_in_end (jsp_ctx_t *); - -extern vm_instr_counter_t dump_try_for_rewrite (jsp_ctx_t *); -extern vm_instr_counter_t dump_catch_for_rewrite (jsp_ctx_t *, jsp_operand_t); -extern vm_instr_counter_t dump_finally_for_rewrite (jsp_ctx_t *); -extern void rewrite_try (jsp_ctx_t *, vm_instr_counter_t); -extern void rewrite_catch (jsp_ctx_t *, vm_instr_counter_t); -extern void rewrite_finally (jsp_ctx_t *, vm_instr_counter_t); -extern void dump_end_try_catch_finally (jsp_ctx_t *); -extern void dump_throw (jsp_ctx_t *, jsp_operand_t); - -extern void dump_variable_declaration (jsp_ctx_t *, lit_cpointer_t); - -extern vm_instr_counter_t dump_reg_var_decl_for_rewrite (jsp_ctx_t *); -extern void rewrite_reg_var_decl (jsp_ctx_t *, vm_instr_counter_t); - -extern void dump_ret (jsp_ctx_t *); -extern void dump_retval (jsp_ctx_t *, jsp_operand_t); - -extern op_meta dumper_get_op_meta (jsp_ctx_t *, vm_instr_counter_t); -extern void dumper_rewrite_op_meta (jsp_ctx_t *, vm_instr_counter_t, op_meta); - -#endif /* !OPCODES_DUMPER_H */ diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index d81ee9feb5..d9cb443e1a 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -1,5 +1,5 @@ -/* Copyright 2014-2016 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -14,5678 +14,58 @@ * limitations under the License. */ -#include "bytecode-data.h" -#include "ecma-helpers.h" -#include "jrt-bit-fields.h" -#include "jrt-libc-includes.h" -#include "jsp-mm.h" -#include "opcodes.h" -#include "opcodes-dumper.h" +#include "js-parser-internal.h" #include "parser.h" -#include "re-parser.h" -#include "scopes-tree.h" -#include "stack.h" -#include "jsp-early-error.h" -#include "jsp-internal.h" -#include "vm.h" -#include "rcs-records.h" -/** - * Flag, indicating whether result of expression - * evaluation should be stored to 'eval result' - * temporary variable. - * - * In other words, the flag indicates whether - * 'eval result' store code should be dumped. - * - * See also: - * parse_expression - */ -typedef enum -{ - JSP_EVAL_RET_STORE_NOT_DUMP, /**< do not dump */ - JSP_EVAL_RET_STORE_DUMP, /**< dump */ -} jsp_eval_ret_store_t; - -static token tok; -static bool parser_show_instrs = false; - -#define EMIT_ERROR(type, MESSAGE) PARSE_ERROR(type, MESSAGE, tok.loc) -#define EMIT_ERROR_VARG(type, MESSAGE, ...) PARSE_ERROR_VARG(type, MESSAGE, tok.loc, __VA_ARGS__) - -/** - * Parse procedure - * - * Parse of any Program production (ECMA-262 v5, 14) is performed in two stages: - * - preparse stage: - * - build tree of scopes (global / eval as root and functions as other nodes) - * - gather necessary properties for each scope: - * - maximum number of instructions - * - maximum number of per-opcode-block unique literals - * - flags, indicating whether scope contains `with`, `try`, etc. - * - perform syntax checks - * - dump stage: - * - dump byte-code instructions - * - fill idx-to-literal hash table - * - perform byte-code optimizations - * - * Parser context - * See also: - * jsp_ctx_t - * - * General description - * Each parse stage is performed using jsp_parse_source_element_list function with common general flow, - * and local branching for actions, specific for particular stage. - * - * Parse of each element is performed in a specific parser state (see also, jsp_state_expr_t and jsp_state_t) - * that determines productions expected by parser in the state. - * - * States are located on managed (non-native) parser stack, so the parser is recursive. - * - * Expression parse flow - * Whenever parser expects an expression, it pushes new state that is initially either JSP_STATE_EXPR_EMPTY or a - * particular fixed helper state. - * - * The new state also contains required expression type, indicating what type of expression is currently expected. - * For states starting with JSP_STATE_EXPR_EMPTY, it is the expression type that indicates final production, - * that is expected to be parsed in the pushed state (AssignmentExpression, LeftHandSideExpression, etc.). - * In case of fixed helper states required type is the same as starting expression type. - * - * In each state parser checks current production for whether it can be extended in some way, acceptable by grammar. - * If it can be extended, it is extended, possibly with pushing new state for parsing subexpression. - * Otherwise, it is just promoted to next (higher / more general) expression type to repeat check with rules, - * corresponding to the next type, or finish parse of current expression. - * Upon finishing parse, associated with the current expression / helper state, `is_completed` flag of the state is - * raised. - * - * Finish of an expression parse is indicated using `is_subexpr_end` local flag in jsp_parse_source_element_list - * function. Upon finish, upper state is popped from stack and becomes current, merging the parsed subexpression - * into its state. - * - * Statements parse flow - * Parse of statements is very similar to parse of expressions. - * - * The states are either fixed (like JSP_STATE_STAT_STATEMENT_LIST), or starting from JSP_STATE_STAT_EMPTY - * with required type equal to JSP_STATE_STAT_STATEMENT. - * - * Source elements list - * Source elements parse state is represented with the single type - JSP_STATE_SOURCE_ELEMENTS. - * - * Labelled statements - * Named labels are pushed as helper states to parser stack and are searched for upon occurence of break or continue - * on label. - * - * Targets of simple break / continue are also searched through stack iteration, looking for iterational or switch - * statements. - */ -typedef enum __attr_packed___ -{ - /* ECMA-262 v5 expression types */ - JSP_STATE_EXPR__BEGIN, - - JSP_STATE_EXPR_EMPTY, /**< no expression yet (at start) */ - JSP_STATE_EXPR_FUNCTION, /**< FunctionExpression (11.2.5) */ - JSP_STATE_EXPR_MEMBER, /**< MemberExpression (11.2) */ - JSP_STATE_EXPR_CALL, /**< CallExpression (11.2) */ - JSP_STATE_EXPR_LEFTHANDSIDE, /**< LeftHandSideExpression (11.2) */ - - JSP_STATE_EXPR__SIMPLE_BEGIN, - - JSP_STATE_EXPR_UNARY, /**< UnaryExpression (11.4) */ - JSP_STATE_EXPR_MULTIPLICATIVE, /**< MultiplicativeExpression (11.5) */ - JSP_STATE_EXPR_ADDITIVE, /**< AdditiveExpression (11.6) */ - JSP_STATE_EXPR_SHIFT, /**< ShiftExpression (11.7) */ - JSP_STATE_EXPR_RELATIONAL, /**< RelationalExpression (11.8) */ - JSP_STATE_EXPR_EQUALITY, /**< EqualityExpression (11.9) */ - JSP_STATE_EXPR_BITWISE_AND, /**< BitwiseAndExpression (11.10) */ - JSP_STATE_EXPR_BITWISE_XOR, /**< BitwiseXorExpression (11.10) */ - JSP_STATE_EXPR_BITWISE_OR, /**< BitwiseOrExpression (11.10) */ - - JSP_STATE_EXPR__SIMPLE_END, - - JSP_STATE_EXPR_LOGICAL_AND, /**< LogicalAndExpression (11.11) */ - JSP_STATE_EXPR_LOGICAL_OR, /**< LogicalOrExpression (11.11) */ - JSP_STATE_EXPR_CONDITION, /**< ConditionalExpression (11.12) */ - JSP_STATE_EXPR_ASSIGNMENT, /**< AssignmentExpression (11.13) */ - JSP_STATE_EXPR_EXPRESSION, /**< Expression (11.14) */ - - JSP_STATE_EXPR_ARRAY_LITERAL, /**< ArrayLiteral (11.1.4) */ - JSP_STATE_EXPR_OBJECT_LITERAL, /**< ObjectLiteral (11.1.5) */ - - JSP_STATE_EXPR_DATA_PROP_DECL, /**< a data property (ObjectLiteral, 11.1.5) */ - JSP_STATE_EXPR_ACCESSOR_PROP_DECL, /**< an accessor's property getter / setter (ObjectLiteral, 11.1.5) */ - - JSP_STATE_EXPR__END, - - JSP_STATE_STAT_EMPTY, /**< no statement yet (at start) */ - JSP_STATE_STAT_IF_BRANCH_START, /**< IfStatement branch start */ - JSP_STATE_STAT_IF_BRANCH_END, /**< IfStatement branch start */ - JSP_STATE_STAT_STATEMENT, /**< Statement */ - JSP_STATE_STAT_STATEMENT_LIST, /**< Statement list */ - JSP_STATE_STAT_VAR_DECL, /**< VariableStatement */ - JSP_STATE_STAT_VAR_DECL_FINISH, - JSP_STATE_STAT_DO_WHILE, /**< IterationStatement */ - JSP_STATE_STAT_WHILE, - JSP_STATE_STAT_FOR_INIT_END, - JSP_STATE_STAT_FOR_INCREMENT, - JSP_STATE_STAT_FOR_COND, - JSP_STATE_STAT_FOR_FINISH, - JSP_STATE_STAT_FOR_IN, - JSP_STATE_STAT_FOR_IN_EXPR, - JSP_STATE_STAT_FOR_IN_FINISH, - JSP_STATE_STAT_ITER_FINISH, - JSP_STATE_STAT_SWITCH, - JSP_STATE_STAT_SWITCH_BRANCH_EXPR, - JSP_STATE_STAT_SWITCH_BRANCH, - JSP_STATE_STAT_SWITCH_FINISH, - JSP_STATE_STAT_TRY, - JSP_STATE_STAT_CATCH_FINISH, - JSP_STATE_STAT_FINALLY_FINISH, - JSP_STATE_STAT_TRY_FINISH, - JSP_STATE_STAT_WITH, - JSP_STATE_STAT_EXPRESSION, - JSP_STATE_STAT_RETURN, - JSP_STATE_STAT_THROW, - - JSP_STATE_FUNC_DECL_FINISH, - - JSP_STATE_SOURCE_ELEMENTS, - - JSP_STATE_STAT_BLOCK, - - JSP_STATE_STAT_NAMED_LABEL -} jsp_state_expr_t; - -typedef struct jsp_state_t -{ - mem_cpointer_t prev_state_cp; /** pointer to previous state on the stack, or MEM_CP_NULL - for bottom stack element */ - mem_cpointer_t next_state_cp; /** pointer to next state on the stack, or MEM_CP_NULL - for top stack element */ - jsp_state_expr_t state; /**< current state */ - jsp_state_expr_t req_state; /**< required state */ - - uint8_t is_completed : 1; /**< the expression parse completed, - * no more tokens can be added to the expression */ - uint8_t is_list_in_process : 1; /**< parsing a list, associated with the expression - * (details depend on current expression type) */ - uint8_t is_no_in_mode : 1; /**< expression is being parsed in NoIn mode (see also: ECMA-262 v5, 11.8) */ - uint8_t is_fixed_ret_operand : 1; /**< the expression's evaluation should produce value that should be - * put to register, specified by operand, specified in state */ - uint8_t is_value_based_reference : 1; /**< flag, indicating whether current state represents evaluated expression - * that evaluated to a value-based reference */ - uint8_t is_get_value_dumped_for_main_operand : 1; - uint8_t is_get_value_dumped_for_prop_operand : 1; - uint8_t is_need_retval : 1; /**< flag, indicating whether result of the expression's - * evaluation, if it is value, is used */ - uint8_t is_complex_production : 1; /**< the expression is being parsed in complex production mode */ - uint8_t var_decl : 1; /**< this flag tells that we are parsing VariableStatement, not - VariableDeclarationList or VariableDeclaration inside - IterationStatement */ - uint8_t is_var_decl_no_in : 1; /**< this flag tells that we are parsing VariableDeclrationNoIn inside - ForIn iteration statement */ - uint8_t was_default : 1; /**< was default branch seen */ - uint8_t is_default_branch : 1; /**< marks default branch of switch statement */ - uint8_t is_simply_jumpable_border : 1; /**< flag, indicating whether simple jump could be performed - * from current statement outside of the statement */ - uint8_t is_dump_eval_ret_store : 1; /**< expression's result should be stored to eval's return register */ - uint8_t is_stmt_list_control_flow_exit_stmt_occured : 1; /**< flag, indicating whether one of the following statements - * occured immediately in the statement list, corresponding - * to the statement: - * - return - * - break - * - continue - * - throw */ - - union u - { - u (void) - { - } - - struct expression - { - union u - { - struct - { - uint32_t list_length; - vm_instr_counter_t header_pos; /**< position of a varg header instruction */ - vm_idx_t reg_alloc_saved_state1; - vm_idx_t reg_alloc_saved_state2; - } varg_sequence; - JERRY_STATIC_ASSERT (sizeof (varg_sequence) == 8); // Please, update size if changed - - struct - { - jsp_operand_t prop_name; - bool is_setter; - } accessor_prop_decl; - JERRY_STATIC_ASSERT (sizeof (accessor_prop_decl) == 6); // Please, update size if changed - - struct - { - vm_instr_counter_t rewrite_chain; /**< chain of jmp instructions to rewrite */ - } logical_and; - JERRY_STATIC_ASSERT (sizeof (logical_and) == 2); // Please, update size if changed - - struct - { - vm_instr_counter_t rewrite_chain; /**< chain of jmp instructions to rewrite */ - } logical_or; - JERRY_STATIC_ASSERT (sizeof (logical_or) == 2); // Please, update size if changed - - struct - { - vm_instr_counter_t conditional_check_pos; - vm_instr_counter_t jump_to_end_pos; - } conditional; - JERRY_STATIC_ASSERT (sizeof (conditional) == 4); // Please, update size if changed - } u; - JERRY_STATIC_ASSERT (sizeof (u) == 8); // Please, update size if changed - - jsp_operand_t operand; /**< operand, associated with expression */ - jsp_operand_t prop_name_operand; /**< operand, describing second part of a value-based reference, - * or empty operand (for Identifier references, values, or constants) */ - jsp_token_type_t token_type; /**< token, related to current and, if binary, to previous expression */ - } expression; - JERRY_STATIC_ASSERT (sizeof (expression) == 20); // Please, update size if changed - - struct statement - { - union u - { - struct iterational - { - union u - { - struct loop_for_in - { - union u - { - locus iterator_expr_loc; - locus body_loc; - } u; - - lit_cpointer_t var_name_lit_cp; - vm_instr_counter_t header_pos; - } loop_for_in; - JERRY_STATIC_ASSERT (sizeof (loop_for_in) == 8); // Please, update size if changed - - struct loop_while - { - union u - { - locus cond_expr_start_loc; - locus end_loc; - } u; - - vm_instr_counter_t next_iter_tgt_pos; - vm_instr_counter_t jump_to_end_pos; - } loop_while; - JERRY_STATIC_ASSERT (sizeof (loop_while) == 8); // Please, update size if changed - - struct loop_do_while - { - vm_instr_counter_t next_iter_tgt_pos; - } loop_do_while; - JERRY_STATIC_ASSERT (sizeof (loop_do_while) == 2); // Please, update size if changed - - struct loop_for - { - union u1 - { - locus body_loc; - locus condition_expr_loc; - } u1; - - union u2 - { - locus increment_expr_loc; - locus end_loc; - } u2; - - vm_instr_counter_t next_iter_tgt_pos; - vm_instr_counter_t jump_to_end_pos; - } loop_for; - JERRY_STATIC_ASSERT (sizeof (loop_for) == 12); // Please, update size if changed - } u; - JERRY_STATIC_ASSERT (sizeof (u) == 12); // Please, update size if changed - - vm_instr_counter_t continues_rewrite_chain; - vm_instr_counter_t continue_tgt_oc; - } iterational; - JERRY_STATIC_ASSERT (sizeof (iterational) == 16); // Please, update size if changed - - struct if_statement - { - vm_instr_counter_t conditional_check_pos; - vm_instr_counter_t jump_to_end_pos; - } if_statement; - JERRY_STATIC_ASSERT (sizeof (if_statement) == 4); // Please, update size if changed - - struct switch_statement - { - jsp_operand_t expr; - - vm_instr_counter_t default_label_oc; /**< MAX_OPCODES - if DefaultClause didn't occur, - * start of StatementList for the DefaultClause, otherwise */ - vm_instr_counter_t last_cond_check_jmp_oc; /**< position of last clause's check, - * of MAX_OPCODES (at start, or after DefaultClause) */ - vm_instr_counter_t skip_check_jmp_oc; /**< position of check for whether next condition check - * should be performed or skipped */ - vm_idx_t saved_reg_next; - vm_idx_t saved_reg_max_for_temps; - } switch_statement; - JERRY_STATIC_ASSERT (sizeof (switch_statement) == 12); // Please, update size if changed - - struct with_statement - { - vm_instr_counter_t header_pos; - } with_statement; - JERRY_STATIC_ASSERT (sizeof (with_statement) == 2); // Please, update size if changed - - struct try_statement - { - vm_instr_counter_t try_pos; - vm_instr_counter_t catch_pos; - vm_instr_counter_t finally_pos; - } try_statement; - JERRY_STATIC_ASSERT (sizeof (try_statement) == 6); // Please, update size if changed - } u; - JERRY_STATIC_ASSERT (sizeof (u) == 16); // Please, update size if changed - - vm_instr_counter_t breaks_rewrite_chain; - } statement; - JERRY_STATIC_ASSERT (sizeof (statement) == 20); // Please, update size if changed - - struct named_label - { - lit_cpointer_t name_cp; - } named_label; - JERRY_STATIC_ASSERT (sizeof (named_label) == 2); // Please, update size if changed - - struct source_elements - { - uint16_t parent_scope_child_scopes_counter; - - vm_instr_counter_t reg_var_decl_oc; - - scope_type_t parent_scope_type; - - vm_idx_t saved_reg_next; - vm_idx_t saved_reg_max_for_temps; - - union - { - mem_cpointer_t parent_scopes_tree_node_cp; - mem_cpointer_t parent_bc_header_cp; - } u; - } source_elements; - JERRY_STATIC_ASSERT (sizeof (source_elements) == 10); // Please, update size if changed - } u; - JERRY_STATIC_ASSERT (sizeof (u) == 20); // Please, update size if changed -} jsp_state_t; - -JERRY_STATIC_ASSERT (sizeof (jsp_state_t) == 28); // Please, update if size is changed - -static void jsp_parse_source_element_list (jsp_ctx_t *, scope_type_t); - -/** - * Initialize parser context - */ -void -jsp_init_ctx (jsp_ctx_t *ctx_p, /**< parser context */ - scope_type_t scope_type) /**< starting scope type (SCOPE_TYPE_GLOBAL or SCOPE_TYPE_EVAL) */ -{ - JERRY_ASSERT (scope_type == SCOPE_TYPE_GLOBAL || scope_type == SCOPE_TYPE_EVAL); - - ctx_p->mode = PREPARSE; - ctx_p->scope_type = scope_type; - ctx_p->state_stack_p = NULL; - ctx_p->u.preparse_stage.current_scope_p = NULL; -} /* jsp_init_ctx */ - -/** - * Check whether current stage is the dump stage - * - * @return true / false - */ -bool -jsp_is_dump_mode (jsp_ctx_t *ctx_p) /**< parser context */ -{ - return (ctx_p->mode == DUMP); -} /* jsp_is_dump_mode */ - -/** - * Switch to dump stage - */ -void -jsp_switch_to_dump_mode (jsp_ctx_t *ctx_p, /**< parser context */ - scopes_tree root_scope_p) /**< root of scopes tree */ -{ - JERRY_ASSERT (ctx_p->mode == PREPARSE); - JERRY_ASSERT (ctx_p->u.preparse_stage.current_scope_p == NULL); - - ctx_p->mode = DUMP; - ctx_p->u.dump_stage.current_bc_header_p = NULL; - ctx_p->u.dump_stage.next_scope_to_dump_p = root_scope_p; -} /* jsp_switch_to_dump_mode */ - -/** - * Check whether currently parsed code is strict mode code - * - * @return true / false - */ -bool -jsp_is_strict_mode (jsp_ctx_t *ctx_p) /**< parser context */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - JERRY_ASSERT (bc_header_p != NULL); - - return bc_header_p->is_strict; - } - else - { - scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); - - JERRY_ASSERT (scope_p != NULL); - - return scope_p->strict_mode; - } -} /* jsp_is_strict_mode */ - -/** - * Indicate that currently parsed code is strict mode code - */ -void -jsp_set_strict_mode (jsp_ctx_t *ctx_p) /**< parser context */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - JERRY_ASSERT (bc_header_p != NULL && bc_header_p->is_strict); - } - else - { - scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); - - JERRY_ASSERT (scope_p != NULL); - - scope_p->strict_mode = true; - } -} /* jsp_set_strict_mode */ - -/** - * Get current scope type - * - * @return scope type - */ -scope_type_t -jsp_get_scope_type (jsp_ctx_t *ctx_p) /**< parser context */ -{ - return ctx_p->scope_type; -} /* jsp_get_scope_type */ - -/** - * Set current scope type - */ -void -jsp_set_scope_type (jsp_ctx_t *ctx_p, /**< parser context */ - scope_type_t scope_type) /**< scope type */ -{ - ctx_p->scope_type = scope_type; -} /* jsp_set_scope_type */ - -/** - * Get number of current scope's child scopes, for which parse was already started - * - * @return the value - */ -uint16_t -jsp_get_processed_child_scopes_counter (jsp_ctx_t *ctx_p) /**< parser context */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - JERRY_ASSERT (ctx_p->processed_child_scopes_counter <= bc_header_p->func_scopes_count); - } - else - { - scopes_tree scope = jsp_get_current_scopes_tree_node (ctx_p); - - JERRY_ASSERT (ctx_p->processed_child_scopes_counter <= scope->child_scopes_num); - } - - return ctx_p->processed_child_scopes_counter; -} /* jsp_get_processed_child_scopes_counter */ - -/** - * Set number of current scope's child scopes, for which parse was already started - */ -void -jsp_set_processed_child_scopes_counter (jsp_ctx_t *ctx_p, /**< parser context */ - uint16_t processed_child_scopes_counter) /**< the new value */ -{ - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - if (bc_header_p == NULL) - { - JERRY_ASSERT (processed_child_scopes_counter == 0); - } - else - { - JERRY_ASSERT (processed_child_scopes_counter <= bc_header_p->func_scopes_count); - } - } - else - { - scopes_tree scope = jsp_get_current_scopes_tree_node (ctx_p); - - if (scope == NULL) - { - JERRY_ASSERT (processed_child_scopes_counter == 0); - } - else - { - JERRY_ASSERT (processed_child_scopes_counter <= scope->child_scopes_num); - } - } - - ctx_p->processed_child_scopes_counter = processed_child_scopes_counter; -} /* jsp_set_processed_child_scopes_counter */ - -/** - * Get and increment number of current scope's child scopes, for which parse was already started - * - * @return the value before increment - */ -uint16_t -jsp_get_and_inc_processed_child_scopes_counter (jsp_ctx_t *ctx_p) /**< parser context */ -{ - uint16_t counter = jsp_get_processed_child_scopes_counter (ctx_p); - - uint16_t ret = counter++; - - jsp_set_processed_child_scopes_counter (ctx_p, counter); - - return ret; -} /* jsp_get_and_inc_processed_child_scopes_counter */ - -/** - * Get next scope to dump - * - * Actually, it the function is iterator interface, used for traversing scopes tree during dump stage. - * It is assumed that node of scopes tree are linked into the list in pre-order traversal order. - */ -scopes_tree -jsp_get_next_scopes_tree_node_to_dump (jsp_ctx_t *ctx_p) /**< parser context */ -{ - JERRY_ASSERT (ctx_p->mode == DUMP); - - scopes_tree node = ctx_p->u.dump_stage.next_scope_to_dump_p; - JERRY_ASSERT (node != NULL); - - ctx_p->u.dump_stage.next_scope_to_dump_p = MEM_CP_GET_POINTER (scopes_tree_int, - node->next_scope_cp); - - return node; -} /* jsp_get_next_scopes_tree_node_to_dump */ - -/** - * Get current scope (node of scopes tree) - * - * Note: - * Valid only during preparse stage - * - * @return the node - */ -scopes_tree -jsp_get_current_scopes_tree_node (jsp_ctx_t *ctx_p) /**< parser context */ -{ - JERRY_ASSERT (ctx_p->mode == PREPARSE); - - return ctx_p->u.preparse_stage.current_scope_p; -} /* jsp_get_current_scopes_tree_node */ - -/** - * Set current scope (node of scopes tree) - * - * Note: - * Valid only during preparse stage - */ -void -jsp_set_current_scopes_tree_node (jsp_ctx_t *ctx_p, /**< parser context */ - scopes_tree scope_p) /**< scopes tree node */ -{ - JERRY_ASSERT (ctx_p->mode == PREPARSE); - JERRY_ASSERT (scope_p != ctx_p->u.preparse_stage.current_scope_p); - - ctx_p->u.preparse_stage.current_scope_p = scope_p; - ctx_p->u.preparse_stage.tmp_lit_set_num = 0; - jsp_set_processed_child_scopes_counter (ctx_p, 0); -} /* jsp_set_current_scopes_tree_node */ - -/** - * Get current scope's byte-code data header - * - * Note: - * Valid only during dump stage - * - * @return the byte-code data header - */ -bytecode_data_header_t * -jsp_get_current_bytecode_header (jsp_ctx_t *ctx_p) /**< parser context */ -{ - JERRY_ASSERT (ctx_p->mode == DUMP); - - return ctx_p->u.dump_stage.current_bc_header_p; -} /* jsp_get_current_bytecode_header */ - -/** - * Set current scope's byte-code data header - * - * Note: - * Valid only during dump stage - */ -void -jsp_set_current_bytecode_header (jsp_ctx_t *ctx_p, /**< parser context */ - bytecode_data_header_t *bc_header_p) /**< byte-code data header */ -{ - JERRY_ASSERT (ctx_p->mode == DUMP); - - ctx_p->u.dump_stage.current_bc_header_p = bc_header_p; - jsp_set_processed_child_scopes_counter (ctx_p, 0); -} /* jsp_set_current_bytecode_header */ - -/** - * Empty temporary literal set - * - * Note: - * Valid only during preparse stage - * - * See also: - * Definition of tmp_lit_set in jsp_ctx_t - */ -void -jsp_empty_tmp_literal_set (jsp_ctx_t *ctx_p) /**< parser context */ -{ - JERRY_ASSERT (ctx_p->mode == PREPARSE); - - ctx_p->u.preparse_stage.tmp_lit_set_num = 0; -} /* jsp_empty_tmp_literal_set */ - -/** - * Take into account the specified literal and adjust temporary literal set and unique literals counter accordingly +/** \addtogroup parser Parser + * @{ * - * Note: - * Valid only during preparse stage - * - * See also: - * Definition of tmp_lit_set in jsp_ctx_t - */ -void -jsp_account_next_bytecode_to_literal_reference (jsp_ctx_t *ctx_p, /**< parser context */ - lit_cpointer_t lit_cp) /**< literal identifier */ -{ - JERRY_ASSERT (ctx_p->mode == PREPARSE); - - scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); - - uint8_t tmp_hash_table_index; - for (tmp_hash_table_index = 0; - tmp_hash_table_index < ctx_p->u.preparse_stage.tmp_lit_set_num; - tmp_hash_table_index++) - { - if (ctx_p->u.preparse_stage.tmp_lit_set[tmp_hash_table_index].packed_value == lit_cp.packed_value) - { - break; - } - } - - if (tmp_hash_table_index == ctx_p->u.preparse_stage.tmp_lit_set_num) - { - scope_p->max_uniq_literals_num++; - - if (ctx_p->u.preparse_stage.tmp_lit_set_num < SCOPE_TMP_LIT_SET_SIZE) - { - ctx_p->u.preparse_stage.tmp_lit_set[ctx_p->u.preparse_stage.tmp_lit_set_num++] = lit_cp; - } - else - { - JERRY_ASSERT (ctx_p->u.preparse_stage.tmp_lit_set_num != 0); - - ctx_p->u.preparse_stage.tmp_lit_set[ctx_p->u.preparse_stage.tmp_lit_set_num - 1u] = lit_cp; - } - } -} /* jsp_account_next_bytecode_to_literal_reference */ - -static bool -token_is (jsp_token_type_t tt) -{ - return (lexer_get_token_type (tok) == tt); -} - -static uint16_t -token_data (void) -{ - return tok.uid; -} - -/** - * Get token data as `lit_cpointer_t` + * \addtogroup jsparser JavaScript + * @{ * - * @return compressed pointer to token data + * \addtogroup jsparser_parser Parser + * @{ */ -static lit_cpointer_t -token_data_as_lit_cp (void) -{ - lit_cpointer_t cp; - cp.packed_value = tok.uid; - - return cp; -} /* token_data_as_lit_cp */ - -static void -skip_token (jsp_ctx_t *ctx_p) -{ - tok = lexer_next_token (false, jsp_is_strict_mode (ctx_p)); -} /** - * In case a regexp token is scanned as a division operator, rescan it + * Parse EcamScript source code */ -static void -rescan_regexp_token (jsp_ctx_t *ctx_p) -{ - lexer_seek (tok.loc); - tok = lexer_next_token (true, jsp_is_strict_mode (ctx_p)); -} /* rescan_regexp_token */ - -static void -seek_token (jsp_ctx_t *ctx_p, - locus loc) -{ - lexer_seek (loc); - - skip_token (ctx_p); -} - -static bool -is_keyword (jsp_token_type_t tt) -{ - return (tt >= TOKEN_TYPE__KEYWORD_BEGIN && tt <= TOKEN_TYPE__KEYWORD_END); -} - -static void -assert_keyword (jsp_token_type_t kw) +jsp_status_t +parser_parse_script (const jerry_api_char_t *source_p, /**< source code */ + size_t size, /**< size of the source code */ + ecma_compiled_code_t **bytecode_data_p) /**< result */ { - if (!token_is (kw)) - { - EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected keyword '%s'", lexer_token_type_to_string (kw)); - JERRY_UNREACHABLE (); - } -} + *bytecode_data_p = parser_parse_script (source_p, size, false, NULL); -static void -current_token_must_be_check_and_skip_it (jsp_ctx_t *ctx_p, - jsp_token_type_t tt) -{ - if (!token_is (tt)) + if (!*bytecode_data_p) { - EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected '%s' token", lexer_token_type_to_string (tt)); + return JSP_STATUS_SYNTAX_ERROR; } - skip_token (ctx_p); -} - -static void -current_token_must_be (jsp_token_type_t tt) -{ - if (!token_is (tt)) - { - EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected '%s' token", lexer_token_type_to_string (tt)); - } -} + return JSP_STATUS_OK; +} /* parser_parse_script */ /** - * Skip block, defined with braces of specified type - * - * Note: - * Missing corresponding brace is considered a syntax error - * - * Note: - * Opening brace of the block to skip should be set as current - * token when the routine is called + * Parse EcamScript eval source code */ -static void -jsp_skip_braces (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_token_type_t brace_type) /**< type of the opening brace */ +jsp_status_t +parser_parse_eval (const jerry_api_char_t *source_p, /**< source code */ + size_t size, /**< size of the source code */ + bool is_strict, /**< strict mode */ + ecma_compiled_code_t **bytecode_data_p) /**< result */ { - current_token_must_be (brace_type); - - jsp_token_type_t closing_bracket_type; - - if (brace_type == TOK_OPEN_PAREN) - { - closing_bracket_type = TOK_CLOSE_PAREN; - } - else if (brace_type == TOK_OPEN_BRACE) - { - closing_bracket_type = TOK_CLOSE_BRACE; - } - else - { - JERRY_ASSERT (brace_type == TOK_OPEN_SQUARE); - closing_bracket_type = TOK_CLOSE_SQUARE; - } - - skip_token (ctx_p); + *bytecode_data_p = parser_parse_script (source_p, size, is_strict, NULL); - while (!token_is (closing_bracket_type) - && !token_is (TOK_EOF)) + if (!*bytecode_data_p) { - if (token_is (TOK_OPEN_PAREN) - || token_is (TOK_OPEN_BRACE) - || token_is (TOK_OPEN_SQUARE)) - { - jsp_skip_braces (ctx_p, lexer_get_token_type (tok)); - } - - skip_token (ctx_p); + return JSP_STATUS_SYNTAX_ERROR; } - current_token_must_be (closing_bracket_type); -} /* jsp_skip_braces */ + return JSP_STATUS_OK; +} /* parser_parse_eval */ /** - * Find next token of specified type before the specified location - * - * Note: - * If skip_brace_blocks is true, every { should correspond to } brace before search end location, - * otherwise a syntax error is raised. - * - * @return true - if token was found (in the case, it is the current token, - * and lexer locus points to it), - * false - otherwise (in the case, lexer locus points to end_loc). + * @} + * @} + * @} */ -static bool -jsp_find_next_token_before_the_locus (jsp_ctx_t *ctx_p, /**< parser context */ - jsp_token_type_t token_to_find, /**< token to search for (except TOK_EOF) */ - locus end_loc, /**< location to search before */ - bool skip_brace_blocks) /**< skip blocks, surrounded with { and } braces */ -{ - JERRY_ASSERT (token_to_find != TOK_EOF); - - while (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) < 0) - { - if (skip_brace_blocks) - { - if (token_is (TOK_OPEN_BRACE)) - { - jsp_skip_braces (ctx_p, TOK_OPEN_BRACE); - - JERRY_ASSERT (token_is (TOK_CLOSE_BRACE)); - skip_token (ctx_p); - - if (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) >= 0) - { - seek_token (ctx_p, end_loc); - - return false; - } - } - else if (token_is (TOK_CLOSE_BRACE)) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unmatched } brace"); - } - } - - if (token_is (token_to_find)) - { - return true; - } - else - { - JERRY_ASSERT (!token_is (TOK_EOF)); - } - - skip_token (ctx_p); - } - - JERRY_ASSERT (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) == 0); - return false; -} /* jsp_find_next_token_before_the_locus */ - -/* property_name - : Identifier - | Keyword - | StringLiteral - | NumericLiteral - ; -*/ -static jsp_operand_t -parse_property_name (void) -{ - jsp_token_type_t tt = lexer_get_token_type (tok); - - if (is_keyword (tt)) - { - const char *s = lexer_token_type_to_string (lexer_get_token_type (tok)); - lit_literal_t lit = lit_find_or_create_literal_from_utf8_string ((const lit_utf8_byte_t *) s, - (lit_utf8_size_t)strlen (s)); - return jsp_make_string_lit_operand (rcs_cpointer_compress (lit)); - } - else - { - switch (tt) - { - case TOK_NAME: - case TOK_STRING: - { - return jsp_make_string_lit_operand (token_data_as_lit_cp ()); - } - case TOK_NUMBER: - case TOK_SMALL_INT: - { - ecma_number_t num; - - if (lexer_get_token_type (tok) == TOK_NUMBER) - { - lit_literal_t num_lit = lit_get_literal_by_cp (token_data_as_lit_cp ()); - JERRY_ASSERT (RCS_RECORD_IS_NUMBER (num_lit)); - num = lit_number_literal_get_number (num_lit); - } - else - { - num = ((ecma_number_t) token_data ()); - } - - lit_utf8_byte_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; - lit_utf8_size_t sz = ecma_number_to_utf8_string (num, buff, sizeof (buff)); - JERRY_ASSERT (sz <= ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); - - lit_literal_t str_lit = lit_find_or_create_literal_from_utf8_string (buff, sz); - return jsp_make_string_lit_operand (rcs_cpointer_compress (str_lit)); - } - case TOK_NULL: - case TOK_BOOL: - { - lit_magic_string_id_t id = (token_is (TOK_NULL) - ? LIT_MAGIC_STRING_NULL - : (tok.uid ? LIT_MAGIC_STRING_TRUE : LIT_MAGIC_STRING_FALSE)); - lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (lit_get_magic_string_utf8 (id), - lit_get_magic_string_size (id)); - return jsp_make_string_lit_operand (rcs_cpointer_compress (lit)); - } - default: - { - EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Wrong property name type: %s", - lexer_token_type_to_string (lexer_get_token_type (tok))); - } - } - } -} - -/** - * Check for "use strict" in directive prologue - */ -static void -jsp_parse_directive_prologue (jsp_ctx_t *ctx_p) -{ - const locus start_loc = tok.loc; - - /* - * Check Directive Prologue for Use Strict directive (see ECMA-262 5.1 section 14.1) - */ - while (token_is (TOK_STRING)) - { - if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "use strict") - && lexer_is_no_escape_sequences_in_token_string (tok)) - { - jsp_set_strict_mode (ctx_p); - break; - } - - skip_token (ctx_p); - - if (token_is (TOK_SEMICOLON)) - { - skip_token (ctx_p); - } - } - - seek_token (ctx_p, start_loc); -} /* jsp_parse_directive_prologue */ - -static void -jsp_state_push (jsp_ctx_t *ctx_p, - jsp_state_t state) -{ - jsp_state_t *new_top_state_p = (jsp_state_t *) jsp_mm_alloc (sizeof (jsp_state_t)); - - *new_top_state_p = state; - - new_top_state_p->next_state_cp = MEM_CP_NULL; - MEM_CP_SET_POINTER (new_top_state_p->prev_state_cp, ctx_p->state_stack_p); - - if (ctx_p->state_stack_p != NULL) - { - MEM_CP_SET_NON_NULL_POINTER (ctx_p->state_stack_p->next_state_cp, new_top_state_p); - } - - ctx_p->state_stack_p = new_top_state_p; -} /* jsp_state_push */ - -static jsp_state_t * -jsp_get_prev_state (jsp_state_t *state_p) -{ - return MEM_CP_GET_POINTER (jsp_state_t, state_p->prev_state_cp); -} /* jsp_get_prev_state */ - -static jsp_state_t * -jsp_get_next_state (jsp_state_t *state_p) -{ - return MEM_CP_GET_NON_NULL_POINTER (jsp_state_t, state_p->next_state_cp); -} /* jsp_get_next_state */ - - -static jsp_state_t * -jsp_state_top (jsp_ctx_t *ctx_p) -{ - JERRY_ASSERT (ctx_p->state_stack_p != NULL); - - return ctx_p->state_stack_p; -} /* jsp_state_top */ - -static bool -jsp_is_stack_contains_exactly_one_element (jsp_ctx_t *ctx_p) -{ - jsp_state_t *top_state_p = jsp_state_top (ctx_p); - - return (top_state_p != NULL && jsp_get_prev_state (top_state_p) == NULL); -} /* jsp_is_stack_contains_exactly_one_element */ - -static void -jsp_state_pop (jsp_ctx_t *ctx_p) -{ - jsp_state_t *top_state_p = ctx_p->state_stack_p; - JERRY_ASSERT (top_state_p != NULL); - - jsp_state_t *prev_state_p = jsp_get_prev_state (top_state_p); - - if (prev_state_p != NULL) - { - prev_state_p->next_state_cp = MEM_CP_NULL; - } - - jsp_mm_free (top_state_p); - - ctx_p->state_stack_p = prev_state_p; -} /* jsp_state_pop */ - -static void -jsp_stack_init (jsp_ctx_t *ctx_p) -{ - JERRY_ASSERT (ctx_p->state_stack_p == NULL); -} /* jsp_stack_init */ - -static void -jsp_stack_finalize (jsp_ctx_t *ctx_p) -{ - while (ctx_p->state_stack_p != NULL) - { - jsp_state_pop (ctx_p); - } -} /* jsp_stack_finalize */ - -static void -jsp_push_new_expr_state (jsp_ctx_t *ctx_p, - jsp_state_expr_t expr_type, - jsp_state_expr_t req_state, - bool in_allowed) -{ - jsp_state_t new_state; - - new_state.state = expr_type; - new_state.req_state = req_state; - new_state.u.expression.operand = empty_operand (); - new_state.u.expression.prop_name_operand = empty_operand (); - new_state.u.expression.token_type = TOK_EMPTY; - - new_state.is_completed = false; - new_state.is_list_in_process = false; - new_state.is_fixed_ret_operand = false; - new_state.is_value_based_reference = false; - new_state.is_complex_production = false; - new_state.var_decl = false; - new_state.is_var_decl_no_in = false; - new_state.is_get_value_dumped_for_main_operand = false; - new_state.is_get_value_dumped_for_prop_operand = false; - new_state.is_need_retval = true; - new_state.prev_state_cp = MEM_CP_NULL; - new_state.next_state_cp = MEM_CP_NULL; - new_state.was_default = false; - new_state.is_default_branch = false; - new_state.is_simply_jumpable_border = false; - new_state.is_dump_eval_ret_store = false; - new_state.is_stmt_list_control_flow_exit_stmt_occured = false; - - new_state.is_no_in_mode = !in_allowed; - - jsp_state_push (ctx_p, new_state); -} /* jsp_push_new_expr_state */ - -static void -jsp_start_statement_parse (jsp_ctx_t *ctx_p, - jsp_state_expr_t stat) -{ - jsp_state_t new_state; - - new_state.state = stat; - new_state.req_state = JSP_STATE_STAT_STATEMENT; - - new_state.u.statement.breaks_rewrite_chain = MAX_OPCODES; - - new_state.is_completed = false; - new_state.is_list_in_process = false; - new_state.is_no_in_mode = false; - new_state.is_fixed_ret_operand = false; - new_state.is_complex_production = false; - new_state.var_decl = false; - new_state.was_default = false; - new_state.is_default_branch = false; - new_state.is_simply_jumpable_border = false; - new_state.is_stmt_list_control_flow_exit_stmt_occured = false; - new_state.prev_state_cp = MEM_CP_NULL; - new_state.next_state_cp = MEM_CP_NULL; - new_state.is_value_based_reference = false; - new_state.is_get_value_dumped_for_main_operand = false; - new_state.is_get_value_dumped_for_prop_operand = false; - new_state.is_need_retval = false; - new_state.is_var_decl_no_in = false; - new_state.is_dump_eval_ret_store = false; - - jsp_state_push (ctx_p, new_state); -} /* jsp_start_statement_parse */ - -static void -jsp_start_parse_source_element_list (jsp_ctx_t *ctx_p, - mem_cpointer_t parent_scope_or_bc_header_cp, - uint16_t parent_scope_processed_scopes_counter, - scope_type_t scope_type) -{ - const scope_type_t parent_scope_type = jsp_get_scope_type (ctx_p); - - jsp_start_statement_parse (ctx_p, JSP_STATE_SOURCE_ELEMENTS); - - jsp_set_scope_type (ctx_p, scope_type); - - jsp_state_t *source_elements_state_p = jsp_state_top (ctx_p); - source_elements_state_p->req_state = JSP_STATE_SOURCE_ELEMENTS; - - if (jsp_is_dump_mode (ctx_p)) - { - source_elements_state_p->u.source_elements.u.parent_bc_header_cp = parent_scope_or_bc_header_cp; - } - else - { - source_elements_state_p->u.source_elements.u.parent_scopes_tree_node_cp = parent_scope_or_bc_header_cp; - } - - dumper_save_reg_alloc_ctx (ctx_p, - &source_elements_state_p->u.source_elements.saved_reg_next, - &source_elements_state_p->u.source_elements.saved_reg_max_for_temps); - source_elements_state_p->u.source_elements.reg_var_decl_oc = dump_reg_var_decl_for_rewrite (ctx_p); - source_elements_state_p->u.source_elements.parent_scope_type = parent_scope_type; - - source_elements_state_p->u.source_elements.parent_scope_child_scopes_counter = parent_scope_processed_scopes_counter; - - if (scope_type == SCOPE_TYPE_EVAL) - { - dump_variable_assignment (ctx_p, - jsp_make_reg_operand (VM_REG_SPECIAL_EVAL_RET), - jsp_make_simple_value_operand (ECMA_SIMPLE_VALUE_UNDEFINED)); - } -} /* jsp_start_parse_source_element_list */ - -static jsp_operand_t -jsp_start_parse_function_scope (jsp_ctx_t *ctx_p, - jsp_operand_t func_name, - bool is_function_expression, - size_t *out_formal_parameters_num_p) -{ - jsp_operand_t func = empty_operand (); - - uint16_t index; - - if (jsp_is_dump_mode (ctx_p)) - { - index = jsp_get_and_inc_processed_child_scopes_counter (ctx_p); - } - else - { - index = 0u; - } - - if (is_function_expression) - { - func = tmp_operand (); - - vm_idx_t idx1 = (vm_idx_t) jrt_extract_bit_field (index, 0, JERRY_BITSINBYTE); - vm_idx_t idx2 = (vm_idx_t) jrt_extract_bit_field (index, JERRY_BITSINBYTE, JERRY_BITSINBYTE); - - dump_binary_op (ctx_p, - VM_OP_FUNC_EXPR_REF, - func, - jsp_make_idx_const_operand (idx1), - jsp_make_idx_const_operand (idx2)); - } - - mem_cpointer_t parent_scope_or_bc_header_cp; - uint16_t parent_scope_processed_scopes_counter = jsp_get_processed_child_scopes_counter (ctx_p); - - scopes_tree func_scope_p; - - if (!jsp_is_dump_mode (ctx_p)) - { - scopes_tree parent_scope_p = jsp_get_current_scopes_tree_node (ctx_p); - MEM_CP_SET_NON_NULL_POINTER (parent_scope_or_bc_header_cp, parent_scope_p); - - scopes_tree_set_contains_functions (parent_scope_p); - - func_scope_p = scopes_tree_new_scope (parent_scope_p, SCOPE_TYPE_FUNCTION); - - jsp_set_current_scopes_tree_node (ctx_p, func_scope_p); - - scopes_tree_set_strict_mode (func_scope_p, scopes_tree_strict_mode (parent_scope_p)); - } - else - { - bytecode_data_header_t *parent_bc_header_p = jsp_get_current_bytecode_header (ctx_p); - MEM_CP_SET_NON_NULL_POINTER (parent_scope_or_bc_header_cp, parent_bc_header_p); - - func_scope_p = jsp_get_next_scopes_tree_node_to_dump (ctx_p); - - bc_dump_single_scope (func_scope_p); - - mem_cpointer_t *parent_declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, - parent_bc_header_p->declarations_cp); - parent_declarations_p[index] = func_scope_p->bc_header_cp; - - bytecode_data_header_t *func_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, - func_scope_p->bc_header_cp); - - jsp_set_current_bytecode_header (ctx_p, func_bc_header_p); - - scopes_tree_free_scope (func_scope_p); - } - - /* parse formal parameters list */ - size_t formal_parameters_num = 0; - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); - - JERRY_ASSERT (jsp_is_empty_operand (func_name) - || jsp_is_string_lit_operand (func_name)); - - varg_list_type vlt = is_function_expression ? VARG_FUNC_EXPR : VARG_FUNC_DECL; - - vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, vlt, func, func_name); - - locus formal_parameters_list_loc = tok.loc; - - while (!token_is (TOK_CLOSE_PAREN)) - { - current_token_must_be (TOK_NAME); - jsp_operand_t formal_parameter_name = jsp_make_string_lit_operand (token_data_as_lit_cp ()); - skip_token (ctx_p); - - dump_varg (ctx_p, formal_parameter_name); - - formal_parameters_num++; - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - } - else - { - current_token_must_be (TOK_CLOSE_PAREN); - } - } - - skip_token (ctx_p); - - rewrite_varg_header_set_args_count (ctx_p, formal_parameters_num, varg_header_pos); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); - - jsp_parse_directive_prologue (ctx_p); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (func_name, jsp_is_strict_mode (ctx_p), tok.loc); - } - - if (jsp_is_strict_mode (ctx_p)) - { - locus body_loc = tok.loc; - - seek_token (ctx_p, formal_parameters_list_loc); - - /* Check duplication of formal parameters names */ - while (!token_is (TOK_CLOSE_PAREN)) - { - current_token_must_be (TOK_NAME); - - lit_literal_t current_parameter_name_lit = lit_get_literal_by_cp (token_data_as_lit_cp ()); - locus current_parameter_loc = tok.loc; - - skip_token (ctx_p); - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - } - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_emit_error_on_eval_and_arguments (current_parameter_name_lit, - current_parameter_loc); - } - - while (!token_is (TOK_CLOSE_PAREN)) - { - current_token_must_be (TOK_NAME); - - if (lit_utf8_iterator_pos_cmp (tok.loc, current_parameter_loc) != 0) - { - lit_literal_t iter_parameter_name_lit = lit_get_literal_by_cp (token_data_as_lit_cp ()); - - if (lit_literal_equal_type (current_parameter_name_lit, iter_parameter_name_lit)) - { - PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Duplication of literal '%s' in FormalParameterList is not allowed in strict mode", - tok.loc, lit_literal_to_str_internal_buf (iter_parameter_name_lit)); - } - } - - skip_token (ctx_p); - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - } - else - { - current_token_must_be (TOK_CLOSE_PAREN); - } - } - - seek_token (ctx_p, current_parameter_loc); - - JERRY_ASSERT (lit_utf8_iterator_pos_cmp (tok.loc, current_parameter_loc) == 0); - skip_token (ctx_p); - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - } - else - { - current_token_must_be (TOK_CLOSE_PAREN); - } - } - - seek_token (ctx_p, body_loc); - } - - if (out_formal_parameters_num_p != NULL) - { - *out_formal_parameters_num_p = formal_parameters_num; - } - - jsp_start_parse_source_element_list (ctx_p, - parent_scope_or_bc_header_cp, - parent_scope_processed_scopes_counter, - SCOPE_TYPE_FUNCTION); - - return func; -} - -static void -jsp_finish_parse_function_scope (jsp_ctx_t *ctx_p) -{ - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_BRACE); -} - -static void -dump_get_value_for_state_if_const (jsp_ctx_t *ctx_p, - jsp_state_t *state_p) -{ - // todo: pass to dumper - (void) ctx_p; - - JERRY_ASSERT (state_p->state > JSP_STATE_EXPR__BEGIN - && state_p->state < JSP_STATE_EXPR__END); - JERRY_ASSERT (!state_p->is_value_based_reference); - - if (jsp_is_string_lit_operand (state_p->u.expression.operand) - || jsp_is_number_lit_operand (state_p->u.expression.operand) - || jsp_is_regexp_lit_operand (state_p->u.expression.operand) - || jsp_is_smallint_operand (state_p->u.expression.operand) - || jsp_is_simple_value_operand (state_p->u.expression.operand)) - { - jsp_operand_t reg = tmp_operand (); - - dump_variable_assignment (ctx_p, reg, state_p->u.expression.operand); - - state_p->u.expression.operand = reg; - } -} - -static bool -dump_get_value_for_state_if_ref (jsp_ctx_t *ctx_p, - jsp_state_t *state_p, - bool is_dump_for_value_based_refs_only, - bool is_check_only) -{ - // TODO: pass to dumper - (void) ctx_p; - - jsp_operand_t obj = state_p->u.expression.operand; - jsp_operand_t prop_name = state_p->u.expression.prop_name_operand; - bool is_value_based_reference = state_p->is_value_based_reference; - - jsp_operand_t val = jsp_make_uninitialized_operand (); - - bool is_dump = false; - - if (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE) - { - if (is_value_based_reference) - { - if (!state_p->is_get_value_dumped_for_main_operand) - { - JERRY_ASSERT (!state_p->is_get_value_dumped_for_prop_operand); - /* FIXME: - if (jsp_is_identifier_operand (obj)) - { - is_dump = true; - - if (!is_check_only) - { - jsp_operand_t general_reg = tmp_operand (); - - dump_variable_assignment (ctx_p, general_reg, obj); - - state_p->u.expression.operand = general_reg; - state_p->is_get_value_dumped_for_main_operand = true; - } - } - - if (jsp_is_identifier_operand (prop_name)) - { - is_dump = true; - - if (!is_check_only) - { - jsp_operand_t general_reg = tmp_operand (); - - dump_variable_assignment (ctx_p, general_reg, prop_name); - - state_p->u.expression.prop_name_operand = general_reg; - state_p->is_get_value_dumped_for_prop_operand = true; - } - } - */ - } - } - } - else - { - if (is_value_based_reference) - { - is_dump = true; - - JERRY_ASSERT (!jsp_is_empty_operand (prop_name)); - - if (!is_check_only) - { - jsp_operand_t reg = tmp_operand (); - - dump_prop_getter (ctx_p, reg, obj, prop_name); - - val = reg; - - state_p->is_get_value_dumped_for_main_operand = true; - state_p->is_get_value_dumped_for_prop_operand = true; - } - } - else if (!is_dump_for_value_based_refs_only - && (jsp_is_identifier_operand (obj) || jsp_is_this_operand (obj))) - { - if (!state_p->is_get_value_dumped_for_main_operand) - { - is_dump = true; - - if (!is_check_only) - { - jsp_operand_t general_reg = tmp_operand (); - - dump_variable_assignment (ctx_p, general_reg, obj); - - val = general_reg; - - state_p->is_get_value_dumped_for_main_operand = true; - } - } - } - - if (!is_check_only && is_dump) - { - JERRY_ASSERT (state_p->is_get_value_dumped_for_main_operand); - JERRY_ASSERT (!state_p->is_value_based_reference || state_p->is_get_value_dumped_for_prop_operand); - - state_p->u.expression.operand = val; - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - } - - return is_dump; -} - -static void -dump_get_value_for_prev_states (jsp_ctx_t *ctx_p, - jsp_state_t *state_p) -{ - jsp_state_t *dump_state_border_for_p = jsp_get_prev_state (state_p); - jsp_state_t *first_state_to_dump_for_p = state_p; - - while (dump_state_border_for_p != NULL) - { - if (dump_state_border_for_p->state > JSP_STATE_EXPR__BEGIN - && dump_state_border_for_p->req_state < JSP_STATE_EXPR__END - && (!dump_state_border_for_p->is_get_value_dumped_for_main_operand - || !dump_state_border_for_p->is_get_value_dumped_for_prop_operand)) - { - first_state_to_dump_for_p = dump_state_border_for_p; - dump_state_border_for_p = jsp_get_prev_state (dump_state_border_for_p); - } - else - { - break; - } - } - - JERRY_ASSERT (first_state_to_dump_for_p != NULL); - - jsp_state_t *state_iter_p = first_state_to_dump_for_p; - - while (state_iter_p != state_p) - { - dump_get_value_for_state_if_ref (ctx_p, state_iter_p, false, false); - - state_iter_p = jsp_get_next_state (state_iter_p); - } -} - -static void -dump_get_value_if_ref (jsp_ctx_t *ctx_p, - jsp_state_t *state_p, - bool is_dump_for_value_based_refs_only) -{ - if (dump_get_value_for_state_if_ref (ctx_p, state_p, is_dump_for_value_based_refs_only, true)) - { - dump_get_value_for_prev_states (ctx_p, state_p); - - dump_get_value_for_state_if_ref (ctx_p, state_p, is_dump_for_value_based_refs_only, false); - } -} - -static vm_instr_counter_t -jsp_start_call_dump (jsp_ctx_t *ctx_p, - jsp_state_t *state_p, - vm_idx_t *out_state_p) -{ - bool is_value_based_reference = state_p->is_value_based_reference; - - opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; - - jsp_operand_t obj, val; - - if (is_value_based_reference) - { - obj = state_p->u.expression.operand; - jsp_operand_t prop_name = state_p->u.expression.prop_name_operand; - - if (jsp_is_identifier_operand (obj) - && !state_p->is_get_value_dumped_for_main_operand) - { - jsp_operand_t reg = tmp_operand (); - - dump_variable_assignment (ctx_p, reg, obj); - - obj = reg; - - state_p->is_get_value_dumped_for_main_operand = true; - } - - val = tmp_operand (); - dump_prop_getter (ctx_p, val, obj, prop_name); - - state_p->is_get_value_dumped_for_prop_operand = true; - - call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_HAVE_THIS_ARG); - - /* - * Presence of explicit 'this' argument implies that this is not Direct call to Eval - * - * See also: - * ECMA-262 v5, 15.2.2.1 - */ - } - else - { - dump_get_value_for_state_if_const (ctx_p, state_p); - - obj = state_p->u.expression.operand; - - if (dumper_is_eval_literal (obj)) - { - call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM); - } - - /* - * Note: - * If function is called through Identifier, then the obj should be an Identifier reference, - * not register variable. - * Otherwise, if function is called immediately, without reference (for example, through anonymous - * function expression), the obj should be a register variable. - * - * See also: - * vm_helper_call_get_call_flags_and_this_arg - */ - val = obj; - - state_p->is_get_value_dumped_for_main_operand = true; - } - - - vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); - - jsp_operand_t ret = tmp_operand (); - vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, VARG_CALL_EXPR, ret, val); - - *out_state_p = dumper_save_reg_alloc_counter (ctx_p); - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - - if (call_flags != OPCODE_CALL_FLAGS__EMPTY) - { - if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - dump_call_additional_info (ctx_p, call_flags, obj); - } - else - { - dump_call_additional_info (ctx_p, call_flags, empty_operand ()); - } - } - - state_p->u.expression.operand = ret; - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - - return varg_header_pos; -} /* jsp_start_call_dump */ - -static void -jsp_finish_call_dump (jsp_ctx_t *ctx_p, - uint32_t args_num, - vm_instr_counter_t header_pos, - vm_idx_t reg_alloc_saved_state) -{ - (void) ctx_p; - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - - rewrite_varg_header_set_args_count (ctx_p, args_num, header_pos); -} /* jsp_finish_call_dump */ - -static vm_instr_counter_t __attr_unused___ -jsp_start_construct_dump (jsp_ctx_t *ctx_p, - jsp_state_t *state_p, - vm_idx_t *out_state_p) -{ - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_for_state_if_const (ctx_p, state_p); - - state_p->is_get_value_dumped_for_main_operand = true; - - vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); - - jsp_operand_t ret = tmp_operand (); - vm_instr_counter_t pos = dump_varg_header_for_rewrite (ctx_p, - VARG_CONSTRUCT_EXPR, - ret, - state_p->u.expression.operand); - state_p->u.expression.operand = ret; - - *out_state_p = dumper_save_reg_alloc_counter (ctx_p); - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - - return pos; -} /* jsp_start_construct_dump */ - -static void -jsp_finish_construct_dump (jsp_ctx_t *ctx_p, - uint32_t args_num, - vm_instr_counter_t header_pos, - vm_idx_t reg_alloc_saved_state) -{ - // TODO: pass to dumper - (void) ctx_p; - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - - rewrite_varg_header_set_args_count (ctx_p, args_num, header_pos); -} /* jsp_finish_construct_dump */ - -static void -jsp_add_nested_jump_to_rewrite_chain (jsp_ctx_t *ctx_p, - vm_instr_counter_t *in_out_rewrite_chain_p) -{ - // TODO: pass to dumper - (void) ctx_p; - - *in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, true, false, false, - empty_operand (), - *in_out_rewrite_chain_p); -} - -static void -jsp_add_simple_jump_to_rewrite_chain (jsp_ctx_t *ctx_p, - vm_instr_counter_t *in_out_rewrite_chain_p) -{ - // TODO: pass to dumper - (void) ctx_p; - - *in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, false, false, false, - empty_operand (), - *in_out_rewrite_chain_p); -} - -static void -jsp_add_conditional_jump_to_rewrite_chain (jsp_ctx_t *ctx_p, - vm_instr_counter_t *in_out_rewrite_chain_p, - bool is_inverted_condition, - jsp_operand_t condition) -{ - // TODO: pass to dumper - (void) ctx_p; - - *in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, false, true, is_inverted_condition, - condition, - *in_out_rewrite_chain_p); -} - -static uint32_t -jsp_rewrite_jumps_chain (jsp_ctx_t *ctx_p, - vm_instr_counter_t *rewrite_chain_p, - vm_instr_counter_t target_oc) -{ - // TODO: pass to dumper - (void) ctx_p; - - uint32_t count = 0; - - while (*rewrite_chain_p != MAX_OPCODES) - { - count++; - - *rewrite_chain_p = rewrite_simple_or_nested_jump_and_get_next (ctx_p, - *rewrite_chain_p, - target_oc); - } - - return count; -} /* jsp_rewrite_jumps_chain */ - -static bool -jsp_is_assignment_expression_end (jsp_state_t *current_state_p) -{ - jsp_token_type_t tt = lexer_get_token_type (tok); - - JERRY_ASSERT (current_state_p->state == JSP_STATE_EXPR_UNARY - || current_state_p->state == JSP_STATE_EXPR_MULTIPLICATIVE - || current_state_p->state == JSP_STATE_EXPR_ADDITIVE - || current_state_p->state == JSP_STATE_EXPR_SHIFT - || current_state_p->state == JSP_STATE_EXPR_RELATIONAL - || current_state_p->state == JSP_STATE_EXPR_EQUALITY - || current_state_p->state == JSP_STATE_EXPR_BITWISE_AND - || current_state_p->state == JSP_STATE_EXPR_BITWISE_XOR - || current_state_p->state == JSP_STATE_EXPR_BITWISE_OR); - - if ((tt >= TOKEN_TYPE__MULTIPLICATIVE_BEGIN - && tt <= TOKEN_TYPE__MULTIPLICATIVE_END) - || (tt >= TOKEN_TYPE__ADDITIVE_BEGIN - && tt <= TOKEN_TYPE__ADDITIVE_END) - || (tt >= TOKEN_TYPE__SHIFT_BEGIN - && tt <= TOKEN_TYPE__SHIFT_END) - || (tt >= TOKEN_TYPE__RELATIONAL_BEGIN - && tt <= TOKEN_TYPE__RELATIONAL_END) - || (tt >= TOKEN_TYPE__EQUALITY_BEGIN - && tt <= TOKEN_TYPE__EQUALITY_END) - || (tt == TOK_AND) - || (tt == TOK_XOR) - || (tt == TOK_OR) - || (tt == TOK_DOUBLE_AND) - || (tt == TOK_DOUBLE_OR) - || (tt == TOK_QUERY)) - { - return false; - } - - return true; -} - -static bool -jsp_dump_unary_op (jsp_ctx_t *ctx_p, - jsp_state_t *state_p, - jsp_state_t *substate_p) -{ - vm_op_t opcode; - - bool is_combined_with_assignment = false; - - jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); - JERRY_ASSERT (prev_state_p != NULL); - - bool is_try_combine_with_assignment = (prev_state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE - && prev_state_p->u.expression.token_type == TOK_EQ - && !prev_state_p->is_value_based_reference - && !prev_state_p->is_need_retval - && jsp_is_assignment_expression_end (state_p)); - - jsp_token_type_t tt = state_p->u.expression.token_type; - state_p->u.expression.token_type = TOK_EMPTY; - - bool is_dump_simple_assignment = false; - bool is_dump_undefined_or_boolean_true = true; /* the value is not valid until is_dump_simple_assignment is set */ - - switch (tt) - { - case TOK_DOUBLE_PLUS: - case TOK_DOUBLE_MINUS: - { - if (!substate_p->is_value_based_reference) - { - if (!jsp_is_dump_mode (ctx_p)) - { - if (jsp_is_identifier_operand (substate_p->u.expression.operand)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (substate_p->u.expression.operand, - jsp_is_strict_mode (ctx_p), - tok.loc); - } - else - { - PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); - } - } - } - - opcode = (tt == TOK_DOUBLE_PLUS ? VM_OP_PRE_INCR : VM_OP_PRE_DECR); - break; - } - case TOK_PLUS: - { - dump_get_value_if_ref (ctx_p, substate_p, true); - - opcode = VM_OP_UNARY_PLUS; - break; - } - case TOK_MINUS: - { - dump_get_value_if_ref (ctx_p, substate_p, true); - - opcode = VM_OP_UNARY_MINUS; - break; - } - case TOK_COMPL: - { - dump_get_value_if_ref (ctx_p, substate_p, true); - - opcode = VM_OP_B_NOT; - break; - } - case TOK_NOT: - { - dump_get_value_if_ref (ctx_p, substate_p, true); - - opcode = VM_OP_LOGICAL_NOT; - break; - } - case TOK_KW_DELETE: - { - if (substate_p->is_value_based_reference) - { - opcode = VM_OP_DELETE_PROP; - } - else if (jsp_is_identifier_operand (substate_p->u.expression.operand)) - { - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_delete (jsp_is_strict_mode (ctx_p), tok.loc); - } - - opcode = VM_OP_DELETE_VAR; - } - else - { - opcode = VM_OP__COUNT /* invalid opcode as it will not be used */; - - is_dump_simple_assignment = true; - is_dump_undefined_or_boolean_true = false; /* dump boolean true value */ - } - break; - } - case TOK_KW_VOID: - { - dump_get_value_if_ref (ctx_p, substate_p, false); - - opcode = VM_OP__COUNT /* invalid opcode as it will not be used */; - - is_dump_simple_assignment = true; - is_dump_undefined_or_boolean_true = true; /* dump undefined value */ - break; - } - default: - { - dump_get_value_if_ref (ctx_p, substate_p, true); - - JERRY_ASSERT (tt == TOK_KW_TYPEOF); - - opcode = VM_OP_TYPEOF; - break; - } - } - - jsp_operand_t dst = tmp_operand (); - - if (!substate_p->is_value_based_reference) - { - dump_get_value_for_state_if_const (ctx_p, substate_p); - } - - if (dump_get_value_for_state_if_ref (ctx_p, substate_p, false, true)) - { - dump_get_value_for_prev_states (ctx_p, substate_p); - } - - if (is_try_combine_with_assignment - && (is_dump_simple_assignment - || !substate_p->is_value_based_reference - || opcode == VM_OP_DELETE_PROP)) - { - dst = prev_state_p->u.expression.operand; - - is_combined_with_assignment = true; - } - - if (is_dump_simple_assignment) - { - if (is_dump_undefined_or_boolean_true) - { - dump_variable_assignment (ctx_p, dst, jsp_make_simple_value_operand (ECMA_SIMPLE_VALUE_UNDEFINED)); - } - else - { - dump_variable_assignment (ctx_p, dst, jsp_make_simple_value_operand (ECMA_SIMPLE_VALUE_TRUE)); - } - } - else - { - if (substate_p->is_value_based_reference) - { - if (opcode == VM_OP_DELETE_PROP) - { - dump_binary_op (ctx_p, - VM_OP_DELETE_PROP, - dst, - substate_p->u.expression.operand, - substate_p->u.expression.prop_name_operand); - } - else - { - JERRY_ASSERT (opcode == VM_OP_PRE_INCR || opcode == VM_OP_PRE_DECR); - - jsp_operand_t reg = tmp_operand (); - - dump_prop_getter (ctx_p, reg, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand); - - dump_unary_op (ctx_p, opcode, reg, reg); - - dump_prop_setter (ctx_p, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand, reg); - - dst = reg; - } - } - else - { - JERRY_ASSERT (!substate_p->is_value_based_reference); - - dump_unary_op (ctx_p, opcode, dst, substate_p->u.expression.operand); - } - } - - JERRY_ASSERT (!state_p->is_value_based_reference); - state_p->u.expression.operand = dst; - - return is_combined_with_assignment; -} - -static bool -jsp_dump_binary_op (jsp_ctx_t *ctx_p, - jsp_state_t *state_p, - jsp_state_t *substate_p) -{ - vm_op_t opcode; - - bool is_combined_with_assignment = false; - - jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); - JERRY_ASSERT (prev_state_p != NULL); - - bool is_try_combine_with_assignment = (prev_state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE - && prev_state_p->u.expression.token_type == TOK_EQ - && !prev_state_p->is_value_based_reference - && !prev_state_p->is_need_retval - && jsp_is_assignment_expression_end (state_p)); - - jsp_token_type_t tt = state_p->u.expression.token_type; - state_p->u.expression.token_type = TOK_EMPTY; - - switch (tt) - { - case TOK_MULT: - { - opcode = VM_OP_MULTIPLICATION; - break; - } - case TOK_DIV: - { - opcode = VM_OP_DIVISION; - break; - } - case TOK_MOD: - { - opcode = VM_OP_REMAINDER; - break; - } - case TOK_PLUS: - { - opcode = VM_OP_ADDITION; - break; - } - case TOK_MINUS: - { - opcode = VM_OP_SUBSTRACTION; - break; - } - case TOK_LSHIFT: - { - opcode = VM_OP_B_SHIFT_LEFT; - break; - } - case TOK_RSHIFT: - { - opcode = VM_OP_B_SHIFT_RIGHT; - break; - } - case TOK_RSHIFT_EX: - { - opcode = VM_OP_B_SHIFT_URIGHT; - break; - } - case TOK_LESS: - { - opcode = VM_OP_LESS_THAN; - break; - } - case TOK_GREATER: - { - opcode = VM_OP_GREATER_THAN; - break; - } - case TOK_LESS_EQ: - { - opcode = VM_OP_LESS_OR_EQUAL_THAN; - break; - } - case TOK_GREATER_EQ: - { - opcode = VM_OP_GREATER_OR_EQUAL_THAN; - break; - } - case TOK_KW_INSTANCEOF: - { - opcode = VM_OP_INSTANCEOF; - break; - } - case TOK_KW_IN: - { - opcode = VM_OP_IN; - break; - } - case TOK_DOUBLE_EQ: - { - opcode = VM_OP_EQUAL_VALUE; - break; - } - case TOK_NOT_EQ: - { - opcode = VM_OP_NOT_EQUAL_VALUE; - break; - } - case TOK_TRIPLE_EQ: - { - opcode = VM_OP_EQUAL_VALUE_TYPE; - break; - } - case TOK_NOT_DOUBLE_EQ: - { - opcode = VM_OP_NOT_EQUAL_VALUE_TYPE; - break; - } - case TOK_AND: - { - opcode = VM_OP_B_AND; - break; - } - case TOK_XOR: - { - opcode = VM_OP_B_XOR; - break; - } - default: - { - JERRY_ASSERT (tt == TOK_OR); - - opcode = VM_OP_B_OR; - break; - } - } - - jsp_operand_t dst, op1, op2; - - if (!state_p->is_value_based_reference) - { - dump_get_value_for_state_if_const (ctx_p, state_p); - } - - if (!substate_p->is_value_based_reference) - { - dump_get_value_for_state_if_const (ctx_p, substate_p); - } - - if (!state_p->is_value_based_reference - && !substate_p->is_value_based_reference) - { - if (dump_get_value_for_state_if_ref (ctx_p, state_p, false, true) - || dump_get_value_for_state_if_ref (ctx_p, substate_p, false, true)) - { - dump_get_value_for_prev_states (ctx_p, state_p); - } - - op1 = state_p->u.expression.operand; - op2 = substate_p->u.expression.operand; - - if (is_try_combine_with_assignment) - { - dst = prev_state_p->u.expression.operand; - - is_combined_with_assignment = true; - } - else if (jsp_is_register_operand (op1)) - { - dst = op1; - } - else - { - dst = tmp_operand (); - } - } - else - { - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_if_ref (ctx_p, substate_p, true); - - if (is_try_combine_with_assignment) - { - dst = prev_state_p->u.expression.operand; - - is_combined_with_assignment = true; - } - else - { - dst = state_p->u.expression.operand; - } - - op1 = state_p->u.expression.operand; - op2 = substate_p->u.expression.operand; - } - - JERRY_ASSERT (!state_p->is_value_based_reference); - state_p->u.expression.operand = dst; - - dump_binary_op (ctx_p, opcode, dst, op1, op2); - - return is_combined_with_assignment; -} - -static lit_cpointer_t -jsp_get_prop_name_after_dot (void) -{ - if (token_is (TOK_NAME)) - { - return token_data_as_lit_cp (); - } - else if (is_keyword (lexer_get_token_type (tok))) - { - const char *s = lexer_token_type_to_string (lexer_get_token_type (tok)); - lit_literal_t lit = lit_find_or_create_literal_from_utf8_string ((lit_utf8_byte_t *) s, - (lit_utf8_size_t) strlen (s)); - if (lit == NULL) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected identifier"); - } - - return rcs_cpointer_compress (lit); - } - else if (token_is (TOK_BOOL) || token_is (TOK_NULL)) - { - lit_magic_string_id_t id = (token_is (TOK_NULL) - ? LIT_MAGIC_STRING_NULL - : (tok.uid ? LIT_MAGIC_STRING_TRUE : LIT_MAGIC_STRING_FALSE)); - lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (lit_get_magic_string_utf8 (id), - lit_get_magic_string_size (id)); - - return rcs_cpointer_compress (lit); - } - else - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected identifier"); - } -} /* jsp_get_prop_name_after_dot */ - -#ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER -/** - * Try to perform move (allocation) of variables' values on registers - * - * Note: - * The optimization is only applied to functions - * - * @return number of instructions removed from function's header - */ -static void -jsp_try_move_vars_to_regs (jsp_ctx_t *ctx_p, - jsp_state_t *state_p) -{ - // TODO: pass to dumper - (void) ctx_p; - - JERRY_ASSERT (state_p->state == JSP_STATE_SOURCE_ELEMENTS); - - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - bool may_replace_vars_with_regs = bc_header_p->is_vars_and_args_to_regs_possible; - - if (may_replace_vars_with_regs) - { - /* no subscopes, as no function declarations / eval etc. in the scope */ - JERRY_ASSERT (bc_header_p->func_scopes_count == 0); - - vm_instr_counter_t instr_pos = 0u; - - const vm_instr_counter_t header_oc = instr_pos++; - op_meta header_opm = dumper_get_op_meta (ctx_p, header_oc); - JERRY_ASSERT (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N || header_opm.op.op_idx == VM_OP_FUNC_DECL_N); - - uint32_t args_num; - - if (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N) - { - args_num = header_opm.op.data.func_expr_n.arg_list; - } - else - { - args_num = header_opm.op.data.func_decl_n.arg_list; - } - - dumper_start_move_of_vars_to_regs (ctx_p); - - mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, bc_header_p->declarations_cp); - lit_cpointer_t *var_declarations_p = (lit_cpointer_t *) (declarations_p + bc_header_p->func_scopes_count); - - /* remove declarations of variables with names equal to an argument's name */ - uint32_t var_decl_index = 0; - while (var_decl_index < bc_header_p->var_decls_count) - { - lit_cpointer_t var_name_lit_cp = var_declarations_p[var_decl_index]; - - op_meta var_decl_opm; - var_decl_opm.op.op_idx = VM_OP_VAR_DECL; - var_decl_opm.lit_id[0] = var_name_lit_cp; - var_decl_opm.lit_id[1] = NOT_A_LITERAL; - var_decl_opm.lit_id[2] = NOT_A_LITERAL; - - bool is_removed = false; - - for (vm_instr_counter_t arg_index = instr_pos; - arg_index < instr_pos + args_num; - arg_index++) - { - op_meta meta_opm = dumper_get_op_meta (ctx_p, arg_index); - JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); - - if (meta_opm.lit_id[1].packed_value == var_name_lit_cp.packed_value) - { - var_declarations_p[var_decl_index] = NOT_A_LITERAL; - - is_removed = true; - break; - } - } - - if (!is_removed) - { - if (dumper_try_replace_identifier_name_with_reg (ctx_p, bc_header_p, &var_decl_opm)) - { - var_declarations_p[var_decl_index] = NOT_A_LITERAL; - } - } - - var_decl_index++; - } - - if (dumper_start_move_of_args_to_regs (ctx_p, args_num)) - { - bc_header_p->is_args_moved_to_regs = true; - bc_header_p->is_no_lex_env = true; - - dumper_rewrite_op_meta (ctx_p, header_oc, header_opm); - - /* - * Mark duplicated arguments names as empty, - * leaving only last declaration for each duplicated - * argument name - */ - for (vm_instr_counter_t arg1_index = instr_pos; - arg1_index < instr_pos + args_num; - arg1_index++) - { - op_meta meta_opm1 = dumper_get_op_meta (ctx_p, arg1_index); - JERRY_ASSERT (meta_opm1.op.op_idx == VM_OP_META); - - for (vm_instr_counter_t arg2_index = (vm_instr_counter_t) (arg1_index + 1u); - arg2_index < instr_pos + args_num; - arg2_index++) - { - op_meta meta_opm2 = dumper_get_op_meta (ctx_p, arg2_index); - JERRY_ASSERT (meta_opm2.op.op_idx == VM_OP_META); - - if (meta_opm1.lit_id[1].packed_value == meta_opm2.lit_id[1].packed_value) - { - meta_opm1.op.data.meta.data_1 = VM_IDX_EMPTY; - meta_opm1.lit_id[1] = NOT_A_LITERAL; - - dumper_rewrite_op_meta (ctx_p, arg1_index, meta_opm1); - - break; - } - } - } - - for (vm_instr_counter_t arg_instr_pos = instr_pos; - arg_instr_pos < instr_pos + args_num; - arg_instr_pos++) - { - op_meta meta_opm = dumper_get_op_meta (ctx_p, arg_instr_pos); - JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); - - opcode_meta_type meta_type = (opcode_meta_type) meta_opm.op.data.meta.type; - JERRY_ASSERT (meta_type == OPCODE_META_TYPE_VARG); - if (meta_opm.op.data.meta.data_1 == VM_IDX_EMPTY) - { - JERRY_ASSERT (meta_opm.lit_id[1].packed_value == NOT_A_LITERAL.packed_value); - - dumper_alloc_reg_for_unused_arg (ctx_p); - } - else - { - /* the varg specifies argument name, and so should be a string literal */ - JERRY_ASSERT (meta_opm.lit_id[1].packed_value != NOT_A_LITERAL.packed_value); - - bool is_replaced = dumper_try_replace_identifier_name_with_reg (ctx_p, bc_header_p, &meta_opm); - JERRY_ASSERT (is_replaced); - } - } - } - } -} -#endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ - -static void -parse_expression_inside_parens_begin (jsp_ctx_t *ctx_p) -{ - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); -} - -static void -parse_expression_inside_parens_end (jsp_ctx_t *ctx_p) -{ - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); -} - -static jsp_state_t * -jsp_find_unnamed_label (jsp_ctx_t *ctx_p, - bool is_label_for_break, - bool *out_is_simply_jumpable_p) -{ - *out_is_simply_jumpable_p = true; - - jsp_state_t *state_p = jsp_state_top (ctx_p); - while (state_p != NULL) - { - if (state_p->state == JSP_STATE_SOURCE_ELEMENTS) - { - break; - } - - if (state_p->is_simply_jumpable_border) - { - *out_is_simply_jumpable_p = false; - } - - bool is_iterational_stmt = (state_p->state == JSP_STATE_STAT_WHILE - || state_p->state == JSP_STATE_STAT_DO_WHILE - || state_p->state == JSP_STATE_STAT_FOR_IN - || state_p->state == JSP_STATE_STAT_FOR_IN_EXPR - || state_p->state == JSP_STATE_STAT_FOR_IN_FINISH - || state_p->state == JSP_STATE_STAT_FOR_INCREMENT); - bool is_switch_stmt = (state_p->state == JSP_STATE_STAT_SWITCH_BRANCH_EXPR); - - if ((is_switch_stmt && is_label_for_break) || is_iterational_stmt) - { - return state_p; - } - - state_p = jsp_get_prev_state (state_p); - } - - return NULL; -} - -static jsp_state_t * -jsp_find_named_label (jsp_ctx_t *ctx_p, - lit_cpointer_t name_cp, - bool *out_is_simply_jumpable_p) -{ - *out_is_simply_jumpable_p = true; - - jsp_state_t *state_p = jsp_state_top (ctx_p); - jsp_state_t *last_non_named_label_state_p = NULL; - - while (state_p != NULL) - { - if (state_p->state == JSP_STATE_SOURCE_ELEMENTS) - { - break; - } - - if (state_p->is_simply_jumpable_border) - { - *out_is_simply_jumpable_p = false; - } - - if (state_p->state != JSP_STATE_STAT_NAMED_LABEL) - { - last_non_named_label_state_p = state_p; - } - else if (state_p->u.named_label.name_cp.packed_value == name_cp.packed_value) - { - return last_non_named_label_state_p; - } - - state_p = jsp_get_prev_state (state_p); - } - - return NULL; -} - -static void -insert_semicolon (jsp_ctx_t *ctx_p) -{ - bool is_new_line_occured = lexer_is_preceded_by_newlines (tok); - bool is_close_brace_or_eof = (token_is (TOK_CLOSE_BRACE) || token_is (TOK_EOF)); - - if (!is_new_line_occured && !is_close_brace_or_eof) - { - if (token_is (TOK_SEMICOLON)) - { - skip_token (ctx_p); - } - else if (!token_is (TOK_EOF)) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected either ';' or newline token"); - } - } -} - - -#define JSP_COMPLETE_STATEMENT_PARSE() \ -do \ -{ \ - JERRY_ASSERT (substate_p == NULL); \ - state_p->state = JSP_STATE_STAT_STATEMENT; \ - JERRY_ASSERT (!state_p->is_completed); \ - dumper_new_statement (ctx_p); \ -} \ -while (0) - -#define JSP_PUSH_STATE_AND_STATEMENT_PARSE(s) \ -do \ -{ \ - state_p->state = (s); \ - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); \ - dumper_new_statement (ctx_p); \ -} \ -while (0) - -#define JSP_FINISH_SUBEXPR() \ - JERRY_ASSERT (substate_p == jsp_state_top (ctx_p)); \ - substate_p = NULL; \ - jsp_state_pop (ctx_p); \ - JERRY_ASSERT (state_p == jsp_state_top (ctx_p)); - -#define JSP_ASSIGNMENT_EXPR_COMBINE() \ - jsp_state_pop (ctx_p); \ - state_p = jsp_state_top (ctx_p); \ - JERRY_ASSERT (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE); \ - JERRY_ASSERT (!state_p->is_need_retval); \ - state_p->state = JSP_STATE_EXPR_ASSIGNMENT; \ - state_p->u.expression.operand = empty_operand (); \ - state_p->u.expression.token_type = TOK_EMPTY; \ - state_p->is_completed = true; - -/** - * Parse source element list - */ -static void __attr_noinline___ -jsp_parse_source_element_list (jsp_ctx_t *ctx_p, - scope_type_t scope_type) -{ - jsp_start_parse_source_element_list (ctx_p, MEM_CP_NULL, 0, scope_type); - - while (true) - { - bool is_source_elements_list_end = false; - - bool is_stmt_list_end = false, is_stmt_list_control_flow_exit_stmt_occured = false; - - bool is_subexpr_end = false; - - jsp_state_t *state_p = jsp_state_top (ctx_p), *substate_p = NULL; - - if (state_p->state == state_p->req_state && state_p->is_completed) - { - if (jsp_is_stack_contains_exactly_one_element (ctx_p)) - { - JERRY_ASSERT (state_p->state == JSP_STATE_SOURCE_ELEMENTS); - - jsp_state_pop (ctx_p); - - break; - } - else - { - is_subexpr_end = (state_p->state > JSP_STATE_EXPR__BEGIN && state_p->state < JSP_STATE_EXPR__END); - - if (is_subexpr_end) - { - substate_p = state_p; - state_p = jsp_get_prev_state (state_p); - } - else - { - if (state_p->req_state == JSP_STATE_SOURCE_ELEMENTS) - { - is_source_elements_list_end = true; - } - else if (state_p->req_state == JSP_STATE_STAT_STATEMENT_LIST) - { - is_stmt_list_end = true; - - is_stmt_list_control_flow_exit_stmt_occured = state_p->is_stmt_list_control_flow_exit_stmt_occured; - } - - JERRY_ASSERT (state_p->is_completed); - jsp_state_pop (ctx_p); - - state_p = jsp_state_top (ctx_p); - } - } - } - else - { - /* - * Any expression production, if parse of the production is not stopped because required - * production type was reached, eventually becomes Expression production. - * - * Because there are no any expression production higher than Expression, - * its invalid to reach Expression production type if required production is lower - * (i.e. is not Expression production type). - */ - JERRY_ASSERT (!(state_p->state == JSP_STATE_EXPR_EXPRESSION && state_p->req_state != JSP_STATE_EXPR_EXPRESSION)); - } - - const bool in_allowed = !state_p->is_no_in_mode; - - if (state_p->state == JSP_STATE_SOURCE_ELEMENTS) - { - scope_type_t scope_type = jsp_get_scope_type (ctx_p); - - jsp_token_type_t end_token_type = (scope_type == SCOPE_TYPE_FUNCTION) ? TOK_CLOSE_BRACE : TOK_EOF; - - if (token_is (end_token_type)) - { -#ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER - if (jsp_is_dump_mode (ctx_p)) - { - jsp_try_move_vars_to_regs (ctx_p, state_p); - } - else - { - scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); - - JERRY_ASSERT (!scope_p->is_vars_and_args_to_regs_possible); - - /* - * We don't try to perform replacement of local variables with registers for global code, eval code. - * - * For global and eval code the replacement can be connected with side effects, - * that currently can only be figured out in runtime. For example, a variable - * can be redefined as accessor property of the Global object. - */ - bool is_vars_and_args_to_regs_possible = (jsp_get_scope_type (ctx_p) == SCOPE_TYPE_FUNCTION - && !scope_p->ref_eval /* 'eval' can reference variables in a way, - * that can't be figured out through static - * analysis */ - && !scope_p->ref_arguments /* 'arguments' variable, if declared, - * should not be moved to a register, - * as it is currently declared in - * function's lexical environment - * (generally, the problem is the same, - * as with function's arguments) */ - && !scope_p->contains_with /* 'with' create new lexical environment - * and so can change way identifier - * is evaluated */ - && !scope_p->contains_try /* same for 'catch' */ - && !scope_p->contains_delete /* 'delete' handles variable's names, - * not values */ - && !scope_p->contains_functions); /* nested functions - * can reference variables - * of current function */ - - scope_p->is_vars_and_args_to_regs_possible = is_vars_and_args_to_regs_possible; - } -#endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ - - rewrite_reg_var_decl (ctx_p, state_p->u.source_elements.reg_var_decl_oc); - - if (scope_type == SCOPE_TYPE_FUNCTION) - { - dump_ret (ctx_p); - - if (jsp_is_dump_mode (ctx_p)) - { - bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); - - if (parser_show_instrs) - { - bc_print_instrs (bc_header_p); - } - - mem_cpointer_t parent_bc_header_cp = state_p->u.source_elements.u.parent_bc_header_cp; - bytecode_data_header_t *parent_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, - parent_bc_header_cp); - - jsp_set_current_bytecode_header (ctx_p, parent_bc_header_p); - } - else - { - scopes_tree parent_scope_p = MEM_CP_GET_POINTER (scopes_tree_int, - state_p->u.source_elements.u.parent_scopes_tree_node_cp); - - jsp_set_current_scopes_tree_node (ctx_p, parent_scope_p); - } - } - else if (scope_type == SCOPE_TYPE_GLOBAL) - { - dump_ret (ctx_p); - } - else - { - JERRY_ASSERT (scope_type == SCOPE_TYPE_EVAL); - - dump_retval (ctx_p, jsp_make_reg_operand (VM_REG_SPECIAL_EVAL_RET)); - } - - jsp_set_scope_type (ctx_p, state_p->u.source_elements.parent_scope_type); - jsp_set_processed_child_scopes_counter (ctx_p, - state_p->u.source_elements.parent_scope_child_scopes_counter); - - dumper_restore_reg_alloc_ctx (ctx_p, - state_p->u.source_elements.saved_reg_next, - state_p->u.source_elements.saved_reg_max_for_temps, - true); - - state_p->is_completed = true; - } - else - { - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_SOURCE_ELEMENTS); - } - } - else if (state_p->state == JSP_STATE_EXPR_EMPTY) - { - rescan_regexp_token (ctx_p); - - /* no subexpressions can occur, as expression parse is just started */ - JERRY_ASSERT (!is_subexpr_end); - JERRY_ASSERT (!state_p->is_completed); - - jsp_token_type_t tt = lexer_get_token_type (tok); - if ((tt >= TOKEN_TYPE__UNARY_BEGIN - && tt <= TOKEN_TYPE__UNARY_END) - || tt == TOK_KW_DELETE - || tt == TOK_KW_VOID - || tt == TOK_KW_TYPEOF) - { - /* UnaryExpression */ - state_p->state = JSP_STATE_EXPR_UNARY; - state_p->u.expression.token_type = tt; - - if (!jsp_is_dump_mode (ctx_p)) - { - if (tt == TOK_KW_DELETE) - { - scopes_tree_set_contains_delete (jsp_get_current_scopes_tree_node (ctx_p)); - } - } - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_UNARY, in_allowed); - } - else if (token_is (TOK_KW_FUNCTION)) - { - /* FunctionExpression */ - state_p->state = JSP_STATE_EXPR_FUNCTION; - } - else if (token_is (TOK_OPEN_SQUARE)) - { - dump_get_value_for_prev_states (ctx_p, state_p); - - /* ArrayLiteral */ - vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); - - state_p->u.expression.operand = tmp_operand (); - vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, - VARG_ARRAY_DECL, - state_p->u.expression.operand, - empty_operand ()); - - vm_idx_t reg_alloc_saved_state1 = dumper_save_reg_alloc_counter (ctx_p); - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - - state_p->state = JSP_STATE_EXPR_ARRAY_LITERAL; - state_p->is_list_in_process = true; - state_p->u.expression.u.varg_sequence.list_length = 0; - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; - state_p->u.expression.u.varg_sequence.header_pos = varg_header_pos; - } - else if (token_is (TOK_OPEN_BRACE)) - { - dump_get_value_for_prev_states (ctx_p, state_p); - - /* ObjectLiteral */ - vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); - - state_p->u.expression.operand = tmp_operand (); - vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, - VARG_OBJ_DECL, - state_p->u.expression.operand, - empty_operand ()); - - vm_idx_t reg_alloc_saved_state1 = dumper_save_reg_alloc_counter (ctx_p); - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_start_checking_of_prop_names (); - } - - state_p->state = JSP_STATE_EXPR_OBJECT_LITERAL; - state_p->is_list_in_process = true; - state_p->u.expression.u.varg_sequence.list_length = 0; - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; - state_p->u.expression.u.varg_sequence.header_pos = varg_header_pos; - } - else - { - /* MemberExpression (PrimaryExpression is immediately promoted to MemberExpression) */ - state_p->state = JSP_STATE_EXPR_MEMBER; - - switch (lexer_get_token_type (tok)) - { - case TOK_OPEN_PAREN: - { - state_p->u.expression.token_type = TOK_OPEN_PAREN; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - - break; - } - case TOK_KW_THIS: - { - state_p->u.expression.operand = jsp_make_this_operand (); - break; - } - case TOK_KW_NEW: - { - state_p->state = JSP_STATE_EXPR_MEMBER; - state_p->u.expression.token_type = TOK_KW_NEW; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_MEMBER, true); - break; - } - case TOK_NAME: - { - if (!jsp_is_dump_mode (ctx_p)) - { - if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "arguments")) - { - scopes_tree_set_arguments_used (jsp_get_current_scopes_tree_node (ctx_p)); - } - - if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "eval")) - { - scopes_tree_set_eval_used (jsp_get_current_scopes_tree_node (ctx_p)); - } - } - - state_p->u.expression.operand = jsp_make_identifier_operand (token_data_as_lit_cp ()); - - break; - } - case TOK_REGEXP: - { - state_p->u.expression.operand = jsp_make_regexp_lit_operand (token_data_as_lit_cp ()); - break; - } - case TOK_NULL: - { - state_p->u.expression.operand = jsp_make_simple_value_operand (ECMA_SIMPLE_VALUE_NULL); - break; - } - case TOK_BOOL: - { - ecma_simple_value_t simple_value = (bool) token_data () ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; - state_p->u.expression.operand = jsp_make_simple_value_operand (simple_value); - break; - } - case TOK_SMALL_INT: - { - state_p->u.expression.operand = jsp_make_smallint_operand ((uint8_t) token_data ()); - break; - } - case TOK_NUMBER: - { - state_p->u.expression.operand = jsp_make_number_lit_operand (token_data_as_lit_cp ()); - break; - } - case TOK_STRING: - { - state_p->u.expression.operand = jsp_make_string_lit_operand (token_data_as_lit_cp ()); - break; - } - default: - { - EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, - "Unknown token %s", - lexer_token_type_to_string (lexer_get_token_type (tok))); - } - } - } - - skip_token (ctx_p); - } - else if (state_p->state == JSP_STATE_EXPR_FUNCTION) - { - JERRY_ASSERT (!state_p->is_completed); - - if (is_source_elements_list_end) - { - jsp_finish_parse_function_scope (ctx_p); - - state_p->state = JSP_STATE_EXPR_MEMBER; - } - else - { - jsp_operand_t name = empty_operand (); - - if (token_is (TOK_NAME)) - { - name = jsp_make_string_lit_operand (token_data_as_lit_cp ()); - skip_token (ctx_p); - } - - state_p->u.expression.operand = jsp_start_parse_function_scope (ctx_p, name, true, NULL); - } - } - else if (state_p->state == JSP_STATE_EXPR_DATA_PROP_DECL) - { - JERRY_ASSERT (!state_p->is_completed); - - if (is_subexpr_end) - { - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - jsp_operand_t prop_name = state_p->u.expression.operand; - jsp_operand_t value = substate_p->u.expression.operand; - - JERRY_ASSERT (jsp_is_string_lit_operand (prop_name)); - - dump_prop_name_and_value (ctx_p, prop_name, value); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_add_prop_name (prop_name, PROP_DATA); - } - - state_p->is_completed = true; - - JSP_FINISH_SUBEXPR (); - } - else - { - JERRY_ASSERT (jsp_is_empty_operand (state_p->u.expression.operand)); - state_p->u.expression.operand = parse_property_name (); - skip_token (ctx_p); - - JERRY_ASSERT (token_is (TOK_COLON)); - skip_token (ctx_p); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - } - } - else if (state_p->state == JSP_STATE_EXPR_ACCESSOR_PROP_DECL) - { - JERRY_ASSERT (!state_p->is_completed); - - if (is_source_elements_list_end) - { - jsp_finish_parse_function_scope (ctx_p); - - jsp_operand_t prop_name = state_p->u.expression.u.accessor_prop_decl.prop_name; - jsp_operand_t func = state_p->u.expression.operand; - bool is_setter = state_p->u.expression.u.accessor_prop_decl.is_setter; - - if (is_setter) - { - dump_prop_setter_decl (ctx_p, prop_name, func); - } - else - { - dump_prop_getter_decl (ctx_p, prop_name, func); - } - - state_p->is_completed = true; - } - else - { - bool is_setter; - - current_token_must_be (TOK_NAME); - - lit_cpointer_t lit_cp = token_data_as_lit_cp (); - - skip_token (ctx_p); - - if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (lit_cp), "get")) - { - is_setter = false; - } - else if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (lit_cp), "set")) - { - is_setter = true; - } - else - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid property declaration"); - } - - jsp_operand_t prop_name = parse_property_name (); - skip_token (ctx_p); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_add_prop_name (prop_name, is_setter ? PROP_SET : PROP_GET); - } - - size_t formal_parameters_num; - const jsp_operand_t func = jsp_start_parse_function_scope (ctx_p, - empty_operand (), - true, - &formal_parameters_num); - - size_t req_num_of_formal_parameters = is_setter ? 1 : 0; - - if (req_num_of_formal_parameters != formal_parameters_num) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid number of formal parameters"); - } - - JERRY_ASSERT (jsp_is_empty_operand (state_p->u.expression.operand)); - state_p->u.expression.operand = func; - - state_p->u.expression.u.accessor_prop_decl.prop_name = prop_name; - state_p->u.expression.u.accessor_prop_decl.is_setter = is_setter; - } - } - else if (state_p->state == JSP_STATE_EXPR_OBJECT_LITERAL) - { - JERRY_ASSERT (!state_p->is_completed); - JERRY_ASSERT (state_p->is_list_in_process); - - if (is_subexpr_end) - { - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_DATA_PROP_DECL - || substate_p->state == JSP_STATE_EXPR_ACCESSOR_PROP_DECL); - - state_p->u.expression.u.varg_sequence.list_length++; - - dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - } - else - { - current_token_must_be (TOK_CLOSE_BRACE); - } - - JSP_FINISH_SUBEXPR (); - } - - if (token_is (TOK_CLOSE_BRACE)) - { - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_duplication_of_prop_names (jsp_is_strict_mode (ctx_p), tok.loc); - } - - skip_token (ctx_p); - - uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; - vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; - - rewrite_varg_header_set_args_count (ctx_p, list_len, header_pos); - - dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); - - state_p->state = JSP_STATE_EXPR_MEMBER; - state_p->is_list_in_process = false; - } - else - { - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); - - locus start_pos = tok.loc; - skip_token (ctx_p); - - jsp_state_expr_t expr_type; - if (token_is (TOK_COLON)) - { - expr_type = JSP_STATE_EXPR_DATA_PROP_DECL; - } - else - { - expr_type = JSP_STATE_EXPR_ACCESSOR_PROP_DECL; - } - - seek_token (ctx_p, start_pos); - - jsp_push_new_expr_state (ctx_p, expr_type, expr_type, true); - } - } - else if (state_p->state == JSP_STATE_EXPR_ARRAY_LITERAL) - { - JERRY_ASSERT (!state_p->is_completed); - JERRY_ASSERT (state_p->is_list_in_process); - - if (is_subexpr_end) - { - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - dump_varg (ctx_p, substate_p->u.expression.operand); - - JSP_FINISH_SUBEXPR (); - - state_p->u.expression.u.varg_sequence.list_length++; - - dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - } - else - { - current_token_must_be (TOK_CLOSE_SQUARE); - } - } - else - { - if (token_is (TOK_CLOSE_SQUARE)) - { - skip_token (ctx_p); - - uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; - vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; - - rewrite_varg_header_set_args_count (ctx_p, list_len, header_pos); - - dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); - - state_p->state = JSP_STATE_EXPR_MEMBER; - state_p->is_list_in_process = false; - } - else if (token_is (TOK_COMMA)) - { - while (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - - vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); - - jsp_operand_t reg = tmp_operand (); - dump_variable_assignment (ctx_p, - reg, - jsp_make_simple_value_operand (ECMA_SIMPLE_VALUE_ARRAY_HOLE)); - dump_varg (ctx_p, reg); - - state_p->u.expression.u.varg_sequence.list_length++; - - dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); - } - } - else - { - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - } - } - } - else if (state_p->state == JSP_STATE_EXPR_MEMBER) - { - if (state_p->is_completed) - { - if (token_is (TOK_OPEN_PAREN)) - { - state_p->state = JSP_STATE_EXPR_CALL; - state_p->is_completed = false; - - /* propagate to CallExpression */ - state_p->state = JSP_STATE_EXPR_CALL; - } - else - { - /* propagate to LeftHandSideExpression */ - state_p->state = JSP_STATE_EXPR_LEFTHANDSIDE; - JERRY_ASSERT (state_p->is_completed); - } - } - else - { - if (is_subexpr_end) - { - if (state_p->is_list_in_process) - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_KW_NEW); - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - dump_varg (ctx_p, substate_p->u.expression.operand); - - JSP_FINISH_SUBEXPR (); - - dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); - - state_p->u.expression.u.varg_sequence.list_length++; - - if (token_is (TOK_CLOSE_PAREN)) - { - state_p->u.expression.token_type = TOK_EMPTY; - state_p->is_list_in_process = false; - - uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; - vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; - - jsp_finish_construct_dump (ctx_p, - list_len, - header_pos, - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); - } - else - { - current_token_must_be (TOK_COMMA); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); - } - - skip_token (ctx_p); - } - else if (state_p->u.expression.token_type == TOK_OPEN_PAREN) - { - JERRY_ASSERT (jsp_is_empty_operand (state_p->u.expression.operand)); - - state_p->u.expression.operand = substate_p->u.expression.operand; - state_p->u.expression.prop_name_operand = substate_p->u.expression.prop_name_operand; - state_p->is_value_based_reference = substate_p->is_value_based_reference; - - state_p->u.expression.token_type = TOK_EMPTY; - - JSP_FINISH_SUBEXPR (); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); - } - else if (state_p->u.expression.token_type == TOK_KW_NEW) - { - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_MEMBER); - JERRY_ASSERT (jsp_is_empty_operand (state_p->u.expression.operand)); - JERRY_ASSERT (!jsp_is_empty_operand (substate_p->u.expression.operand)); - - state_p->u.expression.operand = substate_p->u.expression.operand; - state_p->u.expression.prop_name_operand = substate_p->u.expression.prop_name_operand; - state_p->is_value_based_reference = substate_p->is_value_based_reference; - - JSP_FINISH_SUBEXPR (); - - bool is_arg_list_implicit = true; - bool is_arg_list_empty = true; - - if (token_is (TOK_OPEN_PAREN)) - { - skip_token (ctx_p); - - is_arg_list_implicit = false; - - if (token_is (TOK_CLOSE_PAREN)) - { - skip_token (ctx_p); - } - else - { - is_arg_list_empty = false; - } - } - - if (!is_arg_list_implicit && !is_arg_list_empty) - { - dump_get_value_for_prev_states (ctx_p, state_p); - - vm_idx_t reg_alloc_saved_state1; - vm_instr_counter_t header_pos = jsp_start_construct_dump (ctx_p, state_p, ®_alloc_saved_state1); - - state_p->is_list_in_process = true; - state_p->u.expression.u.varg_sequence.list_length = 0; - state_p->u.expression.u.varg_sequence.header_pos = header_pos; - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - } - else - { - vm_idx_t reg_alloc_saved_state1; - vm_instr_counter_t header_pos = jsp_start_construct_dump (ctx_p, state_p, ®_alloc_saved_state1); - - state_p->u.expression.token_type = TOK_EMPTY; - - if (is_arg_list_implicit) - { - state_p->state = JSP_STATE_EXPR_MEMBER; - state_p->is_completed = true; - } - - jsp_finish_construct_dump (ctx_p, 0, header_pos, reg_alloc_saved_state1); - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - } - else - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_OPEN_SQUARE); - state_p->u.expression.token_type = TOK_EMPTY; - - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_SQUARE); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - state_p->u.expression.prop_name_operand = substate_p->u.expression.operand; - state_p->is_value_based_reference = true; - - JSP_FINISH_SUBEXPR (); - } - } - else if (token_is (TOK_OPEN_SQUARE)) - { - skip_token (ctx_p); - - state_p->u.expression.token_type = TOK_OPEN_SQUARE; - - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_for_state_if_const (ctx_p, state_p); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - } - else if (token_is (TOK_DOT)) - { - skip_token (ctx_p); - - lit_cpointer_t prop_name = jsp_get_prop_name_after_dot (); - skip_token (ctx_p); - - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_for_state_if_const (ctx_p, state_p); - - state_p->u.expression.prop_name_operand = tmp_operand (); - dump_variable_assignment (ctx_p, - state_p->u.expression.prop_name_operand, - jsp_make_string_lit_operand (prop_name)); - - state_p->is_value_based_reference = true; - } - else - { - state_p->is_completed = true; - } - } - } - else if (state_p->state == JSP_STATE_EXPR_CALL) - { - JERRY_ASSERT (!state_p->is_completed); - - if (is_subexpr_end) - { - if (state_p->is_list_in_process) - { - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - dump_varg (ctx_p, substate_p->u.expression.operand); - - JSP_FINISH_SUBEXPR (); - - dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); - - state_p->u.expression.u.varg_sequence.list_length++; - - if (token_is (TOK_CLOSE_PAREN)) - { - state_p->is_list_in_process = false; - - uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; - vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; - - jsp_finish_call_dump (ctx_p, - list_len, - header_pos, - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - else - { - current_token_must_be (TOK_COMMA); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); - } - } - else - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_OPEN_SQUARE); - state_p->u.expression.token_type = TOK_EMPTY; - - current_token_must_be (TOK_CLOSE_SQUARE); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - state_p->u.expression.prop_name_operand = substate_p->u.expression.operand; - state_p->is_value_based_reference = true; - - JSP_FINISH_SUBEXPR (); - } - - skip_token (ctx_p); - } - else - { - if (token_is (TOK_OPEN_PAREN)) - { - skip_token (ctx_p); - - dump_get_value_for_prev_states (ctx_p, state_p); - - vm_idx_t reg_alloc_saved_state1; - - vm_instr_counter_t header_pos = jsp_start_call_dump (ctx_p, state_p, ®_alloc_saved_state1); - - if (token_is (TOK_CLOSE_PAREN)) - { - skip_token (ctx_p); - - jsp_finish_call_dump (ctx_p, 0, header_pos, reg_alloc_saved_state1); - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - else - { - state_p->is_list_in_process = true; - state_p->u.expression.u.varg_sequence.list_length = 0; - state_p->u.expression.u.varg_sequence.header_pos = header_pos; - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; - state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - } - } - else if (token_is (TOK_OPEN_SQUARE)) - { - skip_token (ctx_p); - - state_p->u.expression.token_type = TOK_OPEN_SQUARE; - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_for_state_if_const (ctx_p, state_p); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - } - else if (token_is (TOK_DOT)) - { - skip_token (ctx_p); - - lit_cpointer_t prop_name = jsp_get_prop_name_after_dot (); - skip_token (ctx_p); - - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_for_state_if_const (ctx_p, state_p); - - state_p->u.expression.prop_name_operand = tmp_operand (); - dump_variable_assignment (ctx_p, - state_p->u.expression.prop_name_operand, - jsp_make_string_lit_operand (prop_name)); - - state_p->is_value_based_reference = true; - } - else - { - state_p->state = JSP_STATE_EXPR_LEFTHANDSIDE; - state_p->is_completed = true; - } - } - } - else if (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE) - { - JERRY_ASSERT (state_p->is_completed); - - if (is_subexpr_end) - { - jsp_token_type_t tt = state_p->u.expression.token_type; - JERRY_ASSERT (tt >= TOKEN_TYPE__ASSIGNMENTS_BEGIN && tt <= TOKEN_TYPE__ASSIGNMENTS_END); - - dump_get_value_for_prev_states (ctx_p, state_p); - dump_get_value_if_ref (ctx_p, substate_p, true); - - if (tt == TOK_EQ) - { - if (state_p->is_need_retval) - { - dump_get_value_if_ref (ctx_p, substate_p, false); - dump_get_value_for_state_if_const (ctx_p, substate_p); - } - - if (state_p->is_value_based_reference) - { - dump_get_value_for_state_if_const (ctx_p, substate_p); - - dump_prop_setter (ctx_p, - state_p->u.expression.operand, - state_p->u.expression.prop_name_operand, - substate_p->u.expression.operand); - - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - else - { - dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); - } - - state_p->u.expression.operand = substate_p->u.expression.operand; - - if (state_p->is_need_retval) - { - JERRY_ASSERT (jsp_is_register_operand (state_p->u.expression.operand)); - } - else - { - state_p->u.expression.operand = empty_operand (); - } - } - else - { - vm_op_t opcode; - - if (tt == TOK_MULT_EQ) - { - opcode = VM_OP_MULTIPLICATION; - } - else if (tt == TOK_DIV_EQ) - { - opcode = VM_OP_DIVISION; - } - else if (tt == TOK_MOD_EQ) - { - opcode = VM_OP_REMAINDER; - } - else if (tt == TOK_PLUS_EQ) - { - opcode = VM_OP_ADDITION; - } - else if (tt == TOK_MINUS_EQ) - { - opcode = VM_OP_SUBSTRACTION; - } - else if (tt == TOK_LSHIFT_EQ) - { - opcode = VM_OP_B_SHIFT_LEFT; - } - else if (tt == TOK_RSHIFT_EQ) - { - opcode = VM_OP_B_SHIFT_RIGHT; - } - else if (tt == TOK_RSHIFT_EX_EQ) - { - opcode = VM_OP_B_SHIFT_URIGHT; - } - else if (tt == TOK_AND_EQ) - { - opcode = VM_OP_B_AND; - } - else if (tt == TOK_XOR_EQ) - { - opcode = VM_OP_B_XOR; - } - else - { - JERRY_ASSERT (tt == TOK_OR_EQ); - - opcode = VM_OP_B_OR; - } - - dump_get_value_for_state_if_const (ctx_p, substate_p); - - if (state_p->is_value_based_reference) - { - jsp_operand_t reg = tmp_operand (); - - dump_prop_getter (ctx_p, reg, state_p->u.expression.operand, state_p->u.expression.prop_name_operand); - - dump_binary_op (ctx_p, opcode, reg, reg, substate_p->u.expression.operand); - - dump_prop_setter (ctx_p, - state_p->u.expression.operand, - state_p->u.expression.prop_name_operand, - reg); - - state_p->u.expression.operand = reg; - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - else if (state_p->is_need_retval) - { - jsp_operand_t reg = tmp_operand (); - - dump_binary_op (ctx_p, opcode, reg, state_p->u.expression.operand, substate_p->u.expression.operand); - - dump_variable_assignment (ctx_p, state_p->u.expression.operand, reg); - - state_p->u.expression.operand = reg; - } - else - { - dump_binary_op (ctx_p, opcode, state_p->u.expression.operand, - state_p->u.expression.operand, - substate_p->u.expression.operand); - - state_p->u.expression.operand = empty_operand (); - } - } - - state_p->state = JSP_STATE_EXPR_ASSIGNMENT; - state_p->u.expression.token_type = TOK_EMPTY; - state_p->is_completed = true; - - JSP_FINISH_SUBEXPR (); - } - else - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); - - if (token_is (TOK_DOUBLE_PLUS) - && !lexer_is_preceded_by_newlines (tok)) - { - const jsp_operand_t reg = tmp_operand (); - - if (state_p->is_value_based_reference) - { - const jsp_operand_t val = tmp_operand (); - - dump_prop_getter (ctx_p, val, state_p->u.expression.operand, state_p->u.expression.prop_name_operand); - - dump_unary_op (ctx_p, VM_OP_POST_INCR, reg, val); - - dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, val); - - state_p->u.expression.operand = reg; - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - else if (jsp_is_identifier_operand (state_p->u.expression.operand)) - { - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand, - jsp_is_strict_mode (ctx_p), - tok.loc); - } - - dump_unary_op (ctx_p, VM_OP_POST_INCR, reg, state_p->u.expression.operand); - - state_p->u.expression.operand = reg; - } - else - { - PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); - } - - state_p->state = JSP_STATE_EXPR_UNARY; - JERRY_ASSERT (state_p->is_completed); - - skip_token (ctx_p); - } - else if (token_is (TOK_DOUBLE_MINUS) - && !lexer_is_preceded_by_newlines (tok)) - { - const jsp_operand_t reg = tmp_operand (); - - if (state_p->is_value_based_reference) - { - const jsp_operand_t val = tmp_operand (); - - dump_prop_getter (ctx_p, val, state_p->u.expression.operand, state_p->u.expression.prop_name_operand); - - dump_unary_op (ctx_p, VM_OP_POST_DECR, reg, val); - - dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, val); - - state_p->u.expression.operand = reg; - state_p->u.expression.prop_name_operand = empty_operand (); - state_p->is_value_based_reference = false; - } - else if (jsp_is_identifier_operand (state_p->u.expression.operand)) - { - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand, - jsp_is_strict_mode (ctx_p), - tok.loc); - } - - dump_unary_op (ctx_p, VM_OP_POST_DECR, reg, state_p->u.expression.operand); - - state_p->u.expression.operand = reg; - } - else - { - PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); - } - - state_p->state = JSP_STATE_EXPR_UNARY; - JERRY_ASSERT (state_p->is_completed); - - skip_token (ctx_p); - } - else - { - jsp_token_type_t tt = lexer_get_token_type (tok); - - if (tt >= TOKEN_TYPE__ASSIGNMENTS_BEGIN && tt <= TOKEN_TYPE__ASSIGNMENTS_END) - { - if (!state_p->is_value_based_reference) - { - if (!jsp_is_dump_mode (ctx_p)) - { - if (!jsp_is_identifier_operand (state_p->u.expression.operand)) - { - PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); - } - else - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand, - jsp_is_strict_mode (ctx_p), - tok.loc); - } - } - } - - /* skip the assignment operator */ - skip_token (ctx_p); - state_p->u.expression.token_type = tt; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed); - } - else - { - state_p->state = JSP_STATE_EXPR_UNARY; - } - } - } - } - else if (state_p->state > JSP_STATE_EXPR__SIMPLE_BEGIN - && state_p->state < JSP_STATE_EXPR__SIMPLE_END) - { - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_MULTIPLICATIVE == JSP_STATE_EXPR_UNARY + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_ADDITIVE == JSP_STATE_EXPR_MULTIPLICATIVE + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_SHIFT == JSP_STATE_EXPR_ADDITIVE + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_RELATIONAL == JSP_STATE_EXPR_SHIFT + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_EQUALITY == JSP_STATE_EXPR_RELATIONAL + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_AND == JSP_STATE_EXPR_EQUALITY + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_XOR == JSP_STATE_EXPR_BITWISE_AND + 1u); - JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_OR == JSP_STATE_EXPR_BITWISE_XOR + 1u); - - if (state_p->is_completed) - { - if (state_p->state == JSP_STATE_EXPR_BITWISE_OR) - { - state_p->state = JSP_STATE_EXPR_LOGICAL_AND; - - state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES; - } - else - { - JERRY_ASSERT (state_p->state != state_p->req_state); - JERRY_ASSERT (state_p->state == JSP_STATE_EXPR_UNARY); - - state_p->state = (jsp_state_expr_t) (state_p->state + 1u); - } - - state_p->is_completed = false; - } - else if (is_subexpr_end) - { - bool is_combined_with_assignment; - - if (state_p->state == JSP_STATE_EXPR_UNARY) - { - is_combined_with_assignment = jsp_dump_unary_op (ctx_p, state_p, substate_p); - } - else - { - is_combined_with_assignment = jsp_dump_binary_op (ctx_p, state_p, substate_p); - } - - JSP_FINISH_SUBEXPR (); - - if (is_combined_with_assignment) - { - JSP_ASSIGNMENT_EXPR_COMBINE (); - } - } - else - { - JERRY_ASSERT (!state_p->is_completed); - - - jsp_state_expr_t new_state, subexpr_type; - - bool is_simple = true; - - jsp_token_type_t tt = lexer_get_token_type (tok); - - if (tt >= TOKEN_TYPE__MULTIPLICATIVE_BEGIN && tt <= TOKEN_TYPE__MULTIPLICATIVE_END) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_MULTIPLICATIVE); - - new_state = JSP_STATE_EXPR_MULTIPLICATIVE; - subexpr_type = JSP_STATE_EXPR_UNARY; - } - else if (tt >= TOKEN_TYPE__ADDITIVE_BEGIN && tt <= TOKEN_TYPE__ADDITIVE_END) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_ADDITIVE); - - new_state = JSP_STATE_EXPR_ADDITIVE; - subexpr_type = JSP_STATE_EXPR_MULTIPLICATIVE; - } - else if (tt >= TOKEN_TYPE__SHIFT_BEGIN && tt <= TOKEN_TYPE__SHIFT_END) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_SHIFT); - - new_state = JSP_STATE_EXPR_SHIFT; - subexpr_type = JSP_STATE_EXPR_ADDITIVE; - } - else if ((tt >= TOKEN_TYPE__RELATIONAL_BEGIN && tt <= TOKEN_TYPE__RELATIONAL_END) - || tt == TOK_KW_INSTANCEOF - || (in_allowed && tt == TOK_KW_IN)) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_RELATIONAL); - - new_state = JSP_STATE_EXPR_RELATIONAL; - subexpr_type = JSP_STATE_EXPR_SHIFT; - } - else if (tt >= TOKEN_TYPE__EQUALITY_BEGIN && tt <= TOKEN_TYPE__EQUALITY_END) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_EQUALITY); - - new_state = JSP_STATE_EXPR_EQUALITY; - subexpr_type = JSP_STATE_EXPR_RELATIONAL; - } - else if (tt == TOK_AND) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_AND); - - new_state = JSP_STATE_EXPR_BITWISE_AND; - subexpr_type = JSP_STATE_EXPR_EQUALITY; - } - else if (tt == TOK_XOR) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_XOR); - - new_state = JSP_STATE_EXPR_BITWISE_XOR; - subexpr_type = JSP_STATE_EXPR_BITWISE_AND; - } - else if (tt == TOK_OR) - { - JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_OR); - - new_state = JSP_STATE_EXPR_BITWISE_OR; - subexpr_type = JSP_STATE_EXPR_BITWISE_XOR; - } - else - { - is_simple = false; - } - - if (!is_simple && state_p->req_state >= JSP_STATE_EXPR_LOGICAL_AND) - { - state_p->state = JSP_STATE_EXPR_LOGICAL_AND; - state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES; - } - else - { - JERRY_ASSERT (is_simple || state_p->req_state < JSP_STATE_EXPR__SIMPLE_END); - - if (!is_simple || state_p->req_state < new_state) - { - state_p->state = state_p->req_state; - - state_p->is_completed = true; - } - else - { - skip_token (ctx_p); - - state_p->state = new_state; - state_p->u.expression.token_type = tt; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, subexpr_type, in_allowed); - } - } - } - } - else if (state_p->state == JSP_STATE_EXPR_LOGICAL_AND) - { - if (state_p->is_completed) - { - /* propagate to LogicalOrExpression */ - state_p->state = JSP_STATE_EXPR_LOGICAL_OR; - - state_p->u.expression.u.logical_or.rewrite_chain = MAX_OPCODES; - - state_p->is_completed = false; - } - else - { - if (is_subexpr_end) - { - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_if_ref (ctx_p, substate_p, true); - - JERRY_ASSERT (state_p->u.expression.token_type == TOK_DOUBLE_AND); - - JERRY_ASSERT (jsp_is_register_operand (state_p->u.expression.operand)); - - dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); - - state_p->u.expression.token_type = TOK_EMPTY; - - JSP_FINISH_SUBEXPR (); - } - else - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); - - if (token_is (TOK_DOUBLE_AND)) - { - /* ECMA-262 v5, 11.11 (complex LogicalAndExpression) */ - skip_token (ctx_p); - - /* - * FIXME: - * Consider eliminating COMPLEX_PRODUCTION flag through implementing establishing a general operand - * management for expressions - */ - if (!state_p->is_complex_production) - { - state_p->is_complex_production = true; - - state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES; - - JERRY_ASSERT (!state_p->is_fixed_ret_operand); - - jsp_operand_t ret = tmp_operand (); - - dump_get_value_if_ref (ctx_p, state_p, true); - - dump_variable_assignment (ctx_p, ret, state_p->u.expression.operand); - - state_p->is_fixed_ret_operand = true; - state_p->u.expression.operand = ret; - } - - JERRY_ASSERT (state_p->is_complex_production); - - jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.expression.u.logical_and.rewrite_chain, - true, state_p->u.expression.operand); - - state_p->u.expression.token_type = TOK_DOUBLE_AND; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_BITWISE_OR, in_allowed); - } - else - { - /* end of LogicalAndExpression */ - JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); - - jsp_rewrite_jumps_chain (ctx_p, &state_p->u.expression.u.logical_and.rewrite_chain, - dumper_get_current_instr_counter (ctx_p)); - - state_p->is_complex_production = false; - - state_p->is_completed = true; - } - } - } - } - else if (state_p->state == JSP_STATE_EXPR_LOGICAL_OR) - { - if (state_p->is_completed) - { - /* switching to ConditionalExpression */ - if (token_is (TOK_QUERY)) - { - state_p->state = JSP_STATE_EXPR_CONDITION; - state_p->is_completed = false; - - /* ECMA-262 v5, 11.12 */ - skip_token (ctx_p); - - dump_get_value_if_ref (ctx_p, state_p, true); - dump_get_value_for_state_if_const (ctx_p, state_p); - - vm_instr_counter_t conditional_check_pos = dump_conditional_check_for_rewrite (ctx_p, - state_p->u.expression.operand); - state_p->u.expression.u.conditional.conditional_check_pos = conditional_check_pos; - - state_p->u.expression.token_type = TOK_QUERY; - - if (!state_p->is_fixed_ret_operand) - { - state_p->is_fixed_ret_operand = true; - state_p->u.expression.operand = tmp_operand (); - } - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - } - else - { - /* just propagate */ - state_p->state = JSP_STATE_EXPR_ASSIGNMENT; - JERRY_ASSERT (state_p->is_completed); - } - } - else - { - if (is_subexpr_end) - { - dump_get_value_if_ref (ctx_p, substate_p, true); - - JERRY_ASSERT (state_p->u.expression.token_type == TOK_DOUBLE_OR); - - JERRY_ASSERT (jsp_is_register_operand (state_p->u.expression.operand)); - dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); - - state_p->u.expression.token_type = TOK_EMPTY; - - JSP_FINISH_SUBEXPR (); - } - else - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); - - if (token_is (TOK_DOUBLE_OR)) - { - /* ECMA-262 v5, 11.11 (complex LogicalOrExpression) */ - skip_token (ctx_p); - - /* - * FIXME: - * Consider eliminating COMPLEX_PRODUCTION flag through implementing establishing a general operand - * management for expressions - */ - if (!state_p->is_complex_production) - { - state_p->is_complex_production = true; - - state_p->u.expression.u.logical_or.rewrite_chain = MAX_OPCODES; - - jsp_operand_t ret; - - if (state_p->is_fixed_ret_operand) - { - JERRY_ASSERT (jsp_is_register_operand (state_p->u.expression.operand)); - - ret = state_p->u.expression.operand; - } - else - { - ret = tmp_operand (); - - dump_get_value_if_ref (ctx_p, state_p, true); - - dump_variable_assignment (ctx_p, ret, state_p->u.expression.operand); - - state_p->is_fixed_ret_operand = true; - - state_p->u.expression.operand = ret; - } - } - - JERRY_ASSERT (state_p->is_complex_production); - - jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.expression.u.logical_or.rewrite_chain, - false, state_p->u.expression.operand); - - state_p->u.expression.token_type = TOK_DOUBLE_OR; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_LOGICAL_AND, in_allowed); - } - else - { - /* end of LogicalOrExpression */ - JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); - - jsp_rewrite_jumps_chain (ctx_p, &state_p->u.expression.u.logical_or.rewrite_chain, - dumper_get_current_instr_counter (ctx_p)); - - state_p->is_complex_production = false; - - state_p->is_completed = true; - } - } - } - } - else if (state_p->state == JSP_STATE_EXPR_ASSIGNMENT) - { - /* assignment production can't be continued at the point */ - JERRY_ASSERT (!is_subexpr_end); - - JERRY_ASSERT (state_p->is_completed); - JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); - - /* 'assignment expression' production can't be continued with an operator, - * so just propagating it to 'expression' production */ - state_p->is_completed = false; - state_p->state = JSP_STATE_EXPR_EXPRESSION; - } - else if (state_p->state == JSP_STATE_EXPR_CONDITION) - { - JERRY_ASSERT (!state_p->is_completed); - JERRY_ASSERT (is_subexpr_end); - - /* ECMA-262 v5, 11.12 */ - if (state_p->u.expression.token_type == TOK_QUERY) - { - current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON); - - JERRY_ASSERT (state_p->is_fixed_ret_operand); - JERRY_ASSERT (jsp_is_register_operand (state_p->u.expression.operand)); - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); - - dump_get_value_if_ref (ctx_p, substate_p, true); - - dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); - - JSP_FINISH_SUBEXPR (); - - state_p->u.expression.u.conditional.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); - - rewrite_conditional_check (ctx_p, state_p->u.expression.u.conditional.conditional_check_pos); - - state_p->u.expression.token_type = TOK_COLON; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed); - } - else - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_COLON); - - JERRY_ASSERT (state_p->is_fixed_ret_operand); - JERRY_ASSERT (jsp_is_register_operand (state_p->u.expression.operand)); - JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); - - dump_get_value_if_ref (ctx_p, substate_p, true); - - dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); - - JSP_FINISH_SUBEXPR (); - - rewrite_jump_to_end (ctx_p, state_p->u.expression.u.conditional.jump_to_end_pos); - - state_p->u.expression.token_type = TOK_EMPTY; - state_p->is_fixed_ret_operand = false; - - state_p->state = JSP_STATE_EXPR_ASSIGNMENT; - state_p->is_completed = true; - } - } - else if (state_p->state == JSP_STATE_EXPR_EXPRESSION) - { - /* ECMA-262 v5, 11.14 */ - - if (is_subexpr_end) - { - JERRY_ASSERT (state_p->u.expression.token_type == TOK_COMMA); - - dump_get_value_if_ref (ctx_p, substate_p, false); - - if (state_p->is_need_retval) - { - /* - * The operand should be already evaluated - * - * See also: - * 11.14, step 2 - */ - JERRY_ASSERT (!state_p->is_value_based_reference - && !jsp_is_identifier_operand (state_p->u.expression.operand)); - - state_p->u.expression.operand = substate_p->u.expression.operand; - } - - JSP_FINISH_SUBEXPR (); - } - else - { - JERRY_ASSERT (!state_p->is_completed); - - if (token_is (TOK_COMMA)) - { - skip_token (ctx_p); - - JERRY_ASSERT (!token_is (TOK_COMMA)); - - state_p->u.expression.token_type = TOK_COMMA; - - /* ECMA-262 v5, 11.14, step 2 */ - dump_get_value_if_ref (ctx_p, state_p, false); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed); - } - else - { - if (state_p->is_need_retval) - { - if (!state_p->is_value_based_reference) - { - dump_get_value_for_state_if_const (ctx_p, state_p); - } - } - - state_p->is_completed = true; - } - } - } - else if (state_p->state == JSP_STATE_STAT_EMPTY) - { - dumper_new_statement (ctx_p); - - if (token_is (TOK_KW_IF)) /* IfStatement */ - { - skip_token (ctx_p); - - parse_expression_inside_parens_begin (ctx_p); - - state_p->state = JSP_STATE_STAT_IF_BRANCH_START; - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, in_allowed); - } - else if (token_is (TOK_OPEN_BRACE)) - { - skip_token (ctx_p); - - state_p->state = JSP_STATE_STAT_BLOCK; - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - else if (token_is (TOK_KW_VAR)) - { - state_p->state = JSP_STATE_STAT_VAR_DECL; - state_p->var_decl = true; - } - else if (token_is (TOK_KW_DO) - || token_is (TOK_KW_WHILE) - || token_is (TOK_KW_FOR)) - { - state_p->u.statement.u.iterational.continues_rewrite_chain = MAX_OPCODES; - state_p->u.statement.u.iterational.continue_tgt_oc = MAX_OPCODES; - - if (token_is (TOK_KW_DO)) - { - vm_instr_counter_t next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p); - state_p->u.statement.u.iterational.u.loop_do_while.next_iter_tgt_pos = next_iter_tgt_pos; - skip_token (ctx_p); - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_DO_WHILE); - } - else if (token_is (TOK_KW_WHILE)) - { - skip_token (ctx_p); - - state_p->u.statement.u.iterational.u.loop_while.u.cond_expr_start_loc = tok.loc; - jsp_skip_braces (ctx_p, TOK_OPEN_PAREN); - - state_p->u.statement.u.iterational.u.loop_while.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); - - state_p->u.statement.u.iterational.u.loop_while.next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p); - - skip_token (ctx_p); - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_WHILE); - } - else - { - current_token_must_be_check_and_skip_it (ctx_p, TOK_KW_FOR); - - current_token_must_be (TOK_OPEN_PAREN); - - locus for_open_paren_loc, for_body_statement_loc; - - for_open_paren_loc = tok.loc; - - jsp_skip_braces (ctx_p, TOK_OPEN_PAREN); - skip_token (ctx_p); - - for_body_statement_loc = tok.loc; - - seek_token (ctx_p, for_open_paren_loc); - - bool is_plain_for = jsp_find_next_token_before_the_locus (ctx_p, - TOK_SEMICOLON, - for_body_statement_loc, - true); - seek_token (ctx_p, for_open_paren_loc); - - if (is_plain_for) - { - state_p->u.statement.u.iterational.u.loop_for.u1.body_loc = for_body_statement_loc; - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); - - // Initializer - if (token_is (TOK_KW_VAR)) - { - state_p->state = JSP_STATE_STAT_FOR_INIT_END; - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_VAR_DECL); - } - else - { - if (!token_is (TOK_SEMICOLON)) - { - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, false); - - jsp_state_top (ctx_p)->is_need_retval = false; - } - else - { - // Initializer is empty - } - state_p->state = JSP_STATE_STAT_FOR_INIT_END; - } - } - else - { - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); - - // Save Iterator location - state_p->u.statement.u.iterational.u.loop_for_in.u.iterator_expr_loc = tok.loc; - - while (lit_utf8_iterator_pos_cmp (tok.loc, for_body_statement_loc) < 0) - { - if (jsp_find_next_token_before_the_locus (ctx_p, - TOK_KW_IN, - for_body_statement_loc, - true)) - { - break; - } - else - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid for statement"); - } - } - - JERRY_ASSERT (token_is (TOK_KW_IN)); - skip_token (ctx_p); - - // Collection - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - state_p->state = JSP_STATE_STAT_FOR_IN; - } - } - } - else if (token_is (TOK_KW_SWITCH)) - { - skip_token (ctx_p); - - parse_expression_inside_parens_begin (ctx_p); - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - state_p->state = JSP_STATE_STAT_SWITCH; - } - else if (token_is (TOK_SEMICOLON)) - { - skip_token (ctx_p); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (token_is (TOK_KW_CONTINUE) - || token_is (TOK_KW_BREAK)) - { - bool is_break = token_is (TOK_KW_BREAK); - - skip_token (ctx_p); - - jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); - - if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) - { - prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true; - } - - jsp_state_t *labelled_stmt_p; - bool is_simply_jumpable = true; - - if (!lexer_is_preceded_by_newlines (tok) && token_is (TOK_NAME)) - { - /* break or continue on a label */ - labelled_stmt_p = jsp_find_named_label (ctx_p, token_data_as_lit_cp (), &is_simply_jumpable); - - if (labelled_stmt_p == NULL) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Label not found"); - } - - skip_token (ctx_p); - } - else - { - labelled_stmt_p = jsp_find_unnamed_label (ctx_p, is_break, &is_simply_jumpable); - - if (labelled_stmt_p == NULL) - { - if (is_break) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "No corresponding statement for the break"); - } - else - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "No corresponding statement for the continue"); - } - } - } - - insert_semicolon (ctx_p); - - JERRY_ASSERT (labelled_stmt_p != NULL); - - vm_instr_counter_t *rewrite_chain_p; - if (is_break) - { - rewrite_chain_p = &labelled_stmt_p->u.statement.breaks_rewrite_chain; - } - else - { - rewrite_chain_p = &labelled_stmt_p->u.statement.u.iterational.continues_rewrite_chain; - } - - if (is_simply_jumpable) - { - jsp_add_simple_jump_to_rewrite_chain (ctx_p, rewrite_chain_p); - } - else - { - jsp_add_nested_jump_to_rewrite_chain (ctx_p, rewrite_chain_p); - } - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (token_is (TOK_KW_RETURN)) - { - if (jsp_get_scope_type (ctx_p) != SCOPE_TYPE_FUNCTION) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Return is illegal"); - } - - jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); - - if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) - { - prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true; - } - - skip_token (ctx_p); - - if (!token_is (TOK_SEMICOLON) - && !lexer_is_preceded_by_newlines (tok) - && !token_is (TOK_CLOSE_BRACE)) - { - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - state_p->state = JSP_STATE_STAT_RETURN; - } - else - { - dump_ret (ctx_p); - JSP_COMPLETE_STATEMENT_PARSE (); - } - } - else if (token_is (TOK_KW_TRY)) - { - skip_token (ctx_p); - - if (!jsp_is_dump_mode (ctx_p)) - { - scopes_tree_set_contains_try (jsp_get_current_scopes_tree_node (ctx_p)); - } - - state_p->u.statement.u.try_statement.try_pos = dump_try_for_rewrite (ctx_p); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); - - state_p->is_simply_jumpable_border = true; - - state_p->state = JSP_STATE_STAT_TRY; - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - else if (token_is (TOK_KW_WITH)) - { - skip_token (ctx_p); - - if (jsp_is_strict_mode (ctx_p)) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "'with' expression is not allowed in strict mode."); - } - - parse_expression_inside_parens_begin (ctx_p); - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - state_p->state = JSP_STATE_STAT_WITH; - } - else if (token_is (TOK_KW_THROW)) - { - skip_token (ctx_p); - - jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); - - if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) - { - prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true; - } - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - state_p->state = JSP_STATE_STAT_THROW; - } - else if (token_is (TOK_KW_FUNCTION)) - { - skip_token (ctx_p); - - current_token_must_be (TOK_NAME); - - const jsp_operand_t func_name = jsp_make_string_lit_operand (token_data_as_lit_cp ()); - skip_token (ctx_p); - - state_p->state = JSP_STATE_FUNC_DECL_FINISH; - - jsp_start_parse_function_scope (ctx_p, func_name, false, NULL); - } - else - { - bool is_expression = true; - - if (token_is (TOK_NAME)) // LabelledStatement or ExpressionStatement - { - const token temp = tok; - skip_token (ctx_p); - if (token_is (TOK_COLON)) - { - skip_token (ctx_p); - - lit_cpointer_t name_cp; - name_cp.packed_value = temp.uid; - - bool is_simply_jumpable; - jsp_state_t *named_label_state_p = jsp_find_named_label (ctx_p, name_cp, &is_simply_jumpable); - - if (named_label_state_p != NULL) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Label is duplicated"); - } - - state_p->state = JSP_STATE_STAT_NAMED_LABEL; - state_p->u.named_label.name_cp = name_cp; - - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); - - is_expression = false; - } - else - { - seek_token (ctx_p, temp.loc); - } - } - - if (is_expression) - { - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - - if (jsp_get_scope_type (ctx_p) != SCOPE_TYPE_EVAL) - { - jsp_state_top (ctx_p)->is_need_retval = false; - } - - state_p->state = JSP_STATE_STAT_EXPRESSION; - } - } - } - else if (state_p->state == JSP_STATE_STAT_BLOCK) - { - JERRY_ASSERT (is_stmt_list_end); - - jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); - - if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) - { - prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = is_stmt_list_control_flow_exit_stmt_occured; - } - - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_BRACE); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (state_p->state == JSP_STATE_STAT_IF_BRANCH_START) - { - if (is_subexpr_end) // Finished parsing condition - { - parse_expression_inside_parens_end (ctx_p); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - jsp_operand_t cond = substate_p->u.expression.operand; - - state_p->u.statement.u.if_statement.conditional_check_pos = dump_conditional_check_for_rewrite (ctx_p, cond); - - JSP_FINISH_SUBEXPR (); - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_IF_BRANCH_START); - } - else - { - if (token_is (TOK_KW_ELSE)) - { - skip_token (ctx_p); - - state_p->u.statement.u.if_statement.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); - rewrite_conditional_check (ctx_p, state_p->u.statement.u.if_statement.conditional_check_pos); - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_IF_BRANCH_END); - } - else - { - rewrite_conditional_check (ctx_p, state_p->u.statement.u.if_statement.conditional_check_pos); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - } - } - else if (state_p->state == JSP_STATE_STAT_IF_BRANCH_END) - { - rewrite_jump_to_end (ctx_p, state_p->u.statement.u.if_statement.jump_to_end_pos); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (state_p->state == JSP_STATE_STAT_STATEMENT_LIST) - { - if (is_subexpr_end) - { - JSP_FINISH_SUBEXPR (); - } - - while (token_is (TOK_SEMICOLON)) - { - skip_token (ctx_p); - } - - if (token_is (TOK_CLOSE_BRACE) - || (token_is (TOK_KW_CASE) || token_is (TOK_KW_DEFAULT))) - { - state_p->is_completed = true; - } - else - { - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); - } - } - else if (state_p->state == JSP_STATE_STAT_VAR_DECL) - { - skip_token (ctx_p); - - locus name_loc = tok.loc; - - current_token_must_be (TOK_NAME); - - const lit_cpointer_t lit_cp = token_data_as_lit_cp (); - const jsp_operand_t name = jsp_make_string_lit_operand (lit_cp); - - skip_token (ctx_p); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), name_loc); - } - - if (!jsp_is_dump_mode (ctx_p)) - { - if (!scopes_tree_variable_declaration_exists (jsp_get_current_scopes_tree_node (ctx_p), lit_cp)) - { - dump_variable_declaration (ctx_p, lit_cp); - } - } - - if (token_is (TOK_EQ)) - { - seek_token (ctx_p, name_loc); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); - - jsp_state_top (ctx_p)->is_need_retval = false; - } - - state_p->state = JSP_STATE_STAT_VAR_DECL_FINISH; - } - else if (state_p->state == JSP_STATE_STAT_VAR_DECL_FINISH) - { - if (is_subexpr_end) - { - JSP_FINISH_SUBEXPR (); - } - - if (!token_is (TOK_COMMA)) - { - JSP_COMPLETE_STATEMENT_PARSE (); - - if (state_p->var_decl) - { - insert_semicolon (ctx_p); - } - } - else - { - state_p->state = JSP_STATE_STAT_VAR_DECL; - } - } - else if (state_p->state == JSP_STATE_STAT_DO_WHILE) - { - if (is_subexpr_end) - { - parse_expression_inside_parens_end (ctx_p); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - const jsp_operand_t cond = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - dump_continue_iterations_check (ctx_p, - state_p->u.statement.u.iterational.u.loop_do_while.next_iter_tgt_pos, - cond); - - state_p->state = JSP_STATE_STAT_ITER_FINISH; - } - else - { - assert_keyword (TOK_KW_WHILE); - skip_token (ctx_p); - - JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); - state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); - - parse_expression_inside_parens_begin (ctx_p); - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - } - } - else if (state_p->state == JSP_STATE_STAT_WHILE) - { - if (is_subexpr_end) - { - parse_expression_inside_parens_end (ctx_p); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - const jsp_operand_t cond = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_while.next_iter_tgt_pos, cond); - - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_while.u.end_loc); - - state_p->state = JSP_STATE_STAT_ITER_FINISH; - } - else - { - JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); - state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); - - rewrite_jump_to_end (ctx_p, state_p->u.statement.u.iterational.u.loop_while.jump_to_end_pos); - - const locus end_loc = tok.loc; - - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_while.u.cond_expr_start_loc); - - state_p->u.statement.u.iterational.u.loop_while.u.end_loc = end_loc; - - parse_expression_inside_parens_begin (ctx_p); - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - } - } - else if (state_p->state == JSP_STATE_STAT_FOR_INIT_END) - { - if (is_subexpr_end) - { - JSP_FINISH_SUBEXPR (); - } - - // Jump -> ConditionCheck - state_p->u.statement.u.iterational.u.loop_for.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); - - state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_SEMICOLON); - - locus for_body_statement_loc = state_p->u.statement.u.iterational.u.loop_for.u1.body_loc; - - // Save Condition locus - state_p->u.statement.u.iterational.u.loop_for.u1.condition_expr_loc = tok.loc; - - if (!jsp_find_next_token_before_the_locus (ctx_p, - TOK_SEMICOLON, - for_body_statement_loc, - true)) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid for statement"); - } - - current_token_must_be_check_and_skip_it (ctx_p, TOK_SEMICOLON); - - // Save Increment locus - state_p->u.statement.u.iterational.u.loop_for.u2.increment_expr_loc = tok.loc; - - // Body - seek_token (ctx_p, for_body_statement_loc); - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_FOR_INCREMENT); - } - else if (state_p->state == JSP_STATE_STAT_FOR_INCREMENT) - { - if (is_subexpr_end) - { - JSP_FINISH_SUBEXPR (); - - state_p->state = JSP_STATE_STAT_FOR_COND; - } - else - { - // Save LoopEnd locus - const locus loop_end_loc = tok.loc; - - // Setup ContinueTarget - JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); - state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); - - // Increment - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u2.increment_expr_loc); - - state_p->u.statement.u.iterational.u.loop_for.u2.end_loc = loop_end_loc; - - if (!token_is (TOK_CLOSE_PAREN)) - { - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - - jsp_state_top (ctx_p)->is_need_retval = false; - } - else - { - state_p->state = JSP_STATE_STAT_FOR_COND; - } - } - } - else if (state_p->state == JSP_STATE_STAT_FOR_COND) - { - if (is_subexpr_end) - { - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - jsp_operand_t cond = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos, cond); - - state_p->state = JSP_STATE_STAT_FOR_FINISH; - } - else - { - current_token_must_be (TOK_CLOSE_PAREN); - - // Setup ConditionCheck - rewrite_jump_to_end (ctx_p, state_p->u.statement.u.iterational.u.loop_for.jump_to_end_pos); - - // Condition - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u1.condition_expr_loc); - - if (token_is (TOK_SEMICOLON)) - { - dump_continue_iterations_check (ctx_p, - state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos, - empty_operand ()); - state_p->state = JSP_STATE_STAT_FOR_FINISH; - } - else - { - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - } - } - } - else if (state_p->state == JSP_STATE_STAT_FOR_FINISH) - { - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u2.end_loc); - - state_p->state = JSP_STATE_STAT_ITER_FINISH; - } - else if (state_p->state == JSP_STATE_STAT_FOR_IN) - { - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); - - JERRY_ASSERT (is_subexpr_end); - - locus body_loc = tok.loc; - - // Dump for-in instruction - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - jsp_operand_t collection = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - state_p->u.statement.u.iterational.u.loop_for_in.header_pos = dump_for_in_for_rewrite (ctx_p, collection); - - // Dump assignment VariableDeclarationNoIn / LeftHandSideExpression <- VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.u.iterator_expr_loc); - - if (token_is (TOK_KW_VAR)) - { - skip_token (ctx_p); - - locus name_loc = tok.loc; - - current_token_must_be (TOK_NAME); - - const lit_cpointer_t lit_cp = token_data_as_lit_cp (); - const jsp_operand_t name = jsp_make_string_lit_operand (lit_cp); - - skip_token (ctx_p); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), name_loc); - - if (!scopes_tree_variable_declaration_exists (jsp_get_current_scopes_tree_node (ctx_p), lit_cp)) - { - dump_variable_declaration (ctx_p, lit_cp); - } - } - - if (token_is (TOK_EQ)) - { - seek_token (ctx_p, name_loc); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, false); - } - state_p->is_var_decl_no_in = true; - state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp = lit_cp; - } - else - { - state_p->is_var_decl_no_in = false; - state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp = NOT_A_LITERAL; - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_LEFTHANDSIDE, true); - } - - // Body - state_p->u.statement.u.iterational.u.loop_for_in.u.body_loc = body_loc; - - state_p->state = JSP_STATE_STAT_FOR_IN_EXPR; - } - else if (state_p->state == JSP_STATE_STAT_FOR_IN_EXPR) - { - current_token_must_be (TOK_KW_IN); - - seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.u.body_loc); - - jsp_operand_t for_in_special_reg = jsp_make_reg_operand (VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME); - - if (!state_p->is_var_decl_no_in) - { - JERRY_ASSERT (is_subexpr_end); - - if (substate_p->is_value_based_reference) - { - dump_prop_setter (ctx_p, - substate_p->u.expression.operand, - substate_p->u.expression.prop_name_operand, - for_in_special_reg); - } - else - { - dump_variable_assignment (ctx_p, substate_p->u.expression.operand, for_in_special_reg); - } - - JSP_FINISH_SUBEXPR (); - } - else - { - JERRY_ASSERT (!is_subexpr_end); - - lit_cpointer_t var_name_lit_cp = state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp; - - dump_variable_assignment (ctx_p, jsp_make_identifier_operand (var_name_lit_cp), for_in_special_reg); - } - - state_p->is_simply_jumpable_border = true; - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_FOR_IN_FINISH); - } - else if (state_p->state == JSP_STATE_STAT_FOR_IN_FINISH) - { - // Save LoopEnd locus - const locus loop_end_loc = tok.loc; - - // Setup ContinueTarget - JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); - state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); - - // Write position of for-in end to for_in instruction - rewrite_for_in (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.header_pos); - - // Dump meta (OPCODE_META_TYPE_END_FOR_IN) - dump_for_in_end (ctx_p); - - seek_token (ctx_p, loop_end_loc); - - state_p->state = JSP_STATE_STAT_ITER_FINISH; - } - else if (state_p->state == JSP_STATE_STAT_ITER_FINISH) - { - JSP_COMPLETE_STATEMENT_PARSE (); - - jsp_rewrite_jumps_chain (ctx_p, - &state_p->u.statement.u.iterational.continues_rewrite_chain, - state_p->u.statement.u.iterational.continue_tgt_oc); - } - else if (state_p->state == JSP_STATE_STAT_SWITCH) - { - JERRY_ASSERT (is_subexpr_end); - - parse_expression_inside_parens_end (ctx_p); - - dump_get_value_if_ref (ctx_p, substate_p, false); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - jsp_operand_t switch_expr = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); - - state_p->state = JSP_STATE_STAT_SWITCH_BRANCH_EXPR; - - state_p->u.statement.u.switch_statement.expr = switch_expr; - state_p->u.statement.u.switch_statement.default_label_oc = MAX_OPCODES; - state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc = MAX_OPCODES; - state_p->u.statement.u.switch_statement.skip_check_jmp_oc = MAX_OPCODES; - - dumper_save_reg_alloc_ctx (ctx_p, - &state_p->u.statement.u.switch_statement.saved_reg_next, - &state_p->u.statement.u.switch_statement.saved_reg_max_for_temps); - } - else if (state_p->state == JSP_STATE_STAT_SWITCH_BRANCH_EXPR) - { - if (is_subexpr_end) - { - /* finished parse of an Expression in CaseClause */ - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - jsp_operand_t case_expr = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON); - - jsp_operand_t switch_expr = state_p->u.statement.u.switch_statement.expr; - jsp_operand_t condition_reg = tmp_operand (); - - if (jsp_is_register_operand (case_expr)) - { - /* reuse the register */ - condition_reg = case_expr; - } - - dump_binary_op (ctx_p, VM_OP_EQUAL_VALUE_TYPE, condition_reg, switch_expr, case_expr); - - jsp_add_conditional_jump_to_rewrite_chain (ctx_p, - &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc, - true, - condition_reg); - - uint32_t num = jsp_rewrite_jumps_chain (ctx_p, - &state_p->u.statement.u.switch_statement.skip_check_jmp_oc, - dumper_get_current_instr_counter (ctx_p)); - JERRY_ASSERT (num <= 1); - - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - else if (token_is (TOK_CLOSE_BRACE)) - { - skip_token (ctx_p); - - vm_instr_counter_t last_cond_check_tgt_oc; - - if (state_p->u.statement.u.switch_statement.default_label_oc != MAX_OPCODES) - { - last_cond_check_tgt_oc = state_p->u.statement.u.switch_statement.default_label_oc; - } - else - { - last_cond_check_tgt_oc = dumper_get_current_instr_counter (ctx_p); - } - - uint32_t num = jsp_rewrite_jumps_chain (ctx_p, - &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc, - last_cond_check_tgt_oc); - JERRY_ASSERT (num <= 1); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else - { - if (is_stmt_list_end - && !is_stmt_list_control_flow_exit_stmt_occured - && token_is (TOK_KW_CASE)) - { - jsp_add_simple_jump_to_rewrite_chain (ctx_p, - &state_p->u.statement.u.switch_statement.skip_check_jmp_oc); - } - - if (token_is (TOK_KW_CASE) || token_is (TOK_KW_DEFAULT)) - { - if (!is_stmt_list_end /* no StatementList[opt] occured in the SwitchStatement yet, - * so no conditions were checked for now and the DefaultClause - * should be jumped over */ - && token_is (TOK_KW_DEFAULT)) - { - /* first clause is DefaultClause */ - JERRY_ASSERT (state_p->u.statement.u.switch_statement.default_label_oc == MAX_OPCODES); - - /* the check is unconditional as it is jump over DefaultClause */ - jsp_add_simple_jump_to_rewrite_chain (ctx_p, - &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc); - } - - if (token_is (TOK_KW_CASE)) - { - skip_token (ctx_p); - - uint32_t num = jsp_rewrite_jumps_chain (ctx_p, - &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc, - dumper_get_current_instr_counter (ctx_p)); - JERRY_ASSERT (num <= 1); - - dumper_restore_reg_alloc_ctx (ctx_p, - state_p->u.statement.u.switch_statement.saved_reg_next, - state_p->u.statement.u.switch_statement.saved_reg_max_for_temps, - false); - - jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); - } - else - { - JERRY_ASSERT (token_is (TOK_KW_DEFAULT)); - skip_token (ctx_p); - - if (state_p->u.statement.u.switch_statement.default_label_oc != MAX_OPCODES) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Duplication of 'default' clause"); - } - - state_p->u.statement.u.switch_statement.default_label_oc = dumper_get_current_instr_counter (ctx_p); - - uint32_t num = jsp_rewrite_jumps_chain (ctx_p, - &state_p->u.statement.u.switch_statement.skip_check_jmp_oc, - dumper_get_current_instr_counter (ctx_p)); - JERRY_ASSERT (num <= 1); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON); - - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - } - else - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected 'case' or' 'default' clause"); - } - } - } - else if (state_p->state == JSP_STATE_STAT_TRY) - { - rewrite_try (ctx_p, state_p->u.statement.u.try_statement.try_pos); - - if (!token_is (TOK_KW_CATCH) - && !token_is (TOK_KW_FINALLY)) - { - EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected either 'catch' or 'finally' token"); - } - - if (token_is (TOK_KW_CATCH)) - { - skip_token (ctx_p); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); - - current_token_must_be (TOK_NAME); - - const jsp_operand_t name = jsp_make_string_lit_operand (token_data_as_lit_cp ()); - - if (!jsp_is_dump_mode (ctx_p)) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), tok.loc); - } - - skip_token (ctx_p); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); - - state_p->u.statement.u.try_statement.catch_pos = dump_catch_for_rewrite (ctx_p, name); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); - - state_p->state = JSP_STATE_STAT_CATCH_FINISH; - - JERRY_ASSERT (state_p->is_simply_jumpable_border); - - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - else - { - JERRY_ASSERT (token_is (TOK_KW_FINALLY)); - skip_token (ctx_p); - - state_p->u.statement.u.try_statement.finally_pos = dump_finally_for_rewrite (ctx_p); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); - - state_p->state = JSP_STATE_STAT_FINALLY_FINISH; - - JERRY_ASSERT (state_p->is_simply_jumpable_border); - - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - } - else if (state_p->state == JSP_STATE_STAT_CATCH_FINISH) - { - rewrite_catch (ctx_p, state_p->u.statement.u.try_statement.catch_pos); - - if (token_is (TOK_KW_FINALLY)) - { - skip_token (ctx_p); - state_p->u.statement.u.try_statement.finally_pos = dump_finally_for_rewrite (ctx_p); - - current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); - - state_p->state = JSP_STATE_STAT_FINALLY_FINISH; - - JERRY_ASSERT (state_p->is_simply_jumpable_border); - - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); - jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); - jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; - } - else - { - state_p->state = JSP_STATE_STAT_TRY_FINISH; - } - } - else if (state_p->state == JSP_STATE_STAT_FINALLY_FINISH) - { - rewrite_finally (ctx_p, state_p->u.statement.u.try_statement.finally_pos); - - state_p->state = JSP_STATE_STAT_TRY_FINISH; - } - else if (state_p->state == JSP_STATE_STAT_TRY_FINISH) - { - dump_end_try_catch_finally (ctx_p); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (state_p->state == JSP_STATE_STAT_WITH) - { - if (is_subexpr_end) - { - parse_expression_inside_parens_end (ctx_p); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - const jsp_operand_t expr = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - if (!jsp_is_dump_mode (ctx_p)) - { - scopes_tree_set_contains_with (jsp_get_current_scopes_tree_node (ctx_p)); - } - - state_p->is_simply_jumpable_border = true; - - state_p->u.statement.u.with_statement.header_pos = dump_with_for_rewrite (ctx_p, expr); - - JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_WITH); - } - else - { - rewrite_with (ctx_p, state_p->u.statement.u.with_statement.header_pos); - dump_with_end (ctx_p); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - } - else if (state_p->state == JSP_STATE_FUNC_DECL_FINISH) - { - JERRY_ASSERT (is_source_elements_list_end); - jsp_finish_parse_function_scope (ctx_p); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (state_p->state == JSP_STATE_STAT_NAMED_LABEL) - { - jsp_state_pop (ctx_p); - } - else if (state_p->state == JSP_STATE_STAT_EXPRESSION) - { - JERRY_ASSERT (is_subexpr_end); - insert_semicolon (ctx_p); - - dump_get_value_if_ref (ctx_p, substate_p, false); - - if (jsp_get_scope_type (ctx_p) == SCOPE_TYPE_EVAL) - { - JERRY_ASSERT (substate_p->is_need_retval); - - dump_variable_assignment (ctx_p, - jsp_make_reg_operand (VM_REG_SPECIAL_EVAL_RET), - substate_p->u.expression.operand); - } - - JSP_FINISH_SUBEXPR (); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (state_p->state == JSP_STATE_STAT_RETURN) - { - JERRY_ASSERT (is_subexpr_end); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - const jsp_operand_t op = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - dump_retval (ctx_p, op); - - insert_semicolon (ctx_p); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else if (state_p->state == JSP_STATE_STAT_THROW) - { - JERRY_ASSERT (is_subexpr_end); - - dump_get_value_if_ref (ctx_p, substate_p, true); - dump_get_value_for_state_if_const (ctx_p, substate_p); - - const jsp_operand_t op = substate_p->u.expression.operand; - - JSP_FINISH_SUBEXPR (); - - dump_throw (ctx_p, op); - - insert_semicolon (ctx_p); - - JSP_COMPLETE_STATEMENT_PARSE (); - } - else - { - JERRY_ASSERT (state_p->state == JSP_STATE_STAT_STATEMENT); - JERRY_ASSERT (!state_p->is_completed); - - vm_instr_counter_t break_tgt_oc = dumper_get_current_instr_counter (ctx_p); - jsp_rewrite_jumps_chain (ctx_p, - &state_p->u.statement.breaks_rewrite_chain, - break_tgt_oc); - - state_p->is_completed = true; - } - - JERRY_ASSERT (substate_p == NULL); - } -} /* jsp_parse_source_element_list */ - -/** - * Parse program - * - * program - * : LT!* source_element_list LT!* EOF! - * ; - * - * @return true - if parse finished successfully (no SyntaxError was raised); - * false - otherwise. - */ -static jsp_status_t -parser_parse_program (const jerry_api_char_t *source_p, /**< source code buffer */ - size_t source_size, /**< source code size in bytes */ - bool in_eval, /**< flag indicating if we are parsing body of eval code */ - bool is_strict, /**< flag, indicating whether current code - * inherited strict mode from code of an outer scope */ - const bytecode_data_header_t **out_bytecode_data_p, /**< out: generated byte-code array - * (in case there were no syntax errors) */ - bool *out_contains_functions_p) /**< out: optional (can be NULL, if the output is not needed) - * flag, indicating whether the compiled byte-code - * contains a function declaration / expression */ -{ - JERRY_ASSERT (out_bytecode_data_p != NULL); - - const scope_type_t scope_type = (in_eval ? SCOPE_TYPE_EVAL : SCOPE_TYPE_GLOBAL); - -#ifndef JERRY_NDEBUG - volatile bool is_pre_parse_finished = false; -#endif /* !JERRY_NDEBUG */ - - jsp_status_t status; - - jsp_mm_init (); - - jsp_ctx_t ctx; - jsp_init_ctx (&ctx, scope_type); - - jsp_stack_init (&ctx); - - dumper_init (&ctx, parser_show_instrs); - jsp_early_error_init (); - - scopes_tree_init (); - - scopes_tree scope = scopes_tree_new_scope (NULL, scope_type); - scopes_tree_set_strict_mode (scope, is_strict); - - jsp_set_current_scopes_tree_node (&ctx, scope); - - jmp_buf *jsp_early_error_label_p = jsp_early_error_get_early_error_longjmp_label (); - int r = setjmp (*jsp_early_error_label_p); - - if (r == 0) - { - /* - * Note: - * Operations that could raise an early error can be performed only during execution of the block. - */ - lexer_init (source_p, source_size, parser_show_instrs); - skip_token (&ctx); - - locus start_loc = tok.loc; - - jsp_parse_directive_prologue (&ctx); - - jsp_parse_source_element_list (&ctx, scope_type); - JERRY_ASSERT (token_is (TOK_EOF)); - - jsp_set_current_scopes_tree_node (&ctx, NULL); - - scopes_tree_finish_build (); - -#ifndef JERRY_NDEBUG - is_pre_parse_finished = true; -#endif /* !JERRY_NDEBUG */ - - jsp_early_error_free (); - - if (out_contains_functions_p != NULL) - { - *out_contains_functions_p = scope->contains_functions; - } - - jsp_switch_to_dump_mode (&ctx, scope); - - scopes_tree scope_to_dump_p = jsp_get_next_scopes_tree_node_to_dump (&ctx); - JERRY_ASSERT (scope_to_dump_p == scope); - - bytecode_data_header_t *root_bc_header_p = bc_dump_single_scope (scope); - - scopes_tree_free_scope (scope); - - bc_register_root_bytecode_header (root_bc_header_p); - - *out_bytecode_data_p = root_bc_header_p; - - jsp_set_current_bytecode_header (&ctx, root_bc_header_p); - - seek_token (&ctx, start_loc); - - jsp_parse_source_element_list (&ctx, scope_type); - - jsp_set_current_bytecode_header (&ctx, NULL); - - jsp_stack_finalize (&ctx); - - JERRY_ASSERT (jsp_get_scope_type (&ctx) == scope_type); - - if (parser_show_instrs) - { - lit_dump_literals (); - bc_print_instrs (root_bc_header_p); - } - - scopes_tree_finalize (); - - status = JSP_STATUS_OK; - } - else - { - /* SyntaxError handling */ - -#ifndef JERRY_NDEBUG - JERRY_ASSERT (!is_pre_parse_finished); -#endif /* !JERRY_NDEBUG */ - - *out_bytecode_data_p = NULL; - - jsp_mm_free_all (); - - jsp_early_error_t type = jsp_early_error_get_type (); - - if (type == JSP_EARLY_ERROR_SYNTAX) - { - status = JSP_STATUS_SYNTAX_ERROR; - } - else - { - JERRY_ASSERT (type == JSP_EARLY_ERROR_REFERENCE); - - status = JSP_STATUS_REFERENCE_ERROR; - } - } - - jsp_mm_finalize (); - - return status; -} /* parser_parse_program */ - -/** - * Parse source script - * - * @return true - if parse finished successfully (no SyntaxError were raised); - * false - otherwise. - */ -jsp_status_t -parser_parse_script (const jerry_api_char_t *source, /**< source script */ - size_t source_size, /**< source script size it bytes */ - const bytecode_data_header_t **out_bytecode_data_p) /**< out: generated byte-code array - * (in case there were no syntax errors) */ -{ - return parser_parse_program (source, source_size, false, false, out_bytecode_data_p, NULL); -} /* parser_parse_script */ - -/** - * Parse string passed to eval() call - * - * @return true - if parse finished successfully (no SyntaxError were raised); - * false - otherwise. - */ -jsp_status_t -parser_parse_eval (const jerry_api_char_t *source, /**< string passed to eval() */ - size_t source_size, /**< string size in bytes */ - bool is_strict, /**< flag, indicating whether eval is called - * from strict code in direct mode */ - const bytecode_data_header_t **out_bytecode_data_p, /**< out: generated byte-code array - * (in case there were no syntax errors) */ - bool *out_contains_functions_p) /**< out: flag, indicating whether the compiled byte-code - * contains a function declaration / expression */ -{ - JERRY_ASSERT (out_contains_functions_p != NULL); - - return parser_parse_program (source, - source_size, - true, - is_strict, - out_bytecode_data_p, - out_contains_functions_p); -} /* parser_parse_eval */ - -/** - * Tell parser whether to dump bytecode - */ -void -parser_set_show_instrs (bool show_instrs) /**< flag indicating whether to dump bytecode */ -{ - parser_show_instrs = show_instrs; -} /* parser_set_show_instrs */ diff --git a/jerry-core/parser/js/parser.h b/jerry-core/parser/js/parser.h index f6582ce7aa..cbb06bbd29 100644 --- a/jerry-core/parser/js/parser.h +++ b/jerry-core/parser/js/parser.h @@ -1,4 +1,4 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,40 @@ #ifndef PARSER_H #define PARSER_H -#include "jrt.h" +#include "jerry-api.h" +#include "byte-code.h" + +/** \addtogroup parser Parser + * @{ + * + * \addtogroup jsparser ECMAScript + * @{ + * + * \addtogroup jsparser_parser Parser + * @{ + */ /** * Parser completion status */ typedef enum { - JSP_STATUS_OK, /**< parse finished successfully, no early errors occured */ - JSP_STATUS_SYNTAX_ERROR, /**< SyntaxError early error occured */ + JSP_STATUS_OK, /**< parse finished successfully, no early errors occured */ + JSP_STATUS_SYNTAX_ERROR, /**< SyntaxError early error occured */ JSP_STATUS_REFERENCE_ERROR /**< ReferenceError early error occured */ } jsp_status_t; -void parser_set_show_instrs (bool); -jsp_status_t parser_parse_script (const jerry_api_char_t *, size_t, const bytecode_data_header_t **); -jsp_status_t parser_parse_eval (const jerry_api_char_t *, size_t, bool, const bytecode_data_header_t **, bool *); +extern void parser_set_show_instrs (int); + +extern jsp_status_t parser_parse_script (const jerry_api_char_t *, size_t, + ecma_compiled_code_t **); +extern jsp_status_t parser_parse_eval (const jerry_api_char_t *, size_t, bool, + ecma_compiled_code_t **); + +/** + * @} + * @} + * @} + */ -#endif /* PARSER_H */ +#endif /* !PARSER_H */ diff --git a/jerry-core/parser/js/scopes-tree.cpp b/jerry-core/parser/js/scopes-tree.cpp deleted file mode 100644 index 8e32865a1c..0000000000 --- a/jerry-core/parser/js/scopes-tree.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bytecode-data.h" -#include "jsp-mm.h" -#include "scopes-tree.h" - -scopes_tree scopes_tree_root_node_p = NULL; -scopes_tree scopes_tree_last_node_p = NULL; - -static void -assert_tree (scopes_tree t) -{ - JERRY_ASSERT (t != NULL); -} - -vm_instr_counter_t -scopes_tree_instrs_num (scopes_tree t) -{ - assert_tree (t); - return t->instrs_count; -} - -/** - * Get number of variable declarations in the scope - * - * @return number of variable declarations - */ -vm_instr_counter_t -scopes_tree_var_decls_num (scopes_tree t) /**< scope */ -{ - assert_tree (t); - return linked_list_get_length (t->var_decls); -} /* scopes_tree_var_decls_num */ - -/** - * Add variable declaration to a scope - */ -void -scopes_tree_add_var_decl (scopes_tree tree, /**< scope, to which variable declaration is added */ - op_meta op) /**< variable declaration instruction */ -{ - assert_tree (tree); - linked_list_set_element (tree->var_decls, linked_list_get_length (tree->var_decls), &op); -} /* scopes_tree_add_var_decl */ - -/** - * Get variable declaration for the specified scope - * - * @return instruction, declaring a variable - */ -op_meta -scopes_tree_var_decl (scopes_tree tree, /**< scope, from which variable declaration is retrieved */ - vm_instr_counter_t oc) /**< number of variable declaration in the scope */ -{ - assert_tree (tree); - JERRY_ASSERT (oc < linked_list_get_length (tree->var_decls)); - return *(op_meta *) linked_list_element (tree->var_decls, oc); -} /* scopes_tree_var_decl */ - -/** - * Checks if variable declaration exists in the scope - * - * @return true / false - */ -bool -scopes_tree_variable_declaration_exists (scopes_tree tree, /**< scope */ - lit_cpointer_t lit_id) /**< literal which holds variable's name */ -{ - assert_tree (tree); - - for (vm_instr_counter_t oc = 0u; - oc < linked_list_get_length (tree->var_decls); - oc++) - { - const op_meta* var_decl_om_p = (op_meta *) linked_list_element (tree->var_decls, oc); - - JERRY_ASSERT (var_decl_om_p->op.op_idx == VM_OP_VAR_DECL); - JERRY_ASSERT (var_decl_om_p->op.data.var_decl.variable_name == VM_IDX_REWRITE_LITERAL_UID); - - if (var_decl_om_p->lit_id[0].packed_value == lit_id.packed_value) - { - return true; - } - } - - return false; -} /* scopes_tree_variable_declaration_exists */ - -/** - * Fill variable declaration list of bytecode header - */ -void -scopes_tree_dump_var_decls (scopes_tree scope, /**< scopes tree */ - lit_cpointer_t *var_decls_p) /**< pointer to bytecode header's declarations table, - * where variables' lit_cp's should be stored */ -{ - for (uint32_t i = 0; i < scopes_tree_var_decls_num (scope); ++i) - { - op_meta var_decl_op_meta = *(op_meta *) linked_list_element (scope->var_decls, i); - var_decls_p[i] = var_decl_op_meta.lit_id[0]; - } -} /* scopes_tree_dump_var_decls */ - -/** - * Set up a flag, indicating that scope should be executed in strict mode - */ -void -scopes_tree_set_strict_mode (scopes_tree tree, /**< scope */ - bool strict_mode) /**< value of the strict mode flag */ -{ - assert_tree (tree); - tree->strict_mode = strict_mode; -} /* scopes_tree_set_strict_mode */ - -/** - * Set up a flag, indicating that "arguments" is used inside a scope - */ -void -scopes_tree_set_arguments_used (scopes_tree tree) /**< scope */ -{ - assert_tree (tree); - tree->ref_arguments = true; -} /* merge_subscopes */ - -/** - * Set up a flag, indicating that "eval" is used inside a scope - */ -void -scopes_tree_set_eval_used (scopes_tree tree) /**< scope */ -{ - assert_tree (tree); - tree->ref_eval = true; -} /* scopes_tree_set_eval_used */ - -/** - * Set up a flag, indicating that 'with' statement is contained in a scope - */ -void -scopes_tree_set_contains_with (scopes_tree tree) /**< scope */ -{ - assert_tree (tree); - tree->contains_with = true; -} /* scopes_tree_set_contains_with */ - -/** - * Set up a flag, indicating that 'try' statement is contained in a scope - */ -void -scopes_tree_set_contains_try (scopes_tree tree) /**< scope */ -{ - assert_tree (tree); - tree->contains_try = true; -} /* scopes_tree_set_contains_try */ - -/** - * Set up a flag, indicating that 'delete' operator is contained in a scope - */ -void -scopes_tree_set_contains_delete (scopes_tree tree) /**< scope */ -{ - assert_tree (tree); - tree->contains_delete = true; -} /* scopes_tree_set_contains_delete */ - -/** - * Set up a flag, indicating that there is a function declaration / expression inside a scope - */ -void -scopes_tree_set_contains_functions (scopes_tree tree) /**< scope */ -{ - assert_tree (tree); - tree->contains_functions = true; -} /* scopes_tree_set_contains_functions */ - -bool -scopes_tree_strict_mode (scopes_tree tree) -{ - assert_tree (tree); - return (bool) tree->strict_mode; -} - -/** - * Get number of subscopes (immediate function declarations / expressions) of the specified scope - * - * @return the number - */ -size_t -scopes_tree_child_scopes_num (scopes_tree tree) /**< a scopes tree node */ -{ - return tree->child_scopes_num; -} /* scopes_tree_child_scopes_num */ - -void -scopes_tree_init (void) -{ - scopes_tree_root_node_p = NULL; - scopes_tree_last_node_p = NULL; -} /* scopes_tree_init */ - -void -scopes_tree_finalize (void) -{ - JERRY_ASSERT (scopes_tree_root_node_p == NULL); - JERRY_ASSERT (scopes_tree_last_node_p == NULL); -} /* scopes_tree_finalize */ - -/** - * Initialize a scope - * - * @return initialized scope - */ -scopes_tree -scopes_tree_new_scope (scopes_tree parent, /**< parent scope */ - scope_type_t type) /**< scope type */ -{ - scopes_tree tree = (scopes_tree) jsp_mm_alloc (sizeof (scopes_tree_int)); - - tree->child_scopes_num = 0; - tree->child_scopes_processed_num = 0; - tree->max_uniq_literals_num = 0; - - tree->bc_header_cp = MEM_CP_NULL; - - tree->next_scope_cp = MEM_CP_NULL; - tree->instrs_count = 0; - tree->type = type; - tree->strict_mode = false; - tree->ref_arguments = false; - tree->ref_eval = false; - tree->contains_with = false; - tree->contains_try = false; - tree->contains_delete = false; - tree->contains_functions = false; - tree->is_vars_and_args_to_regs_possible = false; - tree->var_decls = linked_list_init (sizeof (op_meta)); - - if (parent != NULL) - { - JERRY_ASSERT (scopes_tree_root_node_p != NULL); - JERRY_ASSERT (scopes_tree_last_node_p != NULL); - - parent->child_scopes_num++; - } - else - { - JERRY_ASSERT (scopes_tree_root_node_p == NULL); - JERRY_ASSERT (scopes_tree_last_node_p == NULL); - - scopes_tree_root_node_p = tree; - scopes_tree_last_node_p = tree; - } - - MEM_CP_SET_NON_NULL_POINTER (scopes_tree_last_node_p->next_scope_cp, tree); - tree->next_scope_cp = MEM_CP_NULL; - - scopes_tree_last_node_p = tree; - - return tree; -} /* scopes_tree_new_scope */ - -void -scopes_tree_finish_build (void) -{ - JERRY_ASSERT (scopes_tree_root_node_p != NULL); - JERRY_ASSERT (scopes_tree_last_node_p != NULL); - - scopes_tree_root_node_p = NULL; - scopes_tree_last_node_p = NULL; -} /* scopes_tree_finish_build */ - -void -scopes_tree_free_scope (scopes_tree scope_p) -{ - assert_tree (scope_p); - - JERRY_ASSERT (scopes_tree_root_node_p == NULL); - JERRY_ASSERT (scopes_tree_last_node_p == NULL); - - linked_list_free (scope_p->var_decls); - - jsp_mm_free (scope_p); -} /* scopes_tree_free_scope */ - diff --git a/jerry-core/parser/js/scopes-tree.h b/jerry-core/parser/js/scopes-tree.h deleted file mode 100644 index b7520dae6f..0000000000 --- a/jerry-core/parser/js/scopes-tree.h +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SCOPES_TREE_H -#define SCOPES_TREE_H - -#include "linked-list.h" -#include "lexer.h" -#include "ecma-globals.h" -#include "opcodes.h" -#include "lit-id-hash-table.h" -#include "lit-literal.h" - -/** - * Intermediate instruction descriptor that additionally to byte-code instruction - * holds information about literals, associated with corresponding arguments - * of the instruction. - */ -typedef struct -{ - lit_cpointer_t lit_id[3]; /**< literals, corresponding to arguments - * (NOT_A_LITERAL - if not literal is associated - * with corresponding argument) */ - vm_instr_t op; /**< byte-code instruction */ -} op_meta; - -typedef struct tree_header -{ - linked_list children; -} tree_header; - -/** - * Scope type - */ -typedef enum __attr_packed___ -{ - SCOPE_TYPE_GLOBAL, /**< the Global code scope */ - SCOPE_TYPE_FUNCTION, /**< a function code scope */ - SCOPE_TYPE_EVAL /**< an eval code scope */ -} scope_type_t; - -/** - * Structure for holding scope information during parsing - */ -typedef struct -{ - mem_cpointer_t next_scope_cp; /**< next scope with same parent */ - - mem_cpointer_t bc_header_cp; /**< pointer to corresponding byte-code header - * (after bc_dump_single_scope) */ - uint16_t child_scopes_num; /**< number of child scopes */ - uint16_t child_scopes_processed_num; /**< number of child scopes, for which - * byte-code headers were already constructed */ - - uint16_t max_uniq_literals_num; /**< upper estimate number of entries - * in idx-literal hash table */ - - vm_instr_counter_t instrs_count; /**< count of instructions */ - - linked_list var_decls; /**< instructions for variable declarations */ - - scope_type_t type : 2; /**< scope type */ - bool strict_mode: 1; /**< flag, indicating that scope's code should be executed in strict mode */ - bool ref_arguments: 1; /**< flag, indicating that "arguments" variable is used inside the scope - * (not depends on subscopes) */ - bool ref_eval: 1; /**< flag, indicating that "eval" is used inside the scope - * (not depends on subscopes) */ - bool contains_with: 1; /**< flag, indicationg whether 'with' statement is contained in the scope - * (not depends on subscopes) */ - bool contains_try: 1; /**< flag, indicationg whether 'try' statement is contained in the scope - * (not depends on subscopes) */ - bool contains_delete: 1; /**< flag, indicationg whether 'delete' operator is contained in the scope - * (not depends on subscopes) */ - bool contains_functions: 1; /**< flag, indicating that the scope contains a function declaration / expression */ - bool is_vars_and_args_to_regs_possible : 1; /**< the function's variables / arguments can be moved to registers */ -} scopes_tree_int; - -typedef scopes_tree_int *scopes_tree; - -void scopes_tree_init (void); -void scopes_tree_finalize (void); -scopes_tree scopes_tree_new_scope (scopes_tree, scope_type_t); -void scopes_tree_free_scope (scopes_tree); -void scopes_tree_finish_build (void); -size_t scopes_tree_child_scopes_num (scopes_tree); -vm_instr_counter_t scopes_tree_instrs_num (scopes_tree); -vm_instr_counter_t scopes_tree_var_decls_num (scopes_tree); -void scopes_tree_add_var_decl (scopes_tree, op_meta); -op_meta scopes_tree_var_decl (scopes_tree, vm_instr_counter_t); -bool scopes_tree_variable_declaration_exists (scopes_tree, lit_cpointer_t); -void scopes_tree_dump_var_decls (scopes_tree, lit_cpointer_t *); -void scopes_tree_set_strict_mode (scopes_tree, bool); -void scopes_tree_set_arguments_used (scopes_tree); -void scopes_tree_set_eval_used (scopes_tree); -void scopes_tree_set_contains_with (scopes_tree); -void scopes_tree_set_contains_try (scopes_tree); -void scopes_tree_set_contains_delete (scopes_tree); -void scopes_tree_set_contains_functions (scopes_tree); -bool scopes_tree_strict_mode (scopes_tree); -#endif /* SCOPES_TREE_H */ diff --git a/jerry-core/parser/regexp/re-compiler.cpp b/jerry-core/parser/regexp/re-compiler.cpp index c11c62c38a..8ccf227f12 100644 --- a/jerry-core/parser/regexp/re-compiler.cpp +++ b/jerry-core/parser/regexp/re-compiler.cpp @@ -1,5 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -25,6 +25,16 @@ #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN +/** \addtogroup parser Parser + * @{ + * + * \addtogroup regexparser Regular expression + * @{ + * + * \addtogroup regexparser_bytecode Bytecode + * @{ + */ + /** * Size of block of RegExp bytecode. Used for allocation */ @@ -34,7 +44,7 @@ * Get length of bytecode */ static uint32_t -re_get_bytecode_length (re_bytecode_ctx_t *bc_ctx_p) +re_get_bytecode_length (re_bytecode_ctx_t *bc_ctx_p) /**< RegExp bytecode context */ { return ((uint32_t) (bc_ctx_p->current_p - bc_ctx_p->block_start_p)); } /* re_get_bytecode_length */ @@ -47,7 +57,7 @@ re_dump_bytecode (re_bytecode_ctx_t *bc_ctx); * * @return current position in RegExp bytecode */ -static re_bytecode_t* +static uint8_t * re_realloc_regexp_bytecode_block (re_bytecode_ctx_t *bc_ctx_p) /**< RegExp bytecode context */ { JERRY_ASSERT (bc_ctx_p->block_end_p - bc_ctx_p->block_start_p >= 0); @@ -62,8 +72,8 @@ re_realloc_regexp_bytecode_block (re_bytecode_ctx_t *bc_ctx_p) /**< RegExp bytec JERRY_ASSERT (bc_ctx_p->current_p - bc_ctx_p->block_start_p >= 0); size_t current_ptr_offset = static_cast (bc_ctx_p->current_p - bc_ctx_p->block_start_p); - re_bytecode_t *new_block_start_p = (re_bytecode_t *) mem_heap_alloc_block (new_block_size, - MEM_HEAP_ALLOC_SHORT_TERM); + uint8_t *new_block_start_p = (uint8_t *) mem_heap_alloc_block (new_block_size, + MEM_HEAP_ALLOC_SHORT_TERM); if (bc_ctx_p->current_p) { memcpy (new_block_start_p, bc_ctx_p->block_start_p, static_cast (current_ptr_offset)); @@ -81,12 +91,12 @@ re_realloc_regexp_bytecode_block (re_bytecode_ctx_t *bc_ctx_p) /**< RegExp bytec */ static void re_bytecode_list_append (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode context */ - re_bytecode_t *bytecode_p, /**< input bytecode */ + uint8_t *bytecode_p, /**< input bytecode */ size_t length) /**< length of input */ { JERRY_ASSERT (length <= REGEXP_BYTECODE_BLOCK_SIZE); - re_bytecode_t *current_p = bc_ctx_p->current_p; + uint8_t *current_p = bc_ctx_p->current_p; if (current_p + length > bc_ctx_p->block_end_p) { current_p = re_realloc_regexp_bytecode_block (bc_ctx_p); @@ -102,24 +112,24 @@ re_bytecode_list_append (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode conte static void re_bytecode_list_insert (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode context */ size_t offset, /**< distance from the start of the container */ - re_bytecode_t *bytecode_p, /**< input bytecode */ + uint8_t *bytecode_p, /**< input bytecode */ size_t length) /**< length of input */ { JERRY_ASSERT (length <= REGEXP_BYTECODE_BLOCK_SIZE); - re_bytecode_t *current_p = bc_ctx_p->current_p; + uint8_t *current_p = bc_ctx_p->current_p; if (current_p + length > bc_ctx_p->block_end_p) { re_realloc_regexp_bytecode_block (bc_ctx_p); } - re_bytecode_t *src_p = bc_ctx_p->block_start_p + offset; + uint8_t *src_p = bc_ctx_p->block_start_p + offset; if ((re_get_bytecode_length (bc_ctx_p) - offset) > 0) { - re_bytecode_t *dest_p = src_p + length; - re_bytecode_t *tmp_block_start_p; - tmp_block_start_p = (re_bytecode_t *) mem_heap_alloc_block ((re_get_bytecode_length (bc_ctx_p) - offset), - MEM_HEAP_ALLOC_SHORT_TERM); + uint8_t *dest_p = src_p + length; + uint8_t *tmp_block_start_p; + tmp_block_start_p = (uint8_t *) mem_heap_alloc_block ((re_get_bytecode_length (bc_ctx_p) - offset), + MEM_HEAP_ALLOC_SHORT_TERM); memcpy (tmp_block_start_p, src_p, (size_t) (re_get_bytecode_length (bc_ctx_p) - offset)); memcpy (dest_p, tmp_block_start_p, (size_t) (re_get_bytecode_length (bc_ctx_p) - offset)); mem_heap_free_block (tmp_block_start_p); @@ -136,7 +146,7 @@ static void re_append_opcode (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode context */ re_opcode_t opcode) /**< input opcode */ { - re_bytecode_list_append (bc_ctx_p, (re_bytecode_t*) &opcode, sizeof (re_bytecode_t)); + re_bytecode_list_append (bc_ctx_p, (uint8_t*) &opcode, sizeof (uint8_t)); } /* re_append_opcode */ /** @@ -146,7 +156,7 @@ static void re_append_u32 (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode context */ uint32_t value) /**< input value */ { - re_bytecode_list_append (bc_ctx_p, (re_bytecode_t*) &value, sizeof (uint32_t)); + re_bytecode_list_append (bc_ctx_p, (uint8_t*) &value, sizeof (uint32_t)); } /* re_append_u32 */ /** @@ -168,7 +178,7 @@ re_insert_opcode (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode context */ uint32_t offset, /**< distance from the start of the container */ re_opcode_t opcode) /**< input opcode */ { - re_bytecode_list_insert (bc_ctx_p, offset, (re_bytecode_t*) &opcode, sizeof (re_bytecode_t)); + re_bytecode_list_insert (bc_ctx_p, offset, (uint8_t*) &opcode, sizeof (uint8_t)); } /* re_insert_opcode */ /** @@ -179,17 +189,17 @@ re_insert_u32 (re_bytecode_ctx_t *bc_ctx_p, /**< RegExp bytecode context */ uint32_t offset, /**< distance from the start of the container */ uint32_t value) /**< input value */ { - re_bytecode_list_insert (bc_ctx_p, offset, (re_bytecode_t*) &value, sizeof (uint32_t)); + re_bytecode_list_insert (bc_ctx_p, offset, (uint8_t*) &value, sizeof (uint32_t)); } /* re_insert_u32 */ /** * Get a RegExp opcode */ re_opcode_t -re_get_opcode (re_bytecode_t **bc_p) /**< pointer to bytecode start */ +re_get_opcode (uint8_t **bc_p) /**< pointer to bytecode start */ { - re_bytecode_t bytecode = **bc_p; - (*bc_p) += sizeof (re_bytecode_t); + uint8_t bytecode = **bc_p; + (*bc_p) += sizeof (uint8_t); return (re_opcode_t) bytecode; } /* get_opcode */ @@ -197,7 +207,7 @@ re_get_opcode (re_bytecode_t **bc_p) /**< pointer to bytecode start */ * Get a parameter of a RegExp opcode */ uint32_t -re_get_value (re_bytecode_t **bc_p) /**< pointer to bytecode start */ +re_get_value (uint8_t **bc_p) /**< pointer to bytecode start */ { uint32_t value = *((uint32_t*) *bc_p); (*bc_p) += sizeof (uint32_t); @@ -368,6 +378,13 @@ re_insert_into_group_with_jump (re_compiler_ctx_t *re_ctx_p, /**< RegExp compile re_insert_into_group (re_ctx_p, group_start_offset, idx, is_capturable); } /* re_insert_into_group_with_jump */ +/** + * @} + * + * \addtogroup regexparser_compiler Compiler + * @{ + */ + /** * Parse alternatives * @@ -614,9 +631,9 @@ re_parse_alternative (re_compiler_ctx_t *re_ctx_p, /**< RegExp compiler context * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -re_compile_bytecode (re_bytecode_t **out_bytecode_p, /**< out:pointer to bytecode */ +re_compile_bytecode (re_compiled_code_t **out_bytecode_p, /**< out:pointer to bytecode */ ecma_string_t *pattern_str_p, /**< pattern */ - uint8_t flags) /**< flags */ + uint16_t flags) /**< flags */ { ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); re_compiler_ctx_t re_ctx; @@ -661,9 +678,18 @@ re_compile_bytecode (re_bytecode_t **out_bytecode_p, /**< out:pointer to bytecod re_append_opcode (&bc_ctx, RE_OP_EOF); /* 3. Insert extra informations for bytecode header */ - re_insert_u32 (&bc_ctx, 0, (uint32_t) re_ctx.num_of_non_captures); - re_insert_u32 (&bc_ctx, 0, (uint32_t) re_ctx.num_of_captures * 2); - re_insert_u32 (&bc_ctx, 0, (uint32_t) re_ctx.flags); + re_compiled_code_t re_compiled_code; + + re_compiled_code.flags = re_ctx.flags | (1 << ECMA_BYTECODE_REF_SHIFT); + ECMA_SET_NON_NULL_POINTER (re_compiled_code.pattern_cp, + ecma_copy_or_ref_ecma_string (pattern_str_p)); + re_compiled_code.num_of_captures = re_ctx.num_of_captures * 2; + re_compiled_code.num_of_non_captures = re_ctx.num_of_non_captures; + + re_bytecode_list_insert (&bc_ctx, + 0, + (uint8_t *) &re_compiled_code, + sizeof (re_compiled_code_t)); } ECMA_FINALIZE (empty); @@ -679,7 +705,7 @@ re_compile_bytecode (re_bytecode_t **out_bytecode_p, /**< out:pointer to bytecod { /* The RegExp bytecode contains at least a RE_OP_SAVE_AT_START opdoce, so it cannot be NULL. */ JERRY_ASSERT (bc_ctx.block_start_p != NULL); - *out_bytecode_p = bc_ctx.block_start_p; + *out_bytecode_p = (re_compiled_code_t *) bc_ctx.block_start_p; } #ifdef JERRY_ENABLE_LOG @@ -694,12 +720,14 @@ re_compile_bytecode (re_bytecode_t **out_bytecode_p, /**< out:pointer to bytecod * RegExp bytecode dumper */ void -re_dump_bytecode (re_bytecode_ctx_t *bc_ctx_p) +re_dump_bytecode (re_bytecode_ctx_t *bc_ctx_p) /**< RegExp bytecode context */ { - re_bytecode_t *bytecode_p = bc_ctx_p->block_start_p; - JERRY_DLOG ("%d ", re_get_value (&bytecode_p)); - JERRY_DLOG ("%d ", re_get_value (&bytecode_p)); - JERRY_DLOG ("%d | ", re_get_value (&bytecode_p)); + re_compiled_code_t *compiled_code_p = bc_ctx_p->block_start_p; + JERRY_DLOG ("%d ", compiled_code_p->flags); + JERRY_DLOG ("%d ", compiled_code_p->num_of_captures); + JERRY_DLOG ("%d | ", compiled_code_p->num_of_non_captures); + + uint8_t *bytecode_p = (uint8_t *) (compiled_code_p + 1); re_opcode_t op; while ((op = re_get_opcode (&bytecode_p))) @@ -891,4 +919,9 @@ re_dump_bytecode (re_bytecode_ctx_t *bc_ctx_p) } /* re_dump_bytecode */ #endif /* JERRY_ENABLE_LOG */ +/** + * @} + * @} + */ + #endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ diff --git a/jerry-core/parser/regexp/re-compiler.h b/jerry-core/parser/regexp/re-compiler.h index f88f55b7b5..ad88412edc 100644 --- a/jerry-core/parser/regexp/re-compiler.h +++ b/jerry-core/parser/regexp/re-compiler.h @@ -1,5 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -22,6 +22,16 @@ #include "ecma-globals.h" #include "re-parser.h" +/** \addtogroup parser Parser + * @{ + * + * \addtogroup regexparser Regular expression + * @{ + * + * \addtogroup regexparser_compiler Compiler + * @{ + */ + /** * RegExp opcodes */ @@ -31,54 +41,60 @@ typedef enum /* Group opcode order is important, because RE_IS_CAPTURE_GROUP is based on it. * Change it carefully. Capture opcodes should be at first. */ - RE_OP_CAPTURE_GROUP_START, - RE_OP_CAPTURE_GREEDY_ZERO_GROUP_START, - RE_OP_CAPTURE_NON_GREEDY_ZERO_GROUP_START, - RE_OP_CAPTURE_GREEDY_GROUP_END, - RE_OP_CAPTURE_NON_GREEDY_GROUP_END, - RE_OP_NON_CAPTURE_GROUP_START, - RE_OP_NON_CAPTURE_GREEDY_ZERO_GROUP_START, - RE_OP_NON_CAPTURE_NON_GREEDY_ZERO_GROUP_START, - RE_OP_NON_CAPTURE_GREEDY_GROUP_END, - RE_OP_NON_CAPTURE_NON_GREEDY_GROUP_END, + RE_OP_CAPTURE_GROUP_START, /**< group start */ + RE_OP_CAPTURE_GREEDY_ZERO_GROUP_START, /**< greedy zero group start */ + RE_OP_CAPTURE_NON_GREEDY_ZERO_GROUP_START, /**< non-greedy zero group start */ + RE_OP_CAPTURE_GREEDY_GROUP_END, /**< greedy group end */ + RE_OP_CAPTURE_NON_GREEDY_GROUP_END, /**< non-greedy group end */ + RE_OP_NON_CAPTURE_GROUP_START, /**< non-capture group start */ + RE_OP_NON_CAPTURE_GREEDY_ZERO_GROUP_START, /**< non-capture greedy zero group start */ + RE_OP_NON_CAPTURE_NON_GREEDY_ZERO_GROUP_START, /**< non-capture non-greedy zero group start */ + RE_OP_NON_CAPTURE_GREEDY_GROUP_END, /**< non-capture greedy group end */ + RE_OP_NON_CAPTURE_NON_GREEDY_GROUP_END, /**< non-capture non-greedy group end */ - RE_OP_MATCH, - RE_OP_CHAR, - RE_OP_SAVE_AT_START, - RE_OP_SAVE_AND_MATCH, - RE_OP_PERIOD, - RE_OP_ALTERNATIVE, - RE_OP_GREEDY_ITERATOR, - RE_OP_NON_GREEDY_ITERATOR, - RE_OP_ASSERT_START, - RE_OP_ASSERT_END, - RE_OP_ASSERT_WORD_BOUNDARY, - RE_OP_ASSERT_NOT_WORD_BOUNDARY, - RE_OP_LOOKAHEAD_POS, - RE_OP_LOOKAHEAD_NEG, - RE_OP_BACKREFERENCE, - RE_OP_CHAR_CLASS, - RE_OP_INV_CHAR_CLASS + RE_OP_MATCH, /**< match */ + RE_OP_CHAR, /**< any character */ + RE_OP_SAVE_AT_START, /**< save at start */ + RE_OP_SAVE_AND_MATCH, /**< save and match */ + RE_OP_PERIOD, /**< . */ + RE_OP_ALTERNATIVE, /**< | */ + RE_OP_GREEDY_ITERATOR, /**< greedy iterator */ + RE_OP_NON_GREEDY_ITERATOR, /**< non-greedy iterator */ + RE_OP_ASSERT_START, /**< ^ */ + RE_OP_ASSERT_END, /**< $ */ + RE_OP_ASSERT_WORD_BOUNDARY, /**< \b */ + RE_OP_ASSERT_NOT_WORD_BOUNDARY, /**< \B */ + RE_OP_LOOKAHEAD_POS, /**< lookahead pos */ + RE_OP_LOOKAHEAD_NEG, /**< lookahead neg */ + RE_OP_BACKREFERENCE, /**< \[0..9] */ + RE_OP_CHAR_CLASS, /**< [ ] */ + RE_OP_INV_CHAR_CLASS /**< [^ ] */ } re_opcode_t; /** - * Check if a RegExp opcode is a capture group or not + * Compiled byte code data. */ -#define RE_IS_CAPTURE_GROUP(x) (((x) < RE_OP_NON_CAPTURE_GROUP_START) ? 1 : 0) +typedef struct +{ + uint16_t flags; /**< RegExp flags */ + mem_cpointer_t pattern_cp; /**< original RegExp pattern */ + uint32_t num_of_captures; /**< number of capturing brackets */ + uint32_t num_of_non_captures; /**< number of non capturing brackets */ +} re_compiled_code_t; /** - * Type of bytecode elements + * Check if a RegExp opcode is a capture group or not */ -typedef uint8_t re_bytecode_t; +#define RE_IS_CAPTURE_GROUP(x) (((x) < RE_OP_NON_CAPTURE_GROUP_START) ? 1 : 0) /** * Context of RegExp bytecode container */ typedef struct { - re_bytecode_t *block_start_p; /**< start of bytecode block */ - re_bytecode_t *block_end_p; /**< end of bytecode block */ - re_bytecode_t *current_p; /**< current position in bytecode */ + uint8_t *block_start_p; /**< start of bytecode block */ + uint8_t *block_end_p; /**< end of bytecode block */ + uint8_t *current_p; /**< current position in bytecode */ } re_bytecode_ctx_t; /** @@ -86,23 +102,29 @@ typedef struct */ typedef struct { - uint8_t flags; /**< RegExp flags */ - uint32_t num_of_captures; /**< number of capture groups */ - uint32_t num_of_non_captures; /**< number of non-capture groups */ - uint32_t highest_backref; /**< highest backreference */ + uint16_t flags; /**< RegExp flags */ + uint32_t num_of_captures; /**< number of capture groups */ + uint32_t num_of_non_captures; /**< number of non-capture groups */ + uint32_t highest_backref; /**< highest backreference */ re_bytecode_ctx_t *bytecode_ctx_p; /**< pointer of RegExp bytecode context */ - re_token_t current_token; /**< current token */ - re_parser_ctx_t *parser_ctx_p; /**< pointer of RegExp parser context */ + re_token_t current_token; /**< current token */ + re_parser_ctx_t *parser_ctx_p; /**< pointer of RegExp parser context */ } re_compiler_ctx_t; ecma_completion_value_t -re_compile_bytecode (re_bytecode_t **, ecma_string_t *, uint8_t); +re_compile_bytecode (re_compiled_code_t **, ecma_string_t *, uint16_t); re_opcode_t -re_get_opcode (re_bytecode_t **); +re_get_opcode (uint8_t **); uint32_t -re_get_value (re_bytecode_t **); +re_get_value (uint8_t **); + +/** + * @} + * @} + * @} + */ -#endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ -#endif /* RE_COMPILER_H */ +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ +#endif /* !RE_COMPILER_H */ diff --git a/jerry-core/parser/regexp/re-parser.cpp b/jerry-core/parser/regexp/re-parser.cpp index 4806bcba4d..256036c6a0 100644 --- a/jerry-core/parser/regexp/re-parser.cpp +++ b/jerry-core/parser/regexp/re-parser.cpp @@ -24,6 +24,16 @@ #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN +/** \addtogroup parser Parser + * @{ + * + * \addtogroup regexparser Regular expression + * @{ + * + * \addtogroup regexparser_parser Parser + * @{ + */ + /** * Lookup a character in the input string. * @@ -894,4 +904,10 @@ re_parse_next_token (re_parser_ctx_t *parser_ctx_p, /**< RegExp parser context * return ret_value; } /* re_parse_next_token */ -#endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ +/** + * @} + * @} + * @} + */ + +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ diff --git a/jerry-core/parser/regexp/re-parser.h b/jerry-core/parser/regexp/re-parser.h index c53b13eedb..35ffcbe628 100644 --- a/jerry-core/parser/regexp/re-parser.h +++ b/jerry-core/parser/regexp/re-parser.h @@ -1,5 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -19,49 +19,65 @@ #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN -#include "opcodes-dumper.h" +/** \addtogroup parser Parser + * @{ + * + * \addtogroup regexparser Regular expression + * @{ + * + * \addtogroup regexparser_bytecode Bytecode + * @{ + */ /** * RegExp token type definitions */ typedef enum { - RE_TOK_EOF, /* EOF */ - RE_TOK_BACKREFERENCE, /* \[0..9] */ - RE_TOK_CHAR, /* any character */ - RE_TOK_ALTERNATIVE, /* | */ - RE_TOK_ASSERT_START, /* ^ */ - RE_TOK_ASSERT_END, /* $ */ - RE_TOK_PERIOD, /* . */ - RE_TOK_START_CAPTURE_GROUP, /* ( */ - RE_TOK_START_NON_CAPTURE_GROUP, /* (?: */ - RE_TOK_END_GROUP, /* ')' */ + RE_TOK_EOF, /* EOF */ + RE_TOK_BACKREFERENCE, /* \[0..9] */ + RE_TOK_CHAR, /* any character */ + RE_TOK_ALTERNATIVE, /* | */ + RE_TOK_ASSERT_START, /* ^ */ + RE_TOK_ASSERT_END, /* $ */ + RE_TOK_PERIOD, /* . */ + RE_TOK_START_CAPTURE_GROUP, /* ( */ + RE_TOK_START_NON_CAPTURE_GROUP, /* (?: */ + RE_TOK_END_GROUP, /* ')' */ RE_TOK_ASSERT_START_POS_LOOKAHEAD, /* (?= */ RE_TOK_ASSERT_START_NEG_LOOKAHEAD, /* (?! */ - RE_TOK_ASSERT_WORD_BOUNDARY, /* \b */ - RE_TOK_ASSERT_NOT_WORD_BOUNDARY, /* \B */ - RE_TOK_DIGIT, /* \d */ - RE_TOK_NOT_DIGIT, /* \D */ - RE_TOK_WHITE, /* \s */ - RE_TOK_NOT_WHITE, /* \S */ - RE_TOK_WORD_CHAR, /* \w */ - RE_TOK_NOT_WORD_CHAR, /* \W */ - RE_TOK_START_CHAR_CLASS, /* [ ] */ - RE_TOK_START_INV_CHAR_CLASS, /* [^ ] */ + RE_TOK_ASSERT_WORD_BOUNDARY, /* \b */ + RE_TOK_ASSERT_NOT_WORD_BOUNDARY, /* \B */ + RE_TOK_DIGIT, /* \d */ + RE_TOK_NOT_DIGIT, /* \D */ + RE_TOK_WHITE, /* \s */ + RE_TOK_NOT_WHITE, /* \S */ + RE_TOK_WORD_CHAR, /* \w */ + RE_TOK_NOT_WORD_CHAR, /* \W */ + RE_TOK_START_CHAR_CLASS, /* [ ] */ + RE_TOK_START_INV_CHAR_CLASS, /* [^ ] */ } re_token_type_t; + /** - * RegExp constant of infinite - */ + * @} + * + * \addtogroup regexparser_parser Parser + * @{ + */ + +/** + * RegExp constant of infinite + */ #define RE_ITERATOR_INFINITE ((uint32_t)-1) /** - * Maximum number of decimal escape digits - */ + * Maximum number of decimal escape digits + */ #define RE_MAX_RE_DECESC_DIGITS 9 /** - * Undefined character (out of the range of the codeunit) - */ + * Undefined character (out of the range of the codeunit) + */ #define RE_CHAR_UNDEF 0xFFFFFFFF /** @@ -69,11 +85,11 @@ typedef enum */ typedef struct { - re_token_type_t type; /**< type of the token */ - uint32_t value; /**< value of the token */ - uint32_t qmin; /**< minimum number of token iterations */ - uint32_t qmax; /**< maximum number of token iterations */ - bool greedy; /**< type of iteration */ + re_token_type_t type; /**< type of the token */ + uint32_t value; /**< value of the token */ + uint32_t qmin; /**< minimum number of token iterations */ + uint32_t qmax; /**< maximum number of token iterations */ + bool greedy; /**< type of iteration */ } re_token_t; /** @@ -82,10 +98,10 @@ typedef struct typedef struct { lit_utf8_byte_t *input_start_p; /**< start of input pattern */ - lit_utf8_byte_t *input_curr_p; /**< current position in input pattern */ - lit_utf8_byte_t *input_end_p; /**< end of input pattern */ - int num_of_groups; /**< number of groups */ - uint32_t num_of_classes; /**< number of character classes */ + lit_utf8_byte_t *input_curr_p; /**< current position in input pattern */ + lit_utf8_byte_t *input_end_p; /**< end of input pattern */ + int num_of_groups; /**< number of groups */ + uint32_t num_of_classes; /**< number of character classes */ } re_parser_ctx_t; typedef void (*re_char_class_callback) (void *re_ctx_p, uint32_t start, uint32_t end); @@ -96,5 +112,10 @@ re_parse_char_class (re_parser_ctx_t *, re_char_class_callback, void *, re_token ecma_completion_value_t re_parse_next_token (re_parser_ctx_t *, re_token_t *); -#endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ -#endif /* RE_PARSER_H */ +/** + * @} + * @} + */ + +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ +#endif /* !RE_PARSER_H */ diff --git a/jerry-core/vm/opcodes-agnostic.cpp b/jerry-core/vm/opcodes-agnostic.cpp deleted file mode 100644 index 9b49291864..0000000000 --- a/jerry-core/vm/opcodes-agnostic.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "opcodes.h" -#include "opcodes-ecma-support.h" - -/** - * 'Jump down if true' opcode handler. - * - * Note: - * current instruction's position changes by adding specified offset - * if argument evaluates to true. - */ -ecma_completion_value_t -opfunc_is_true_jmp_down (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t cond_var_idx = instr.data.is_true_jmp_down.value; - const vm_instr_counter_t offset = vm_calc_instr_counter_from_idx_idx (instr.data.is_true_jmp_down.oc_idx_1, - instr.data.is_true_jmp_down.oc_idx_2); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (cond_value, get_variable_value (frame_ctx_p, cond_var_idx, false), ret_value); - - ecma_completion_value_t to_bool_completion = ecma_op_to_boolean (cond_value); - JERRY_ASSERT (ecma_is_completion_value_normal (to_bool_completion)); - - if (ecma_is_completion_value_normal_true (to_bool_completion)) - { - JERRY_ASSERT ((uint32_t) frame_ctx_p->pos + offset < MAX_OPCODES); - frame_ctx_p->pos = (vm_instr_counter_t) (frame_ctx_p->pos + offset); - } - else - { - frame_ctx_p->pos++; - } - - ret_value = ecma_make_empty_completion_value (); - - ECMA_FINALIZE (cond_value); - - return ret_value; -} - -/* Likewise to opfunc_is_true_jmp_down, but jumps up. */ -ecma_completion_value_t -opfunc_is_true_jmp_up (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t cond_var_idx = instr.data.is_true_jmp_up.value; - const vm_instr_counter_t offset = vm_calc_instr_counter_from_idx_idx (instr.data.is_true_jmp_up.oc_idx_1, - instr.data.is_true_jmp_up.oc_idx_2); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (cond_value, get_variable_value (frame_ctx_p, cond_var_idx, false), ret_value); - - ecma_completion_value_t to_bool_completion = ecma_op_to_boolean (cond_value); - JERRY_ASSERT (ecma_is_completion_value_normal (to_bool_completion)); - - if (ecma_is_completion_value_normal_true (to_bool_completion)) - { - JERRY_ASSERT ((uint32_t) frame_ctx_p->pos >= offset); - frame_ctx_p->pos = (vm_instr_counter_t) (frame_ctx_p->pos - offset); - } - else - { - frame_ctx_p->pos++; - } - - ret_value = ecma_make_empty_completion_value (); - - ECMA_FINALIZE (cond_value); - - return ret_value; -} - -/** - * 'Jump down if false' opcode handler. - * - * Note: - * current instruction's position changes by adding specified offset - * if argument evaluates to false. - */ -ecma_completion_value_t -opfunc_is_false_jmp_down (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t cond_var_idx = instr.data.is_false_jmp_down.value; - const vm_instr_counter_t offset = vm_calc_instr_counter_from_idx_idx (instr.data.is_false_jmp_down.oc_idx_1, - instr.data.is_false_jmp_down.oc_idx_2); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (cond_value, get_variable_value (frame_ctx_p, cond_var_idx, false), ret_value); - - ecma_completion_value_t to_bool_completion = ecma_op_to_boolean (cond_value); - JERRY_ASSERT (ecma_is_completion_value_normal (to_bool_completion)); - - if (!ecma_is_completion_value_normal_true (to_bool_completion)) - { - JERRY_ASSERT ((uint32_t) frame_ctx_p->pos + offset < MAX_OPCODES); - frame_ctx_p->pos = (vm_instr_counter_t) (frame_ctx_p->pos + offset); - } - else - { - frame_ctx_p->pos++; - } - - ret_value = ecma_make_empty_completion_value (); - - ECMA_FINALIZE (cond_value); - - return ret_value; -} - -/* Likewise to opfunc_is_false_jmp_down, but jumps up. */ -ecma_completion_value_t -opfunc_is_false_jmp_up (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t cond_var_idx = instr.data.is_false_jmp_up.value; - const vm_instr_counter_t offset = vm_calc_instr_counter_from_idx_idx (instr.data.is_false_jmp_up.oc_idx_1, - instr.data.is_false_jmp_up.oc_idx_2); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (cond_value, get_variable_value (frame_ctx_p, cond_var_idx, false), ret_value); - - ecma_completion_value_t to_bool_completion = ecma_op_to_boolean (cond_value); - JERRY_ASSERT (ecma_is_completion_value_normal (to_bool_completion)); - - if (!ecma_is_completion_value_normal_true (to_bool_completion)) - { - JERRY_ASSERT ((uint32_t) frame_ctx_p->pos >= offset); - frame_ctx_p->pos = (vm_instr_counter_t) (frame_ctx_p->pos - offset); - } - else - { - frame_ctx_p->pos++; - } - - ret_value = ecma_make_empty_completion_value (); - - ECMA_FINALIZE (cond_value); - - return ret_value; -} - -/** - * 'Jump down' opcode handler. - * - * Note: - * the opcode changes adds specified value to current instruction position - */ -ecma_completion_value_t -opfunc_jmp_down (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_instr_counter_t offset = vm_calc_instr_counter_from_idx_idx (instr.data.jmp_down.oc_idx_1, - instr.data.jmp_down.oc_idx_2); - - JERRY_ASSERT (((uint32_t) frame_ctx_p->pos + offset < MAX_OPCODES)); - - frame_ctx_p->pos = (vm_instr_counter_t) (frame_ctx_p->pos + offset); - - return ecma_make_empty_completion_value (); -} - -/** - * 'Jump up' opcode handler. - * - * Note: - * the opcode changes substracts specified value from current instruction position - */ -ecma_completion_value_t -opfunc_jmp_up (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_instr_counter_t offset = vm_calc_instr_counter_from_idx_idx (instr.data.jmp_up.oc_idx_1, - instr.data.jmp_up.oc_idx_2); - JERRY_ASSERT ((uint32_t) frame_ctx_p->pos >= offset); - - frame_ctx_p->pos = (vm_instr_counter_t) (frame_ctx_p->pos - offset); - - return ecma_make_empty_completion_value (); -} - -/** - * 'Break or continue jump' opcode handler. - * - * Note: - * the opcode returns break-continue completion value with jump target - */ -ecma_completion_value_t -opfunc_jmp_break_continue (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - vm_instr_counter_t target = frame_ctx_p->pos; - target = (vm_instr_counter_t) (target + vm_calc_instr_counter_from_idx_idx (instr.data.jmp_down.oc_idx_1, - instr.data.jmp_down.oc_idx_2)); - - return ecma_make_jump_completion_value (target); -} /* opfunc_jmp_break_continue */ diff --git a/jerry-core/vm/opcodes-ecma-arithmetics.cpp b/jerry-core/vm/opcodes-ecma-arithmetics.cpp index 7dfdaa2bf4..db8a9ee091 100644 --- a/jerry-core/vm/opcodes-ecma-arithmetics.cpp +++ b/jerry-core/vm/opcodes-ecma-arithmetics.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-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. @@ -13,22 +14,20 @@ * limitations under the License. */ -#include "opcodes.h" -#include "opcodes-ecma-support.h" +#include "ecma-alloc.h" +#include "ecma-conversion.h" +#include "ecma-helpers.h" #include "ecma-number-arithmetic.h" +#include "ecma-try-catch-macro.h" +#include "opcodes.h" #include "jrt-libc-includes.h" -/** - * Number arithmetic operations. +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_opcodes Opcodes + * @{ */ -typedef enum -{ - number_arithmetic_addition, /**< addition */ - number_arithmetic_substraction, /**< substraction */ - number_arithmetic_multiplication, /**< multiplication */ - number_arithmetic_division, /**< division */ - number_arithmetic_remainder, /**< remainder calculation */ -} number_arithmetic_op; /** * Perform ECMA number arithmetic operation. @@ -41,52 +40,48 @@ typedef enum * @return completion value * Returned value must be freed with ecma_free_completion_value */ -static ecma_completion_value_t -do_number_arithmetic (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - vm_idx_t dst_var_idx, /**< destination variable identifier */ - number_arithmetic_op op, /**< number arithmetic operation */ +ecma_completion_value_t +do_number_arithmetic (number_arithmetic_op op, /**< number arithmetic operation */ ecma_value_t left_value, /**< left value */ - ecma_value_t right_value) /** right value */ + ecma_value_t right_value) /**< right value */ { ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (num_right, right_value, ret_value); - ecma_number_t *res_p = frame_ctx_p->tmp_num_p; + ecma_number_t *res_p = ecma_alloc_number (); switch (op) { - case number_arithmetic_addition: + case NUMBER_ARITHMETIC_ADDITION: { *res_p = ecma_number_add (num_left, num_right); break; } - case number_arithmetic_substraction: + case NUMBER_ARITHMETIC_SUBSTRACTION: { *res_p = ecma_number_substract (num_left, num_right); break; } - case number_arithmetic_multiplication: + case NUMBER_ARITHMETIC_MULTIPLICATION: { *res_p = ecma_number_multiply (num_left, num_right); break; } - case number_arithmetic_division: + case NUMBER_ARITHMETIC_DIVISION: { *res_p = ecma_number_divide (num_left, num_right); break; } - case number_arithmetic_remainder: + case NUMBER_ARITHMETIC_REMAINDER: { *res_p = ecma_op_number_remainder (num_left, num_right); break; } } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (res_p)); + ret_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_number_value (res_p)); ECMA_OP_TO_NUMBER_FINALIZE (num_right); ECMA_OP_TO_NUMBER_FINALIZE (num_left); @@ -103,17 +98,11 @@ do_number_arithmetic (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_addition (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_addition (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.addition.dst; - const vm_idx_t left_var_idx = instr.data.addition.var_left; - const vm_idx_t right_var_idx = instr.data.addition.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (prim_left_value, ecma_op_to_primitive (left_value, ECMA_PREFERRED_TYPE_NO), @@ -134,172 +123,24 @@ opfunc_addition (vm_instr_t instr, /**< instruction */ ecma_string_t *concat_str_p = ecma_concat_ecma_strings (string1_p, string2_p); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, ecma_make_string_value (concat_str_p)); - - ecma_deref_ecma_string (concat_str_p); + ret_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_string_value (concat_str_p)); ECMA_FINALIZE (str_right_value); ECMA_FINALIZE (str_left_value); } else { - ret_value = do_number_arithmetic (frame_ctx_p, - dst_var_idx, - number_arithmetic_addition, - prim_left_value, - prim_right_value); + ret_value = do_number_arithmetic (NUMBER_ARITHMETIC_ADDITION, + left_value, + right_value); } ECMA_FINALIZE (prim_right_value); ECMA_FINALIZE (prim_left_value); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_addition */ -/** - * 'Substraction' opcode handler. - * - * See also: ECMA-262 v5, 11.6.2 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_substraction (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.substraction.dst; - const vm_idx_t left_var_idx = instr.data.substraction.var_left; - const vm_idx_t right_var_idx = instr.data.substraction.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_arithmetic (frame_ctx_p, - dst_var_idx, - number_arithmetic_substraction, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_substraction */ - -/** - * 'Multiplication' opcode handler. - * - * See also: ECMA-262 v5, 11.5, 11.5.1 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_multiplication (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.multiplication.dst; - const vm_idx_t left_var_idx = instr.data.multiplication.var_left; - const vm_idx_t right_var_idx = instr.data.multiplication.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_arithmetic (frame_ctx_p, - dst_var_idx, - number_arithmetic_multiplication, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_multiplication */ - -/** - * 'Division' opcode handler. - * - * See also: ECMA-262 v5, 11.5, 11.5.2 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_division (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.division.dst; - const vm_idx_t left_var_idx = instr.data.division.var_left; - const vm_idx_t right_var_idx = instr.data.division.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_arithmetic (frame_ctx_p, - dst_var_idx, - number_arithmetic_division, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_division */ - -/** - * 'Remainder calculation' opcode handler. - * - * See also: ECMA-262 v5, 11.5, 11.5.3 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_remainder (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.remainder.dst; - const vm_idx_t left_var_idx = instr.data.remainder.var_left; - const vm_idx_t right_var_idx = instr.data.remainder.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_arithmetic (frame_ctx_p, - dst_var_idx, - number_arithmetic_remainder, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_remainder */ - /** * 'Unary "+"' opcode handler. * @@ -309,30 +150,20 @@ opfunc_remainder (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_unary_plus (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_unary_plus (ecma_value_t left_value) /**< left value */ { - const vm_idx_t dst_var_idx = instr.data.remainder.dst; - const vm_idx_t var_idx = instr.data.remainder.var_left; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (var_value, get_variable_value (frame_ctx_p, var_idx, false), ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (num_var_value, - var_value, + left_value, ret_value); - ecma_number_t *tmp_p = frame_ctx_p->tmp_num_p; + ecma_number_t *tmp_p = ecma_alloc_number (); *tmp_p = num_var_value; - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (tmp_p)); + ret_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_number_value (tmp_p)); ECMA_OP_TO_NUMBER_FINALIZE (num_var_value); - ECMA_FINALIZE (var_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_unary_plus */ @@ -346,30 +177,25 @@ opfunc_unary_plus (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_unary_minus (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_unary_minus (ecma_value_t left_value) /**< left value */ { - const vm_idx_t dst_var_idx = instr.data.remainder.dst; - const vm_idx_t var_idx = instr.data.remainder.var_left; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (var_value, get_variable_value (frame_ctx_p, var_idx, false), ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (num_var_value, - var_value, + left_value, ret_value); - ecma_number_t *tmp_p = frame_ctx_p->tmp_num_p; + ecma_number_t *tmp_p = ecma_alloc_number (); *tmp_p = ecma_number_negate (num_var_value); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (tmp_p)); + ret_value = ecma_make_completion_value (ECMA_COMPLETION_TYPE_NORMAL, ecma_make_number_value (tmp_p)); ECMA_OP_TO_NUMBER_FINALIZE (num_var_value); - ECMA_FINALIZE (var_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_unary_minus */ + +/** + * @} + * @} + */ diff --git a/jerry-core/vm/opcodes-ecma-bitwise.cpp b/jerry-core/vm/opcodes-ecma-bitwise.cpp index 15d6a35076..b7823c9db0 100644 --- a/jerry-core/vm/opcodes-ecma-bitwise.cpp +++ b/jerry-core/vm/opcodes-ecma-bitwise.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-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. @@ -13,22 +14,18 @@ * limitations under the License. */ +#include "ecma-alloc.h" +#include "ecma-conversion.h" +#include "ecma-helpers.h" +#include "ecma-try-catch-macro.h" #include "opcodes.h" -#include "opcodes-ecma-support.h" -/** - * Number bitwise logic operations. +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_opcodes Opcodes + * @{ */ -typedef enum -{ - number_bitwise_logic_and, /**< bitwise AND calculation */ - number_bitwise_logic_or, /**< bitwise OR calculation */ - number_bitwise_logic_xor, /**< bitwise XOR calculation */ - number_bitwise_shift_left, /**< bitwise LEFT SHIFT calculation */ - number_bitwise_shift_right, /**< bitwise RIGHT_SHIFT calculation */ - number_bitwise_shift_uright, /**< bitwise UNSIGNED RIGHT SHIFT calculation */ - number_bitwise_not, /**< bitwise NOT calculation */ -} number_bitwise_logic_op; /** * Perform ECMA number logic operation. @@ -41,10 +38,8 @@ typedef enum * @return completion value * Returned value must be freed with ecma_free_completion_value */ -static ecma_completion_value_t -do_number_bitwise_logic (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - vm_idx_t dst_var_idx, /**< destination variable identifier */ - number_bitwise_logic_op op, /**< number bitwise logic operation */ +ecma_completion_value_t +do_number_bitwise_logic (number_bitwise_logic_op op, /**< number bitwise logic operation */ ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /** right value */ { @@ -53,56 +48,53 @@ do_number_bitwise_logic (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context * ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); ECMA_OP_TO_NUMBER_TRY_CATCH (num_right, right_value, ret_value); - ecma_number_t* res_p = frame_ctx_p->tmp_num_p; + ecma_number_t* res_p = ecma_alloc_number (); int32_t left_int32 = ecma_number_to_int32 (num_left); - // int32_t right_int32 = ecma_number_to_int32 (num_right); uint32_t left_uint32 = ecma_number_to_uint32 (num_left); uint32_t right_uint32 = ecma_number_to_uint32 (num_right); switch (op) { - case number_bitwise_logic_and: + case NUMBER_BITWISE_LOGIC_AND: { *res_p = ecma_int32_to_number ((int32_t) (left_uint32 & right_uint32)); break; } - case number_bitwise_logic_or: + case NUMBER_BITWISE_LOGIC_OR: { *res_p = ecma_int32_to_number ((int32_t) (left_uint32 | right_uint32)); break; } - case number_bitwise_logic_xor: + case NUMBER_BITWISE_LOGIC_XOR: { *res_p = ecma_int32_to_number ((int32_t) (left_uint32 ^ right_uint32)); break; } - case number_bitwise_shift_left: + case NUMBER_BITWISE_SHIFT_LEFT: { *res_p = ecma_int32_to_number (left_int32 << (right_uint32 & 0x1F)); break; } - case number_bitwise_shift_right: + case NUMBER_BITWISE_SHIFT_RIGHT: { *res_p = ecma_int32_to_number (left_int32 >> (right_uint32 & 0x1F)); break; } - case number_bitwise_shift_uright: + case NUMBER_BITWISE_SHIFT_URIGHT: { *res_p = ecma_uint32_to_number (left_uint32 >> (right_uint32 & 0x1F)); break; } - case number_bitwise_not: + case NUMBER_BITWISE_NOT: { *res_p = ecma_int32_to_number ((int32_t) ~right_uint32); break; } } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (res_p)); + ret_value = ecma_make_normal_completion_value (ecma_make_number_value (res_p)); ECMA_OP_TO_NUMBER_FINALIZE (num_right); ECMA_OP_TO_NUMBER_FINALIZE (num_left); @@ -111,243 +103,6 @@ do_number_bitwise_logic (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context * } /* do_number_bitwise_logic */ /** - * 'Bitwise AND' opcode handler. - * - * See also: ECMA-262 v5, 11.10 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_b_and (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_and.dst; - const vm_idx_t left_var_idx = instr.data.b_and.var_left; - const vm_idx_t right_var_idx = instr.data.b_and.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_logic_and, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_and */ - -/** - * 'Bitwise OR' opcode handler. - * - * See also: ECMA-262 v5, 11.10 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_b_or (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_or.dst; - const vm_idx_t left_var_idx = instr.data.b_or.var_left; - const vm_idx_t right_var_idx = instr.data.b_or.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_logic_or, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_or */ - -/** - * 'Bitwise XOR' opcode handler. - * - * See also: ECMA-262 v5, 11.10 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_b_xor (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_xor.dst; - const vm_idx_t left_var_idx = instr.data.b_xor.var_left; - const vm_idx_t right_var_idx = instr.data.b_xor.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_logic_xor, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_xor */ - -/** - * 'Left Shift Operator' opcode handler. - * - * See also: ECMA-262 v5, 11.7.1 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_b_shift_left (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_shift_left.dst; - const vm_idx_t left_var_idx = instr.data.b_shift_left.var_left; - const vm_idx_t right_var_idx = instr.data.b_shift_left.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_shift_left, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_shift_left */ - -/** - * 'Right Shift Operator' opcode handler. - * - * See also: ECMA-262 v5, 11.7.2 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_b_shift_right (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_shift_right.dst; - const vm_idx_t left_var_idx = instr.data.b_shift_right.var_left; - const vm_idx_t right_var_idx = instr.data.b_shift_right.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_shift_right, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_shift_right */ - -/** - * 'Unsigned Right Shift Operator' opcode handler. - * - * See also: ECMA-262 v5, 11.7.3 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_b_shift_uright (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_shift_uright.dst; - const vm_idx_t left_var_idx = instr.data.b_shift_uright.var_left; - const vm_idx_t right_var_idx = instr.data.b_shift_uright.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_shift_uright, - left_value, - right_value); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_shift_uright */ - -/** - * 'Bitwise NOT Operator' opcode handler. - * - * See also: ECMA-262 v5, 10.4 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value + * @} + * @} */ -ecma_completion_value_t -opfunc_b_not (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.b_not.dst; - const vm_idx_t right_var_idx = instr.data.b_not.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - - ret_value = do_number_bitwise_logic (frame_ctx_p, - dst_var_idx, - number_bitwise_not, - right_value, - right_value); - - ECMA_FINALIZE (right_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_b_not */ diff --git a/jerry-core/vm/opcodes-ecma-equality.cpp b/jerry-core/vm/opcodes-ecma-equality.cpp index 3f9caaac98..44c351193c 100644 --- a/jerry-core/vm/opcodes-ecma-equality.cpp +++ b/jerry-core/vm/opcodes-ecma-equality.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-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. @@ -13,8 +14,23 @@ * limitations under the License. */ +#include "ecma-builtins.h" +#include "ecma-comparison.h" +#include "ecma-exceptions.h" +#include "ecma-function-object.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-lex-env.h" +#include "ecma-try-catch-macro.h" #include "opcodes.h" -#include "opcodes-ecma-support.h" +#include "vm-defines.h" + +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_opcodes Opcodes + * @{ + */ /** * 'Equals' opcode handler. @@ -25,17 +41,11 @@ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_equal_value (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_equal_value (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.equal_value.dst; - const vm_idx_t left_var_idx = instr.data.equal_value.var_left; - const vm_idx_t right_var_idx = instr.data.equal_value.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (compare_result, ecma_op_abstract_equality_compare (left_value, right_value), @@ -43,14 +53,9 @@ opfunc_equal_value (vm_instr_t instr, /**< instruction */ JERRY_ASSERT (ecma_is_value_boolean (compare_result)); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, - compare_result); + ret_value = ecma_make_normal_completion_value (compare_result); ECMA_FINALIZE (compare_result); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_equal_value */ @@ -64,17 +69,11 @@ opfunc_equal_value (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_not_equal_value (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_not_equal_value (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.not_equal_value.dst; - const vm_idx_t left_var_idx = instr.data.not_equal_value.var_left; - const vm_idx_t right_var_idx = instr.data.not_equal_value.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (compare_result, ecma_op_abstract_equality_compare (left_value, right_value), ret_value); @@ -83,15 +82,10 @@ opfunc_not_equal_value (vm_instr_t instr, /**< instruction */ bool is_equal = ecma_is_value_true (compare_result); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, - ecma_make_simple_value (is_equal ? ECMA_SIMPLE_VALUE_FALSE - : ECMA_SIMPLE_VALUE_TRUE)); + ret_value = ecma_make_normal_completion_value (ecma_make_simple_value (is_equal ? ECMA_SIMPLE_VALUE_FALSE + : ECMA_SIMPLE_VALUE_TRUE)); ECMA_FINALIZE (compare_result); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_not_equal_value */ @@ -105,30 +99,13 @@ opfunc_not_equal_value (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_equal_value_type (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_equal_value_type (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.equal_value_type.dst; - const vm_idx_t left_var_idx = instr.data.equal_value_type.var_left; - const vm_idx_t right_var_idx = instr.data.equal_value_type.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - bool is_equal = ecma_op_strict_equality_compare (left_value, right_value); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, - ecma_make_simple_value (is_equal ? ECMA_SIMPLE_VALUE_TRUE - : ECMA_SIMPLE_VALUE_FALSE)); - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; + return ecma_make_normal_completion_value (ecma_make_simple_value (is_equal ? ECMA_SIMPLE_VALUE_TRUE + : ECMA_SIMPLE_VALUE_FALSE)); } /* opfunc_equal_value_type */ /** @@ -140,29 +117,16 @@ opfunc_equal_value_type (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_not_equal_value_type (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_not_equal_value_type (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.not_equal_value_type.dst; - const vm_idx_t left_var_idx = instr.data.not_equal_value_type.var_left; - const vm_idx_t right_var_idx = instr.data.not_equal_value_type.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - bool is_equal = ecma_op_strict_equality_compare (left_value, right_value); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, - ecma_make_simple_value (is_equal ? ECMA_SIMPLE_VALUE_FALSE - : ECMA_SIMPLE_VALUE_TRUE)); - - - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - - return ret_value; + return ecma_make_normal_completion_value (ecma_make_simple_value (is_equal ? ECMA_SIMPLE_VALUE_FALSE + : ECMA_SIMPLE_VALUE_TRUE)); } /* opfunc_not_equal_value_type */ + +/** + * @} + * @} + */ diff --git a/jerry-core/vm/opcodes-ecma-relational.cpp b/jerry-core/vm/opcodes-ecma-relational.cpp index 243bfbb3b3..32353df3e2 100644 --- a/jerry-core/vm/opcodes-ecma-relational.cpp +++ b/jerry-core/vm/opcodes-ecma-relational.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-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. @@ -13,8 +14,20 @@ * limitations under the License. */ +#include "ecma-comparison.h" +#include "ecma-conversion.h" +#include "ecma-exceptions.h" +#include "ecma-helpers.h" +#include "ecma-objects.h" +#include "ecma-try-catch-macro.h" #include "opcodes.h" -#include "opcodes-ecma-support.h" + +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_opcodes Opcodes + * @{ + */ /** * 'Less-than' opcode handler. @@ -25,17 +38,11 @@ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_less_than (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_less_than (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.less_than.dst; - const vm_idx_t left_var_idx = instr.data.less_than.var_left; - const vm_idx_t right_var_idx = instr.data.less_than.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (compare_result, ecma_op_abstract_relational_compare (left_value, right_value, true), ret_value); @@ -53,13 +60,9 @@ opfunc_less_than (vm_instr_t instr, /**< instruction */ res = (ecma_is_value_true (compare_result) ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE); } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, ecma_make_simple_value (res)); + ret_value = ecma_make_simple_completion_value (res); ECMA_FINALIZE (compare_result); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_less_than */ @@ -73,17 +76,11 @@ opfunc_less_than (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_greater_than (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_greater_than (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.greater_than.dst; - const vm_idx_t left_var_idx = instr.data.greater_than.var_left; - const vm_idx_t right_var_idx = instr.data.greater_than.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (compare_result, ecma_op_abstract_relational_compare (right_value, left_value, false), ret_value); @@ -101,13 +98,9 @@ opfunc_greater_than (vm_instr_t instr, /**< instruction */ res = (ecma_is_value_true (compare_result) ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE); } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, ecma_make_simple_value (res)); + ret_value = ecma_make_simple_completion_value (res); ECMA_FINALIZE (compare_result); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_greater_than */ @@ -121,17 +114,11 @@ opfunc_greater_than (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_less_or_equal_than (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_less_or_equal_than (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.less_or_equal_than.dst; - const vm_idx_t left_var_idx = instr.data.less_or_equal_than.var_left; - const vm_idx_t right_var_idx = instr.data.less_or_equal_than.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (compare_result, ecma_op_abstract_relational_compare (right_value, left_value, false), ret_value); @@ -156,13 +143,9 @@ opfunc_less_or_equal_than (vm_instr_t instr, /**< instruction */ } } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, ecma_make_simple_value (res)); + ret_value = ecma_make_simple_completion_value (res); ECMA_FINALIZE (compare_result); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_less_or_equal_than */ @@ -176,17 +159,11 @@ opfunc_less_or_equal_than (vm_instr_t instr, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_greater_or_equal_than (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_greater_or_equal_than (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_var_idx = instr.data.greater_or_equal_than.dst; - const vm_idx_t left_var_idx = instr.data.greater_or_equal_than.var_left; - const vm_idx_t right_var_idx = instr.data.greater_or_equal_than.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); ECMA_TRY_CATCH (compare_result, ecma_op_abstract_relational_compare (left_value, right_value, true), ret_value); @@ -211,13 +188,9 @@ opfunc_greater_or_equal_than (vm_instr_t instr, /**< instruction */ } } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, ecma_make_simple_value (res)); + ret_value = ecma_make_simple_completion_value (res); ECMA_FINALIZE (compare_result); - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; return ret_value; } /* opfunc_greater_or_equal_than */ @@ -231,18 +204,11 @@ opfunc_greater_or_equal_than (vm_instr_t instr, /**< instruction */ * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t -opfunc_instanceof (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ +opfunc_instanceof (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_idx = instr.data.instanceof.dst; - const vm_idx_t left_var_idx = instr.data.instanceof.var_left; - const vm_idx_t right_var_idx = instr.data.instanceof.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - if (!ecma_is_value_object (right_value)) { ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); @@ -255,16 +221,11 @@ opfunc_instanceof (vm_instr_t instr __attr_unused___, /**< instruction */ ecma_op_object_has_instance (right_value_obj_p, left_value), ret_value); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_idx, is_instance_of); + ret_value = ecma_make_normal_completion_value (is_instance_of); ECMA_FINALIZE (is_instance_of); } - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - return ret_value; } /* opfunc_instanceof */ @@ -277,18 +238,11 @@ opfunc_instanceof (vm_instr_t instr __attr_unused___, /**< instruction */ * returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t -opfunc_in (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ +opfunc_in (ecma_value_t left_value, /**< left value */ + ecma_value_t right_value) /**< right value */ { - const vm_idx_t dst_idx = instr.data.in.dst; - const vm_idx_t left_var_idx = instr.data.in.var_left; - const vm_idx_t right_var_idx = instr.data.in.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (left_value, get_variable_value (frame_ctx_p, left_var_idx, false), ret_value); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - if (!ecma_is_value_object (right_value)) { ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); @@ -310,17 +264,15 @@ opfunc_in (vm_instr_t instr __attr_unused___, /**< instruction */ is_in = ECMA_SIMPLE_VALUE_FALSE; } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_idx, - ecma_make_simple_value (is_in)); + ret_value = ecma_make_simple_completion_value (is_in); ECMA_FINALIZE (str_left_value); } - ECMA_FINALIZE (right_value); - ECMA_FINALIZE (left_value); - - frame_ctx_p->pos++; - return ret_value; } /* opfunc_in */ + +/** + * @} + * @} + */ diff --git a/jerry-core/vm/opcodes-ecma-support.h b/jerry-core/vm/opcodes-ecma-support.h deleted file mode 100644 index 461064ae8b..0000000000 --- a/jerry-core/vm/opcodes-ecma-support.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OPCODES_ECMA_SUPPORT_H -#define OPCODES_ECMA_SUPPORT_H - -#include "ecma-alloc.h" -#include "ecma-array-object.h" -#include "ecma-builtins.h" -#include "ecma-comparison.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-number-arithmetic.h" -#include "ecma-objects.h" -#include "ecma-objects-general.h" -#include "ecma-reference.h" -#include "ecma-regexp-object.h" -#include "ecma-try-catch-macro.h" - -bool vm_is_reg_variable (vm_idx_t); -ecma_completion_value_t get_variable_value (vm_frame_ctx_t *, vm_idx_t, bool); -ecma_completion_value_t set_variable_value (vm_frame_ctx_t *, vm_instr_counter_t, vm_idx_t, ecma_value_t); -ecma_completion_value_t vm_fill_varg_list (vm_frame_ctx_t *, ecma_length_t, ecma_collection_header_t *); -extern vm_instr_counter_t vm_fill_params_list (const bytecode_data_header_t *, - vm_instr_counter_t, - ecma_length_t, - ecma_collection_header_t *); -extern ecma_completion_value_t vm_function_declaration (const bytecode_data_header_t *bytecode_header_p, - bool is_strict, - bool is_eval_code, - ecma_object_t *lex_env_p); -#endif /* OPCODES_ECMA_SUPPORT_H */ diff --git a/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp b/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp deleted file mode 100644 index c60a62eb8b..0000000000 --- a/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bytecode-data.h" -#include "jrt.h" -#include "vm.h" -#include "opcodes.h" -#include "opcodes-ecma-support.h" - -/** - * 'Try' opcode handler. - * - * See also: ECMA-262 v5, 12.14 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_try_block (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t block_end_oc_idx_1 = instr.data.try_block.oc_idx_1; - const vm_idx_t block_end_oc_idx_2 = instr.data.try_block.oc_idx_2; - const vm_instr_counter_t try_end_oc = (vm_instr_counter_t) ( - vm_calc_instr_counter_from_idx_idx (block_end_oc_idx_1, block_end_oc_idx_2) + frame_ctx_p->pos); - - frame_ctx_p->pos++; - - vm_run_scope_t run_scope_try = { frame_ctx_p->pos, try_end_oc }; - ecma_completion_value_t try_completion = vm_loop (frame_ctx_p, &run_scope_try); - JERRY_ASSERT ((!ecma_is_completion_value_empty (try_completion) && frame_ctx_p->pos <= try_end_oc) - || (ecma_is_completion_value_empty (try_completion) && frame_ctx_p->pos == try_end_oc)); - frame_ctx_p->pos = try_end_oc; - - vm_instr_t next_instr = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, frame_ctx_p->pos); - JERRY_ASSERT (next_instr.op_idx == VM_OP_META); - - if (next_instr.data.meta.type == OPCODE_META_TYPE_CATCH) - { - const vm_instr_counter_t catch_end_oc = (vm_instr_counter_t) ( - vm_read_instr_counter_from_meta (OPCODE_META_TYPE_CATCH, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos) + frame_ctx_p->pos); - frame_ctx_p->pos++; - - if (ecma_is_completion_value_throw (try_completion)) - { - next_instr = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, frame_ctx_p->pos); - JERRY_ASSERT (next_instr.op_idx == VM_OP_META); - JERRY_ASSERT (next_instr.data.meta.type == OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER); - - lit_cpointer_t catch_exc_val_var_name_lit_cp = bc_get_literal_cp_by_uid (next_instr.data.meta.data_1, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - frame_ctx_p->pos++; - - ecma_string_t *catch_exc_var_name_str_p = ecma_new_ecma_string_from_lit_cp (catch_exc_val_var_name_lit_cp); - - ecma_object_t *old_env_p = frame_ctx_p->lex_env_p; - ecma_object_t *catch_env_p = ecma_create_decl_lex_env (old_env_p); - ecma_completion_value_t completion = ecma_op_create_mutable_binding (catch_env_p, - catch_exc_var_name_str_p, - false); - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - - completion = ecma_op_set_mutable_binding (catch_env_p, - catch_exc_var_name_str_p, - ecma_get_completion_value_value (try_completion), - false); - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - - ecma_deref_ecma_string (catch_exc_var_name_str_p); - - frame_ctx_p->lex_env_p = catch_env_p; - - ecma_free_completion_value (try_completion); - - vm_run_scope_t run_scope_catch = { frame_ctx_p->pos, catch_end_oc }; - try_completion = vm_loop (frame_ctx_p, &run_scope_catch); - - frame_ctx_p->lex_env_p = old_env_p; - - ecma_deref_object (catch_env_p); - - JERRY_ASSERT ((!ecma_is_completion_value_empty (try_completion) && frame_ctx_p->pos <= catch_end_oc) - || (ecma_is_completion_value_empty (try_completion) && frame_ctx_p->pos == catch_end_oc)); - } - - frame_ctx_p->pos = catch_end_oc; - } - - next_instr = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, frame_ctx_p->pos); - JERRY_ASSERT (next_instr.op_idx == VM_OP_META); - - if (next_instr.data.meta.type == OPCODE_META_TYPE_FINALLY) - { - const vm_instr_counter_t finally_end_oc = (vm_instr_counter_t) ( - vm_read_instr_counter_from_meta (OPCODE_META_TYPE_FINALLY, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos) + frame_ctx_p->pos); - frame_ctx_p->pos++; - - vm_run_scope_t run_scope_finally = { frame_ctx_p->pos, finally_end_oc }; - ecma_completion_value_t finally_completion = vm_loop (frame_ctx_p, &run_scope_finally); - - JERRY_ASSERT ((!ecma_is_completion_value_empty (finally_completion) && frame_ctx_p->pos <= finally_end_oc) - || (ecma_is_completion_value_empty (finally_completion) && frame_ctx_p->pos == finally_end_oc)); - frame_ctx_p->pos = finally_end_oc; - - if (!ecma_is_completion_value_empty (finally_completion)) - { - ecma_free_completion_value (try_completion); - try_completion = finally_completion; - } - } - - next_instr = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, frame_ctx_p->pos++); - JERRY_ASSERT (next_instr.op_idx == VM_OP_META); - JERRY_ASSERT (next_instr.data.meta.type == OPCODE_META_TYPE_END_TRY_CATCH_FINALLY); - - return try_completion; -} /* opfunc_try_block */ diff --git a/jerry-core/vm/opcodes-for-in.cpp b/jerry-core/vm/opcodes-for-in.cpp deleted file mode 100644 index efc92cc629..0000000000 --- a/jerry-core/vm/opcodes-for-in.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bytecode-data.h" -#include "jrt.h" -#include "opcodes.h" -#include "opcodes-ecma-support.h" - -/** - * 'for-in' opcode handler - * - * See also: - * ECMA-262 v5, 12.6.4 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_for_in (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *int_data_p) /**< interpreter context */ -{ - const vm_idx_t expr_idx = instr.data.for_in.expr; - const vm_idx_t block_end_oc_idx_1 = instr.data.for_in.oc_idx_1; - const vm_idx_t block_end_oc_idx_2 = instr.data.for_in.oc_idx_2; - const vm_instr_counter_t for_in_end_oc = (vm_instr_counter_t) ( - vm_calc_instr_counter_from_idx_idx (block_end_oc_idx_1, - block_end_oc_idx_2) + int_data_p->pos); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - /* 1., 2. */ - ECMA_TRY_CATCH (expr_value, - get_variable_value (int_data_p, - expr_idx, - false), - ret_value); - - int_data_p->pos++; - - vm_instr_t meta_instr = vm_get_instr (int_data_p->bytecode_header_p->instrs_p, for_in_end_oc); - JERRY_ASSERT (meta_instr.op_idx == VM_OP_META); - JERRY_ASSERT (meta_instr.data.meta.type == OPCODE_META_TYPE_END_FOR_IN); - - /* 3. */ - if (!ecma_is_value_undefined (expr_value) - && !ecma_is_value_null (expr_value)) - { - /* 4. */ - ECMA_TRY_CATCH (obj_expr_value, - ecma_op_to_object (expr_value), - ret_value); - - ecma_object_t *obj_p = ecma_get_object_from_value (obj_expr_value); - - ecma_collection_iterator_t names_iterator; - ecma_collection_header_t *names_p = ecma_op_object_get_property_names (obj_p, false, true, true); - - if (names_p != NULL) - { - ecma_collection_iterator_init (&names_iterator, names_p); - - const vm_instr_counter_t for_in_body_begin_oc = int_data_p->pos; - const vm_instr_counter_t for_in_body_end_oc = for_in_end_oc; - - while (ecma_collection_iterator_next (&names_iterator)) - { - ecma_value_t name_value = *names_iterator.current_value_p; - - ecma_string_t *name_p = ecma_get_string_from_value (name_value); - - if (ecma_op_object_get_property (obj_p, name_p) != NULL) - { - ecma_completion_value_t completion = set_variable_value (int_data_p, - int_data_p->pos, - VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME, - name_value); - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - - vm_run_scope_t run_scope_for_in = { for_in_body_begin_oc, for_in_body_end_oc }; - - ecma_completion_value_t for_in_body_completion = vm_loop (int_data_p, &run_scope_for_in); - if (ecma_is_completion_value_empty (for_in_body_completion)) - { - JERRY_ASSERT (int_data_p->pos == for_in_body_end_oc); - - int_data_p->pos = for_in_body_begin_oc; - } - else - { - JERRY_ASSERT (ecma_is_completion_value_throw (for_in_body_completion) - || ecma_is_completion_value_return (for_in_body_completion) - || ecma_is_completion_value_jump (for_in_body_completion)); - JERRY_ASSERT (int_data_p->pos <= for_in_body_end_oc); - - ret_value = for_in_body_completion; - break; - } - } - } - - ecma_free_values_collection (names_p, true); - } - - ECMA_FINALIZE (obj_expr_value); - } - - int_data_p->pos = (vm_instr_counter_t) (for_in_end_oc + 1u); - - ECMA_FINALIZE (expr_value); - - return ret_value; -} /* opfunc_for_in */ - diff --git a/jerry-core/vm/opcodes-helpers-variables.cpp b/jerry-core/vm/opcodes-helpers-variables.cpp deleted file mode 100644 index e83f4d9903..0000000000 --- a/jerry-core/vm/opcodes-helpers-variables.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bytecode-data.h" -#include "opcodes-ecma-support.h" - -#ifndef JERRY_NDEBUG -/** - * Perform so-called 'strict eval or arguments reference' check - * that is used in definition of several statement handling algorithms, - * but has no ECMA-defined name. - */ -static void -do_strict_eval_arguments_check (ecma_object_t *ref_base_lex_env_p, /**< base of ECMA-reference - (lexical environment) */ - ecma_string_t *var_name_string_p, /**< variable name */ - bool is_strict) /**< flag indicating strict mode */ -{ - bool is_check_failed = false; - - if (is_strict) - { - if (ref_base_lex_env_p != NULL) - { - JERRY_ASSERT (ecma_is_lexical_environment (ref_base_lex_env_p)); - - ecma_string_t* magic_string_eval = ecma_get_magic_string (LIT_MAGIC_STRING_EVAL); - ecma_string_t* magic_string_arguments = ecma_get_magic_string (LIT_MAGIC_STRING_ARGUMENTS); - - is_check_failed = (ecma_compare_ecma_strings (var_name_string_p, - magic_string_eval) - || ecma_compare_ecma_strings (var_name_string_p, - magic_string_arguments)); - - ecma_deref_ecma_string (magic_string_eval); - ecma_deref_ecma_string (magic_string_arguments); - } - } - - JERRY_ASSERT (!is_check_failed); -} /* do_strict_eval_arguments_check */ -#endif /* !JERRY_NDEBUG */ - -/** - * Check if the variable is register variable. - * - * @return true - if var_idx is a register variable, - * false - otherwise. - */ -bool -vm_is_reg_variable (vm_idx_t var_idx) /**< variable identifier */ -{ - return (var_idx >= VM_REG_FIRST && var_idx <= VM_REG_LAST); -} /* vm_is_reg_variable */ - -/** - * Get variable's value. - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -get_variable_value (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - vm_idx_t var_idx, /**< variable identifier */ - bool do_eval_or_arguments_check) /** run 'strict eval or arguments reference' check - See also: do_strict_eval_arguments_check */ -{ - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - if (vm_is_reg_variable (var_idx)) - { - ecma_value_t reg_value = vm_stack_frame_get_reg_value (&frame_ctx_p->stack_frame, var_idx); - - JERRY_ASSERT (!ecma_is_value_empty (reg_value)); - - ret_value = ecma_make_normal_completion_value (ecma_copy_value (reg_value, true)); - } - else - { - ecma_string_t var_name_string; - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (var_idx, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); - ecma_new_ecma_string_on_stack_from_lit_cp (&var_name_string, lit_cp); - - ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, - &var_name_string); - - if (do_eval_or_arguments_check) - { -#ifndef JERRY_NDEBUG - do_strict_eval_arguments_check (ref_base_lex_env_p, - &var_name_string, - frame_ctx_p->is_strict); -#endif /* !JERRY_NDEBUG */ - } - - ret_value = ecma_op_get_value_lex_env_base (ref_base_lex_env_p, - &var_name_string, - frame_ctx_p->is_strict); - - ecma_check_that_ecma_string_need_not_be_freed (&var_name_string); - } - - return ret_value; -} /* get_variable_value */ - -/** - * Set variable's value. - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -set_variable_value (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - vm_instr_counter_t lit_oc, /**< instruction counter for literal */ - vm_idx_t var_idx, /**< variable identifier */ - ecma_value_t value) /**< value to set */ -{ - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - if (vm_is_reg_variable (var_idx)) - { - ret_value = ecma_make_empty_completion_value (); - - ecma_value_t reg_value = vm_stack_frame_get_reg_value (&frame_ctx_p->stack_frame, var_idx); - - if (ecma_is_value_number (reg_value) - && ecma_is_value_number (value)) - { - *ecma_get_number_from_value (reg_value) = *ecma_get_number_from_value (value); - } - else - { - if (!ecma_is_value_empty (reg_value)) - { - ecma_free_value (reg_value, false); - } - - vm_stack_frame_set_reg_value (&frame_ctx_p->stack_frame, var_idx, ecma_copy_value (value, false)); - } - } - else - { - ecma_string_t var_name_string; - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (var_idx, frame_ctx_p->bytecode_header_p, lit_oc); - JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); - ecma_new_ecma_string_on_stack_from_lit_cp (&var_name_string, lit_cp); - - ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, - &var_name_string); - -#ifndef JERRY_NDEBUG - do_strict_eval_arguments_check (ref_base_lex_env_p, - &var_name_string, - frame_ctx_p->is_strict); -#endif /* !JERRY_NDEBUG */ - - ret_value = ecma_op_put_value_lex_env_base (ref_base_lex_env_p, - &var_name_string, - frame_ctx_p->is_strict, - value); - - ecma_check_that_ecma_string_need_not_be_freed (&var_name_string); - } - - return ret_value; -} /* set_variable_value */ diff --git a/jerry-core/vm/opcodes-varg.cpp b/jerry-core/vm/opcodes-varg.cpp deleted file mode 100644 index b57dca0777..0000000000 --- a/jerry-core/vm/opcodes-varg.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "bytecode-data.h" -#include "jrt.h" -#include "opcodes.h" -#include "opcodes-ecma-support.h" -#include "vm.h" - -/** - * Fill arguments' list - * - * @return empty completion value if argument list was filled successfully, - * otherwise - not normal completion value indicating completion type - * of last expression evaluated - */ -ecma_completion_value_t -vm_fill_varg_list (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - ecma_length_t args_number, /**< number of arguments */ - ecma_collection_header_t *arg_collection_p) /** collection to fill with argument values */ -{ - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ecma_length_t arg_index; - for (arg_index = 0; - arg_index < args_number && ecma_is_completion_value_empty (ret_value); - arg_index++) - { - ECMA_TRY_CATCH (evaluate_arg, - vm_loop (frame_ctx_p, NULL), - ret_value); - - vm_instr_t next_instr = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, frame_ctx_p->pos); - JERRY_ASSERT (next_instr.op_idx == VM_OP_META); - JERRY_ASSERT (next_instr.data.meta.type == OPCODE_META_TYPE_VARG); - - const vm_idx_t varg_var_idx = next_instr.data.meta.data_1; - - ECMA_TRY_CATCH (get_arg, - get_variable_value (frame_ctx_p, varg_var_idx, false), - ret_value); - - ecma_append_to_values_collection (arg_collection_p, - ecma_get_completion_value_value (get_arg_completion), - true); - - ECMA_FINALIZE (get_arg); - - frame_ctx_p->pos++; - - ECMA_FINALIZE (evaluate_arg); - } - - return ret_value; -} /* vm_fill_varg_list */ - -/** - * Fill parameters' list - */ -vm_instr_counter_t -vm_fill_params_list (const bytecode_data_header_t *bytecode_header_p, /**< header of byte-code */ - vm_instr_counter_t first_instr_pos, /**< position of the first instruction - * with a formal parameter's name */ - ecma_length_t params_number, /**< number of parameters */ - ecma_collection_header_t *formal_params_collection_p) /**< collection to fill with - * parameters' names */ -{ - vm_instr_counter_t instr_pos = first_instr_pos; - - uint32_t param_index; - for (param_index = 0; - param_index < params_number; - param_index++) - { - vm_instr_t next_instr = vm_get_instr (bytecode_header_p->instrs_p, instr_pos); - JERRY_ASSERT (next_instr.op_idx == VM_OP_META); - JERRY_ASSERT (next_instr.data.meta.type == OPCODE_META_TYPE_VARG); - - const lit_cpointer_t param_name_lit_idx = bc_get_literal_cp_by_uid (next_instr.data.meta.data_1, - bytecode_header_p, - instr_pos); - - ecma_string_t *param_name_str_p = ecma_new_ecma_string_from_lit_cp (param_name_lit_idx); - ecma_value_t param_name_value = ecma_make_string_value (param_name_str_p); - - ecma_append_to_values_collection (formal_params_collection_p, param_name_value, false); - - ecma_deref_ecma_string (param_name_str_p); - - instr_pos++; - } - - JERRY_ASSERT (param_index == params_number); - - return instr_pos; -} /* vm_fill_params_list */ diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index 8285370691..46de24088e 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -1,5 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * Copyright 2015 University of Szeged. +/* 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. @@ -14,1294 +14,135 @@ * limitations under the License. */ -#include "bytecode-data.h" -#include "jrt.h" -#include "jrt-bit-fields.h" +#include "ecma-alloc.h" +#include "ecma-builtins.h" +#include "ecma-conversion.h" +#include "ecma-exceptions.h" +#include "ecma-function-object.h" +#include "ecma-gc.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-lex-env.h" +#include "ecma-objects.h" +#include "ecma-try-catch-macro.h" #include "opcodes.h" -#include "opcodes-ecma-support.h" -#include "rcs-records.h" +#include "vm-defines.h" -/** - * Note: - * The note describes exception handling in opcode handlers that perform operations, - * that can throw exceptions, and do not themself handle the exceptions. - * - * Generally, each opcode handler consists of sequence of operations. - * Some of these operations (exceptionable operations) can throw exceptions and other - cannot. - * - * 1. At the beginning of the handler there should be declared opcode handler's 'return value' variable. - * - * 2. All exceptionable operations except the last should be enclosed in ECMA_TRY_CATCH macro. - * All subsequent operations in the opcode handler should be placed into block between - * the ECMA_TRY_CATCH and corresponding ECMA_FINALIZE. - * - * 3. The last exceptionable's operation result should be assigned directly to opcode handler's - * 'return value' variable without using ECMA_TRY_CATCH macro. - * - * 4. After last ECMA_FINALIZE statement there should be only one operator. - * The operator should return from the opcode handler with it's 'return value'. - * - * 5. No other operations with opcode handler's 'return value' variable should be performed. - * - * Note: - * get_variable_value, taking an idx should be called with instruction counter value, - * corresponding to the instruction, containing the idx argument. - */ - -/** - * 'Assignment' opcode handler. - * - * Note: - * This handler implements case of assignment of a literal's or a variable's - * value to a variable. Assignment to an object's property is not implemented - * by this opcode. - * - * See also: ECMA-262 v5, 11.13.1 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_assignment (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.assignment.var_left; - const opcode_arg_type_operand type_value_right = (opcode_arg_type_operand) instr.data.assignment.type_value_right; - const vm_idx_t src_val_descr = instr.data.assignment.value_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - if (type_value_right == OPCODE_ARG_TYPE_SIMPLE) - { - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - ecma_make_simple_value ((ecma_simple_value_t) src_val_descr)); - } - else if (type_value_right == OPCODE_ARG_TYPE_STRING) - { - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (src_val_descr, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - ecma_string_t *string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - ecma_make_string_value (string_p)); - - ecma_deref_ecma_string (string_p); - } - else if (type_value_right == OPCODE_ARG_TYPE_VARIABLE) - { - ECMA_TRY_CATCH (var_value, - get_variable_value (frame_ctx_p, - src_val_descr, - false), - ret_value); - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - var_value); - - ECMA_FINALIZE (var_value); - } - else if (type_value_right == OPCODE_ARG_TYPE_NUMBER) - { - ecma_number_t *num_p = frame_ctx_p->tmp_num_p; - - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (src_val_descr, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - lit_literal_t lit = lit_get_literal_by_cp (lit_cp); - JERRY_ASSERT (RCS_RECORD_IS_NUMBER (lit)); - - *num_p = lit_number_literal_get_number (lit); - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (num_p)); - } - else if (type_value_right == OPCODE_ARG_TYPE_NUMBER_NEGATE) - { - ecma_number_t *num_p = frame_ctx_p->tmp_num_p; - - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (src_val_descr, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - lit_literal_t lit = lit_get_literal_by_cp (lit_cp); - JERRY_ASSERT (RCS_RECORD_IS_NUMBER (lit)); - - *num_p = lit_number_literal_get_number (lit); - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (num_p)); - } - else if (type_value_right == OPCODE_ARG_TYPE_SMALLINT) - { - ecma_number_t *num_p = frame_ctx_p->tmp_num_p; - - *num_p = src_val_descr; - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (num_p)); - } - else if (type_value_right == OPCODE_ARG_TYPE_REGEXP) - { -#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (src_val_descr, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - ecma_string_t *string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); - - lit_utf8_size_t re_utf8_buffer_size = ecma_string_get_size (string_p); - MEM_DEFINE_LOCAL_ARRAY (re_utf8_buffer_p, re_utf8_buffer_size, lit_utf8_byte_t); - - ssize_t sz = ecma_string_to_utf8_string (string_p, re_utf8_buffer_p, (ssize_t) re_utf8_buffer_size); - JERRY_ASSERT (sz >= 0); - - lit_utf8_byte_t *ch_p = re_utf8_buffer_p; - lit_utf8_byte_t *last_slash_p = NULL; - while (ch_p < re_utf8_buffer_p + re_utf8_buffer_size) - { - if (*ch_p == '/') - { - last_slash_p = ch_p; - } - ch_p++; - } - - JERRY_ASSERT (last_slash_p != NULL); - JERRY_ASSERT ((re_utf8_buffer_p < last_slash_p) && (last_slash_p < ch_p)); - JERRY_ASSERT ((last_slash_p - re_utf8_buffer_p) > 0); - ecma_string_t *pattern_p = ecma_new_ecma_string_from_utf8 (re_utf8_buffer_p, - (lit_utf8_size_t) (last_slash_p - re_utf8_buffer_p)); - ecma_string_t *flags_p = NULL; - - if ((ch_p - last_slash_p) > 1) - { - flags_p = ecma_new_ecma_string_from_utf8 (last_slash_p + 1, - (lit_utf8_size_t) ((ch_p - last_slash_p - 1))); - } - - ECMA_TRY_CATCH (regexp_obj_value, - ecma_op_create_regexp_object (pattern_p, flags_p), - ret_value); - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - regexp_obj_value); - - ECMA_FINALIZE (regexp_obj_value); - - ecma_deref_ecma_string (pattern_p); - if (flags_p != NULL) - { - ecma_deref_ecma_string (flags_p); - } - - MEM_FINALIZE_LOCAL_ARRAY (re_utf8_buffer_p) - ecma_deref_ecma_string (string_p); -#else - JERRY_UNIMPLEMENTED ("Regular Expressions are not supported in compact profile!"); -#endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */ - } - else - { - JERRY_ASSERT (type_value_right == OPCODE_ARG_TYPE_SMALLINT_NEGATE); - ecma_number_t *num_p = frame_ctx_p->tmp_num_p; - - *num_p = ecma_number_negate (src_val_descr); - - ret_value = set_variable_value (frame_ctx_p, - frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (num_p)); - } - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_assignment */ - -/** - * 'Pre increment' opcode handler. - * - * See also: ECMA-262 v5, 11.4.4 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_pre_incr (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.pre_incr.dst; - const vm_idx_t incr_var_idx = instr.data.pre_incr.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - // 1., 2., 3. - ECMA_TRY_CATCH (old_value, get_variable_value (frame_ctx_p, incr_var_idx, true), ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (old_num, old_value, ret_value); - - // 4. - ecma_number_t* new_num_p = frame_ctx_p->tmp_num_p; - - *new_num_p = ecma_number_add (old_num, ECMA_NUMBER_ONE); - - ecma_value_t new_num_value = ecma_make_number_value (new_num_p); - - // 5. - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - incr_var_idx, - new_num_value); - - // assignment of operator result to register variable - ecma_completion_value_t reg_assignment_res = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - new_num_value); - JERRY_ASSERT (ecma_is_completion_value_empty (reg_assignment_res)); - - ECMA_OP_TO_NUMBER_FINALIZE (old_num); - ECMA_FINALIZE (old_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_pre_incr */ - -/** - * 'Pre decrement' opcode handler. - * - * See also: ECMA-262 v5, 11.4.4 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_pre_decr (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.pre_decr.dst; - const vm_idx_t decr_var_idx = instr.data.pre_decr.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - // 1., 2., 3. - ECMA_TRY_CATCH (old_value, get_variable_value (frame_ctx_p, decr_var_idx, true), ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (old_num, old_value, ret_value); - - // 4. - ecma_number_t* new_num_p = frame_ctx_p->tmp_num_p; - - *new_num_p = ecma_number_substract (old_num, ECMA_NUMBER_ONE); - - ecma_value_t new_num_value = ecma_make_number_value (new_num_p); - - // 5. - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - decr_var_idx, - new_num_value); - - // assignment of operator result to register variable - ecma_completion_value_t reg_assignment_res = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - new_num_value); - JERRY_ASSERT (ecma_is_completion_value_empty (reg_assignment_res)); - - ECMA_OP_TO_NUMBER_FINALIZE (old_num); - ECMA_FINALIZE (old_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_pre_decr */ - -/** - * 'Post increment' opcode handler. - * - * See also: ECMA-262 v5, 11.3.1 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_post_incr (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.post_incr.dst; - const vm_idx_t incr_var_idx = instr.data.post_incr.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - // 1., 2., 3. - ECMA_TRY_CATCH (old_value, get_variable_value (frame_ctx_p, incr_var_idx, true), ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (old_num, old_value, ret_value); - - // 4. - ecma_number_t* new_num_p = frame_ctx_p->tmp_num_p; - - *new_num_p = ecma_number_add (old_num, ECMA_NUMBER_ONE); - - // 5. - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - incr_var_idx, - ecma_make_number_value (new_num_p)); - - ecma_number_t *tmp_p = frame_ctx_p->tmp_num_p; - *tmp_p = old_num; - - // assignment of operator result to register variable - ecma_completion_value_t reg_assignment_res = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (tmp_p)); - JERRY_ASSERT (ecma_is_completion_value_empty (reg_assignment_res)); - - ECMA_OP_TO_NUMBER_FINALIZE (old_num); - ECMA_FINALIZE (old_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_post_incr */ - -/** - * 'Post decrement' opcode handler. - * - * See also: ECMA-262 v5, 11.3.2 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_post_decr (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t dst_var_idx = instr.data.post_decr.dst; - const vm_idx_t decr_var_idx = instr.data.post_decr.var_right; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - // 1., 2., 3. - ECMA_TRY_CATCH (old_value, get_variable_value (frame_ctx_p, decr_var_idx, true), ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (old_num, old_value, ret_value); - - // 4. - ecma_number_t* new_num_p = frame_ctx_p->tmp_num_p; - - *new_num_p = ecma_number_substract (old_num, ECMA_NUMBER_ONE); - - // 5. - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - decr_var_idx, - ecma_make_number_value (new_num_p)); - - ecma_number_t *tmp_p = frame_ctx_p->tmp_num_p; - *tmp_p = old_num; - - // assignment of operator result to register variable - ecma_completion_value_t reg_assignment_res = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_number_value (tmp_p)); - JERRY_ASSERT (ecma_is_completion_value_empty (reg_assignment_res)); - - ECMA_OP_TO_NUMBER_FINALIZE (old_num); - ECMA_FINALIZE (old_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_post_decr */ - -/** - * 'Register variable declaration' opcode handler. - * - * The opcode is meta-opcode that is not supposed to be executed. - */ -ecma_completion_value_t -opfunc_reg_var_decl (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ -{ - JERRY_UNREACHABLE (); -} /* opfunc_reg_var_decl */ - -/** - * 'Variable declaration' opcode handler. - * - * See also: ECMA-262 v5, 10.5 - Declaration binding instantiation (block 8). - * - * @return completion value - * Returned value is simple and so need not be freed. - * However, ecma_free_completion_value may be called for it, but it is a no-op. - */ -ecma_completion_value_t -opfunc_var_decl (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ -{ - JERRY_UNREACHABLE (); -} /* opfunc_var_decl */ - -/** - * Function declaration helper - * - * @return completion value - * returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -vm_function_declaration (const bytecode_data_header_t *bytecode_header_p, /**< byte-code header */ - bool is_strict, /**< is function declared in strict mode code? */ - bool is_eval_code, /**< is function declared in eval code? */ - ecma_object_t *lex_env_p) /**< lexical environment to use */ -{ - vm_instr_t *function_instrs_p = bytecode_header_p->instrs_p; - - vm_instr_counter_t instr_pos = 0; - - vm_instr_t func_header_instr = function_instrs_p[instr_pos]; - - bool is_func_decl; - vm_idx_t function_name_idx; - ecma_length_t params_number; - - if (func_header_instr.op_idx == VM_OP_FUNC_DECL_N) - { - is_func_decl = true; - - function_name_idx = func_header_instr.data.func_decl_n.name_lit_idx; - params_number = func_header_instr.data.func_decl_n.arg_list; - } - else - { - is_func_decl = false; - - function_name_idx = func_header_instr.data.func_expr_n.name_lit_idx; - params_number = func_header_instr.data.func_expr_n.arg_list; - } - - lit_cpointer_t function_name_lit_cp = NOT_A_LITERAL; - - if (function_name_idx != VM_IDX_EMPTY) - { - function_name_lit_cp = bc_get_literal_cp_by_uid (function_name_idx, - bytecode_header_p, - instr_pos); - } - else - { - JERRY_ASSERT (!is_func_decl); - } - - instr_pos++; - - ecma_completion_value_t ret_value; - - ecma_collection_header_t *formal_params_collection_p = NULL; - - if (bytecode_header_p->is_args_moved_to_regs) - { - instr_pos = (vm_instr_counter_t) (instr_pos + params_number); - } - else if (params_number != 0) - { - formal_params_collection_p = ecma_new_strings_collection (NULL, 0); - - instr_pos = vm_fill_params_list (bytecode_header_p, - instr_pos, - params_number, - formal_params_collection_p); - } - - if (is_func_decl) - { - const bool is_configurable_bindings = is_eval_code; - - ecma_string_t *function_name_string_p = ecma_new_ecma_string_from_lit_cp (function_name_lit_cp); - - ret_value = ecma_op_function_declaration (lex_env_p, - function_name_string_p, - bytecode_header_p, - instr_pos, - formal_params_collection_p, - is_strict, - is_configurable_bindings); - - ecma_deref_ecma_string (function_name_string_p); - } - else - { - ecma_object_t *scope_p; - ecma_string_t *function_name_string_p = NULL; - - bool is_named_func_expr = (function_name_idx != VM_IDX_EMPTY); - - if (is_named_func_expr) - { - scope_p = ecma_create_decl_lex_env (lex_env_p); - - JERRY_ASSERT (function_name_lit_cp.packed_value != MEM_CP_NULL); - - function_name_string_p = ecma_new_ecma_string_from_lit_cp (function_name_lit_cp); - ecma_op_create_immutable_binding (scope_p, function_name_string_p); - } - else - { - scope_p = lex_env_p; - ecma_ref_object (scope_p); - } - - ecma_object_t *func_obj_p = ecma_op_create_function_object (formal_params_collection_p, - scope_p, - is_strict, - bytecode_header_p, - instr_pos); - - ret_value = ecma_make_normal_completion_value (ecma_make_object_value (func_obj_p)); - - if (is_named_func_expr) - { - ecma_op_initialize_immutable_binding (scope_p, - function_name_string_p, - ecma_make_object_value (func_obj_p)); - ecma_deref_ecma_string (function_name_string_p); - } - - ecma_deref_object (scope_p); - } - - return ret_value; -} /* vm_function_declaration */ - -/** - * 'Function declaration header' opcode handler. - * - * @return completion value - * returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -opfunc_func_decl_n (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ -{ - JERRY_UNREACHABLE (); -} /* opfunc_func_decl_n */ - -/** - * 'Function expression header' opcode handler. - * - * @return completion value - * returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -opfunc_func_expr_n (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ -{ - JERRY_UNREACHABLE (); -} /* opfunc_func_expr_n */ - -/** - * 'Function expression' opcode handler. - * - * @return completion value - * returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -opfunc_func_expr_ref (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_instr_counter_t lit_oc = frame_ctx_p->pos; - - frame_ctx_p->pos++; - - const vm_idx_t dst_var_idx = instr.data.func_expr_ref.lhs; - const vm_idx_t idx1 = instr.data.func_expr_ref.idx1; - const vm_idx_t idx2 = instr.data.func_expr_ref.idx2; - - uint16_t index = (uint16_t) jrt_set_bit_field_value (jrt_set_bit_field_value (0, idx1, 0, JERRY_BITSINBYTE), - idx2, JERRY_BITSINBYTE, JERRY_BITSINBYTE); - - const bytecode_data_header_t *header_p = frame_ctx_p->bytecode_header_p; - mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, header_p->declarations_cp); - JERRY_ASSERT (index < header_p->func_scopes_count); - - const bytecode_data_header_t *func_expr_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (const bytecode_data_header_t, - declarations_p[index]); - - JERRY_ASSERT (func_expr_bc_header_p->instrs_p[0].op_idx == VM_OP_FUNC_EXPR_N); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (func_obj_value, - vm_function_declaration (func_expr_bc_header_p, - frame_ctx_p->is_strict, - frame_ctx_p->is_eval_code, - frame_ctx_p->lex_env_p), - ret_value); - - JERRY_ASSERT (ecma_is_constructor (func_obj_value)); - - ret_value = set_variable_value (frame_ctx_p, lit_oc, dst_var_idx, func_obj_value); - - ECMA_FINALIZE (func_obj_value); - - return ret_value; -} /* opfunc_func_expr_ref */ - -/** - * Get 'this' argument value and call flags mask for function call - * - * See also: - * ECMA-262 v5, 11.2.3, steps 6-7 - - * @return ecma-value - * Returned value must be freed with ecma_free_value - */ -static ecma_value_t -vm_helper_call_get_call_flags_and_this_arg (vm_frame_ctx_t *int_data_p, /**< interpreter context */ - vm_instr_counter_t var_idx_lit_oc, /**< instruction counter of instruction - with var_idx */ - vm_idx_t var_idx, /**< idx, used to retrieve the called function object */ - opcode_call_flags_t *out_flags_p) /**< out: call flags */ -{ - JERRY_ASSERT (out_flags_p != NULL); - - bool is_increase_instruction_pointer; - - opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; - vm_idx_t this_arg_var_idx = VM_IDX_EMPTY; - - vm_instr_t next_opcode = vm_get_instr (int_data_p->bytecode_header_p->instrs_p, int_data_p->pos); - if (next_opcode.op_idx == VM_OP_META - && next_opcode.data.meta.type == OPCODE_META_TYPE_CALL_SITE_INFO) - { - call_flags = (opcode_call_flags_t) next_opcode.data.meta.data_1; - - if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - this_arg_var_idx = next_opcode.data.meta.data_2; - JERRY_ASSERT (vm_is_reg_variable (this_arg_var_idx)); - - JERRY_ASSERT ((call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) == 0); - } - - is_increase_instruction_pointer = true; - } - else - { - is_increase_instruction_pointer = false; - } - - ecma_completion_value_t get_this_completion_value; - ecma_value_t this_value; - - if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - /* 6.a.i */ - get_this_completion_value = get_variable_value (int_data_p, this_arg_var_idx, false); - } - else - { - /* - * Note: - * If function object is in a register variable, then base is not lexical environment - * - * See also: - * parse_argument_list - */ - if (!vm_is_reg_variable (var_idx)) - { - /* - * FIXME [PERF]: - * The second search for base lexical environment can be removed or performed less frequently. - * - * For example, in one of the following ways: - * - parser could dump meta information about whether current lexical environment chain - * can contain object-bound lexical environment, created using 'with' statement - * (in the case, the search should be performed, otherwise - it is unnecessary, - * as base lexical environment would be either the Global lexical environment, - * or a declarative lexical environment); - * - get_variable_value and opfunc_prop_getter could save last resolved base - * into interpreter context (in the case, `have_this_arg` can be unnecesary, - * as base would always be accessible through interpreter context; on the other hand, - * some stack-like description of active nested call expressions should be provided). - * - */ - - /* 6.b.i */ - ecma_string_t var_name_string; - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (var_idx, - int_data_p->bytecode_header_p, - var_idx_lit_oc); - ecma_new_ecma_string_on_stack_from_lit_cp (&var_name_string, lit_cp); - - ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (int_data_p->lex_env_p, - &var_name_string); - get_this_completion_value = ecma_op_implicit_this_value (ref_base_lex_env_p); - JERRY_ASSERT (ref_base_lex_env_p != NULL); - - ecma_check_that_ecma_string_need_not_be_freed (&var_name_string); - } - else - { - /* 7. a */ - get_this_completion_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED); - } - } - JERRY_ASSERT (ecma_is_completion_value_normal (get_this_completion_value)); - - this_value = ecma_get_completion_value_value (get_this_completion_value); - - if (is_increase_instruction_pointer) - { - int_data_p->pos++; - } - - *out_flags_p = call_flags; - - return this_value; -} /* vm_helper_call_get_call_flags_and_this_arg */ - -/** - * 'Function call' opcode handler. - * - * See also: ECMA-262 v5, 11.2.3 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -opfunc_call_n (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t lhs_var_idx = instr.data.call_n.lhs; - const vm_idx_t function_var_idx = instr.data.call_n.function_var_idx; - const vm_idx_t args_number_idx = instr.data.call_n.arg_list; - const vm_instr_counter_t lit_oc = frame_ctx_p->pos; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (func_value, get_variable_value (frame_ctx_p, function_var_idx, false), ret_value); - - frame_ctx_p->pos++; - - JERRY_ASSERT (!frame_ctx_p->is_call_in_direct_eval_form); - - opcode_call_flags_t call_flags; - ecma_value_t this_value = vm_helper_call_get_call_flags_and_this_arg (frame_ctx_p, - lit_oc, - function_var_idx, - &call_flags); - - ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (NULL, 0, true); - - ecma_completion_value_t get_arg_completion = vm_fill_varg_list (frame_ctx_p, - args_number_idx, - arg_collection_p); - ecma_length_t args_read = arg_collection_p->unit_number; - - if (ecma_is_completion_value_empty (get_arg_completion)) - { - JERRY_ASSERT (args_read == args_number_idx); - - if (!ecma_op_is_callable (func_value)) - { - ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); - } - else - { - if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) - { - frame_ctx_p->is_call_in_direct_eval_form = true; - } - - ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value); - - ECMA_TRY_CATCH (call_ret_value, - ecma_op_function_call (func_obj_p, - this_value, - arg_collection_p), - ret_value); - - ret_value = set_variable_value (frame_ctx_p, lit_oc, - lhs_var_idx, - call_ret_value); - - ECMA_FINALIZE (call_ret_value); - - if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) - { - JERRY_ASSERT (frame_ctx_p->is_call_in_direct_eval_form); - frame_ctx_p->is_call_in_direct_eval_form = false; - } - else - { - JERRY_ASSERT (!frame_ctx_p->is_call_in_direct_eval_form); - } - } - } - else - { - JERRY_ASSERT (!ecma_is_completion_value_normal (get_arg_completion)); - - ret_value = get_arg_completion; - } - - ecma_free_values_collection (arg_collection_p, true); - ecma_free_value (this_value, true); - - ECMA_FINALIZE (func_value); - - return ret_value; -} /* opfunc_call_n */ - -/** - * 'Constructor call' opcode handler. - * - * See also: ECMA-262 v5, 11.2.2 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -opfunc_construct_n (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t lhs_var_idx = instr.data.construct_n.lhs; - const vm_idx_t constructor_name_lit_idx = instr.data.construct_n.name_lit_idx; - const vm_idx_t args_number = instr.data.construct_n.arg_list; - const vm_instr_counter_t lit_oc = frame_ctx_p->pos; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (constructor_value, - get_variable_value (frame_ctx_p, constructor_name_lit_idx, false), - ret_value); - - frame_ctx_p->pos++; - - ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (NULL, 0, true); - - ecma_completion_value_t get_arg_completion = vm_fill_varg_list (frame_ctx_p, - args_number, - arg_collection_p); - ecma_length_t args_read = arg_collection_p->unit_number; - - if (ecma_is_completion_value_empty (get_arg_completion)) - { - JERRY_ASSERT (args_read == args_number); - - if (!ecma_is_constructor (constructor_value)) - { - ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); - } - else - { - ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor_value); - - ECMA_TRY_CATCH (construction_ret_value, - ecma_op_function_construct (constructor_obj_p, - arg_collection_p), - ret_value); - - ret_value = set_variable_value (frame_ctx_p, lit_oc, lhs_var_idx, - construction_ret_value); - - ECMA_FINALIZE (construction_ret_value); - } - } - else - { - JERRY_ASSERT (!ecma_is_completion_value_normal (get_arg_completion)); - - ret_value = get_arg_completion; - } - - ecma_free_values_collection (arg_collection_p, true); - - ECMA_FINALIZE (constructor_value); - - return ret_value; -} /* opfunc_construct_n */ - -/** - * 'Array initializer' opcode handler. - * - * See also: ECMA-262 v5, 11.1.4 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value. - */ -ecma_completion_value_t -opfunc_array_decl (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t lhs_var_idx = instr.data.array_decl.lhs; - const vm_idx_t args_number_high_byte = instr.data.array_decl.list_1; - const vm_idx_t args_number_low_byte = instr.data.array_decl.list_2; - const vm_instr_counter_t lit_oc = frame_ctx_p->pos; - - frame_ctx_p->pos++; - - ecma_length_t args_number = (((ecma_length_t) args_number_high_byte << JERRY_BITSINBYTE) - + (ecma_length_t) args_number_low_byte); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (NULL, 0, true); - - ecma_completion_value_t get_arg_completion = vm_fill_varg_list (frame_ctx_p, - args_number, - arg_collection_p); - ecma_length_t args_read = arg_collection_p->unit_number; - - if (ecma_is_completion_value_empty (get_arg_completion)) - { - JERRY_ASSERT (args_read == args_number); - - MEM_DEFINE_LOCAL_ARRAY (arg_values, args_read, ecma_value_t); - - ecma_collection_iterator_t arg_collection_iter; - ecma_collection_iterator_init (&arg_collection_iter, - arg_collection_p); - - for (ecma_length_t arg_index = 0; - ecma_collection_iterator_next (&arg_collection_iter); - arg_index++) - { - arg_values[arg_index] = *arg_collection_iter.current_value_p; - } - - ECMA_TRY_CATCH (array_obj_value, - ecma_op_create_array_object (arg_values, - args_number, - false), - ret_value); - - ret_value = set_variable_value (frame_ctx_p, lit_oc, - lhs_var_idx, - array_obj_value); - - ECMA_FINALIZE (array_obj_value); - - MEM_FINALIZE_LOCAL_ARRAY (arg_values); - } - else - { - JERRY_ASSERT (!ecma_is_completion_value_normal (get_arg_completion)); - - ret_value = get_arg_completion; - } - - ecma_free_values_collection (arg_collection_p, true); - - return ret_value; -} /* opfunc_array_decl */ +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_opcodes Opcodes + * @{ + */ /** - * 'Object initializer' opcode handler. + * 'Function call' opcode handler. * - * See also: ECMA-262 v5, 11.1.5 + * See also: ECMA-262 v5, 11.2.3 * * @return completion value * Returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t -opfunc_obj_decl (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_call_n (ecma_value_t this_value, /**< this object value */ + ecma_value_t func_value, /**< function object value */ + const ecma_value_t *arguments_list_p, /**< stack pointer */ + ecma_length_t arguments_list_len) /**< number of arguments */ { - const vm_idx_t lhs_var_idx = instr.data.obj_decl.lhs; - const vm_idx_t args_number_high_byte = instr.data.obj_decl.list_1; - const vm_idx_t args_number_low_byte = instr.data.obj_decl.list_2; - const vm_instr_counter_t obj_lit_oc = frame_ctx_p->pos; - - frame_ctx_p->pos++; - - ecma_length_t args_number = (((ecma_length_t) args_number_high_byte << JERRY_BITSINBYTE) - + (ecma_length_t) args_number_low_byte); - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ecma_object_t *obj_p = ecma_op_create_object_object_noarg (); - - for (uint32_t prop_index = 0; - prop_index < args_number && ecma_is_completion_value_empty (ret_value); - prop_index++) - { - ecma_completion_value_t evaluate_prop_completion = vm_loop (frame_ctx_p, NULL); - - if (ecma_is_completion_value_empty (evaluate_prop_completion)) - { - vm_instr_t next_opcode = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, frame_ctx_p->pos); - JERRY_ASSERT (next_opcode.op_idx == VM_OP_META); - - const opcode_meta_type type = (opcode_meta_type) next_opcode.data.meta.type; - JERRY_ASSERT (type == OPCODE_META_TYPE_VARG_PROP_DATA - || type == OPCODE_META_TYPE_VARG_PROP_GETTER - || type == OPCODE_META_TYPE_VARG_PROP_SETTER); - - const vm_idx_t prop_name_idx = next_opcode.data.meta.data_1; - - const vm_idx_t value_for_prop_desc_var_idx = next_opcode.data.meta.data_2; - - ECMA_TRY_CATCH (value_for_prop_desc, - get_variable_value (frame_ctx_p, - value_for_prop_desc_var_idx, - false), - ret_value); - - lit_cpointer_t prop_name_lit_cp = bc_get_literal_cp_by_uid (prop_name_idx, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - JERRY_ASSERT (prop_name_lit_cp.packed_value != MEM_CP_NULL); - ecma_string_t *prop_name_string_p = ecma_new_ecma_string_from_lit_cp (prop_name_lit_cp); - - bool is_throw_syntax_error = false; - - ecma_property_t *previous_p = ecma_op_object_get_own_property (obj_p, prop_name_string_p); - - const bool is_previous_undefined = (previous_p == NULL); - const bool is_previous_data_desc = (!is_previous_undefined - && previous_p->type == ECMA_PROPERTY_NAMEDDATA); - const bool is_previous_accessor_desc = (!is_previous_undefined - && previous_p->type == ECMA_PROPERTY_NAMEDACCESSOR); - JERRY_ASSERT (is_previous_undefined || is_previous_data_desc || is_previous_accessor_desc); - - ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); - { - prop_desc.is_enumerable_defined = true; - prop_desc.is_enumerable = true; - - prop_desc.is_configurable_defined = true; - prop_desc.is_configurable = true; - } - - if (type == OPCODE_META_TYPE_VARG_PROP_DATA) - { - prop_desc.is_value_defined = true; - prop_desc.value = value_for_prop_desc; - - prop_desc.is_writable_defined = true; - prop_desc.is_writable = true; - - if (!is_previous_undefined - && ((is_previous_data_desc - && frame_ctx_p->is_strict) - || is_previous_accessor_desc)) - { - is_throw_syntax_error = true; - } - } - else if (type == OPCODE_META_TYPE_VARG_PROP_GETTER) - { - prop_desc.is_get_defined = true; - prop_desc.get_p = ecma_get_object_from_value (value_for_prop_desc); - - if (!is_previous_undefined - && is_previous_data_desc) - { - is_throw_syntax_error = true; - } - } - else - { - prop_desc.is_set_defined = true; - prop_desc.set_p = ecma_get_object_from_value (value_for_prop_desc); - - if (!is_previous_undefined - && is_previous_data_desc) - { - is_throw_syntax_error = true; - } - } - - /* The SyntaxError should be treated as an early error */ - JERRY_ASSERT (!is_throw_syntax_error); - - ecma_completion_value_t define_prop_completion = ecma_op_object_define_own_property (obj_p, - prop_name_string_p, - &prop_desc, - false); - JERRY_ASSERT (ecma_is_completion_value_normal_true (define_prop_completion) - || ecma_is_completion_value_normal_false (define_prop_completion)); - - ecma_deref_ecma_string (prop_name_string_p); - - ECMA_FINALIZE (value_for_prop_desc); - - frame_ctx_p->pos++; - } - else - { - JERRY_ASSERT (ecma_is_completion_value_throw (evaluate_prop_completion)); - - ret_value = evaluate_prop_completion; - } - } - - if (ecma_is_completion_value_empty (ret_value)) - { - ret_value = set_variable_value (frame_ctx_p, obj_lit_oc, lhs_var_idx, ecma_make_object_value (obj_p)); - } - else + if (!ecma_op_is_callable (func_value)) { - JERRY_ASSERT (ecma_is_completion_value_throw (ret_value)); + return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); } - ecma_deref_object (obj_p); - - return ret_value; -} /* opfunc_obj_decl */ -/** - * 'Return with no expression' opcode handler. - * - * See also: ECMA-262 v5, 12.9 - * - * @return completion value - * Returned value is simple and so need not be freed. - * However, ecma_free_completion_value may be called for it, but it is a no-op. - */ -ecma_completion_value_t -opfunc_ret (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ -{ - return ecma_make_return_completion_value (ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); -} /* opfunc_ret */ - -/** - * 'Return with expression' opcode handler. - * - * See also: ECMA-262 v5, 12.9 - * - * @return completion value - * Returned value is simple and so need not be freed. - * However, ecma_free_completion_value may be called for it, but it is a no-op. - */ -ecma_completion_value_t -opfunc_retval (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ -{ - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (expr_val, get_variable_value (frame_ctx_p, instr.data.retval.ret_value, false), ret_value); - - ret_value = ecma_make_return_completion_value (ecma_copy_value (expr_val, true)); + ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value); - ECMA_FINALIZE (expr_val); + ret_value = ecma_op_function_call_array_args (func_obj_p, + this_value, + arguments_list_p, + arguments_list_len); return ret_value; -} /* opfunc_retval */ +} /* opfunc_call_n */ /** - * 'Property getter' opcode handler. + * 'Constructor call' opcode handler. * - * See also: ECMA-262 v5, 11.2.1 - * ECMA-262 v5, 11.13.1 + * See also: ECMA-262 v5, 11.2.2 * * @return completion value - * returned value must be freed with ecma_free_completion_value. + * Returned value must be freed with ecma_free_completion_value. */ ecma_completion_value_t -opfunc_prop_getter (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ +opfunc_construct_n (ecma_value_t constructor_value, /**< constructor object value */ + uint8_t args_num, /**< number of arguments */ + ecma_value_t *stack_p) /**< stack pointer */ { - const vm_idx_t lhs_var_idx = instr.data.prop_getter.lhs; - const vm_idx_t base_var_idx = instr.data.prop_getter.obj; - const vm_idx_t prop_name_var_idx = instr.data.prop_getter.prop; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (NULL, 0, true); - ECMA_TRY_CATCH (base_value, - get_variable_value (frame_ctx_p, base_var_idx, false), - ret_value); - ECMA_TRY_CATCH (prop_name_value, - get_variable_value (frame_ctx_p, prop_name_var_idx, false), - ret_value); - ECMA_TRY_CATCH (check_coercible_ret, - ecma_op_check_object_coercible (base_value), - ret_value); - ECMA_TRY_CATCH (prop_name_str_value, - ecma_op_to_string (prop_name_value), - ret_value); - - ecma_string_t *prop_name_string_p = ecma_get_string_from_value (prop_name_str_value); - ecma_reference_t ref = ecma_make_reference (base_value, prop_name_string_p, frame_ctx_p->is_strict); - - ECMA_TRY_CATCH (prop_value, ecma_op_get_value_object_base (ref), ret_value); + for (int i = 0; i < args_num; i++) + { + ecma_append_to_values_collection (arg_collection_p, stack_p[i], true); + } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, lhs_var_idx, prop_value); + if (!ecma_is_constructor (constructor_value)) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + else + { + ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor_value); - ECMA_FINALIZE (prop_value); + ECMA_TRY_CATCH (construction_ret_value, + ecma_op_function_construct (constructor_obj_p, + arg_collection_p), + ret_value); - ecma_free_reference (ref); + ret_value = ecma_make_normal_completion_value (ecma_copy_value (construction_ret_value, true)); - ECMA_FINALIZE (prop_name_str_value); - ECMA_FINALIZE (check_coercible_ret); - ECMA_FINALIZE (prop_name_value); - ECMA_FINALIZE (base_value); + ECMA_FINALIZE (construction_ret_value); + } - frame_ctx_p->pos++; + ecma_free_values_collection (arg_collection_p, true); return ret_value; -} /* opfunc_prop_getter */ +} /* opfunc_construct_n */ /** - * 'Property setter' opcode handler. + * 'Variable declaration' opcode handler. * - * See also: ECMA-262 v5, 11.2.1 - * ECMA-262 v5, 11.13.1 + * See also: ECMA-262 v5, 10.5 - Declaration binding instantiation (block 8). * * @return completion value - * returned value must be freed with ecma_free_completion_value. + * Returned value is simple and so need not be freed. + * However, ecma_free_completion_value may be called for it, but it is a no-op. */ ecma_completion_value_t -opfunc_prop_setter (vm_instr_t instr __attr_unused___, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ +vm_var_decl (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ + ecma_string_t *var_name_str_p) /**< variable name */ { - const vm_idx_t base_var_idx = instr.data.prop_setter.obj; - const vm_idx_t prop_name_var_idx = instr.data.prop_setter.prop; - const vm_idx_t rhs_var_idx = instr.data.prop_setter.rhs; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (base_value, - get_variable_value (frame_ctx_p, base_var_idx, false), - ret_value); - ECMA_TRY_CATCH (prop_name_value, - get_variable_value (frame_ctx_p, prop_name_var_idx, false), - ret_value); - ECMA_TRY_CATCH (check_coercible_ret, - ecma_op_check_object_coercible (base_value), - ret_value); - ECMA_TRY_CATCH (prop_name_str_value, - ecma_op_to_string (prop_name_value), - ret_value); - - ecma_string_t *prop_name_string_p = ecma_get_string_from_value (prop_name_str_value); - ecma_reference_t ref = ecma_make_reference (base_value, - prop_name_string_p, - frame_ctx_p->is_strict); - - ECMA_TRY_CATCH (rhs_value, get_variable_value (frame_ctx_p, rhs_var_idx, false), ret_value); - ret_value = ecma_op_put_value_object_base (ref, rhs_value); - ECMA_FINALIZE (rhs_value); - - ecma_free_reference (ref); + if (!ecma_op_has_binding (frame_ctx_p->lex_env_p, var_name_str_p)) + { + const bool is_configurable_bindings = frame_ctx_p->is_eval_code; - ECMA_FINALIZE (prop_name_str_value); - ECMA_FINALIZE (check_coercible_ret); - ECMA_FINALIZE (prop_name_value); - ECMA_FINALIZE (base_value); + ecma_completion_value_t completion = ecma_op_create_mutable_binding (frame_ctx_p->lex_env_p, + var_name_str_p, + is_configurable_bindings); - frame_ctx_p->pos++; + JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - return ret_value; -} /* opfunc_prop_setter */ + /* Skipping SetMutableBinding as we have already checked that there were not + * any binding with specified name in current lexical environment + * and CreateMutableBinding sets the created binding's value to undefined */ + JERRY_ASSERT (ecma_is_completion_value_normal_simple_value (ecma_op_get_binding_value (frame_ctx_p->lex_env_p, + var_name_str_p, + true), + ECMA_SIMPLE_VALUE_UNDEFINED)); + } + return ecma_make_empty_completion_value (); +} /* vm_var_decl */ /** * 'Logical NOT Operator' opcode handler. @@ -1312,190 +153,23 @@ opfunc_prop_setter (vm_instr_t instr __attr_unused___, /**< instruction */ * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_logical_not (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_logical_not (ecma_value_t left_value) /**< left value */ { - const vm_idx_t dst_var_idx = instr.data.logical_not.dst; - const vm_idx_t right_var_idx = instr.data.logical_not.var_right; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (right_value, get_variable_value (frame_ctx_p, right_var_idx, false), ret_value); - ecma_simple_value_t old_value = ECMA_SIMPLE_VALUE_TRUE; - ecma_completion_value_t to_bool_value = ecma_op_to_boolean (right_value); + ecma_completion_value_t to_bool_value = ecma_op_to_boolean (left_value); if (ecma_is_value_true (ecma_get_completion_value_value (to_bool_value))) { old_value = ECMA_SIMPLE_VALUE_FALSE; } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_simple_value (old_value)); - - ECMA_FINALIZE (right_value); - - frame_ctx_p->pos++; + ret_value = ecma_make_simple_completion_value (old_value); return ret_value; } /* opfunc_logical_not */ -/** - * 'With' opcode handler. - * - * See also: ECMA-262 v5, 12.10 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_with (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t expr_var_idx = instr.data.with.expr; - const vm_idx_t block_end_oc_idx_1 = instr.data.with.oc_idx_1; - const vm_idx_t block_end_oc_idx_2 = instr.data.with.oc_idx_2; - const vm_instr_counter_t with_end_oc = (vm_instr_counter_t) ( - vm_calc_instr_counter_from_idx_idx (block_end_oc_idx_1, block_end_oc_idx_2) + frame_ctx_p->pos); - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (expr_value, - get_variable_value (frame_ctx_p, - expr_var_idx, - false), - ret_value); - ECMA_TRY_CATCH (obj_expr_value, - ecma_op_to_object (expr_value), - ret_value); - - frame_ctx_p->pos++; - - ecma_object_t *obj_p = ecma_get_object_from_value (obj_expr_value); - - ecma_object_t *old_env_p = frame_ctx_p->lex_env_p; - ecma_object_t *new_env_p = ecma_create_object_lex_env (old_env_p, - obj_p, - true); - frame_ctx_p->lex_env_p = new_env_p; - -#ifndef JERRY_NDEBUG - vm_instr_t meta_opcode = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, with_end_oc); - JERRY_ASSERT (meta_opcode.op_idx == VM_OP_META); - JERRY_ASSERT (meta_opcode.data.meta.type == OPCODE_META_TYPE_END_WITH); -#endif /* !JERRY_NDEBUG */ - - vm_run_scope_t run_scope_with = { frame_ctx_p->pos, with_end_oc }; - ecma_completion_value_t with_completion = vm_loop (frame_ctx_p, &run_scope_with); - - if (ecma_is_completion_value_empty (with_completion)) - { - JERRY_ASSERT (frame_ctx_p->pos == with_end_oc); - - frame_ctx_p->pos++; - } - else - { - JERRY_ASSERT (ecma_is_completion_value_throw (with_completion) - || ecma_is_completion_value_return (with_completion) - || ecma_is_completion_value_jump (with_completion)); - JERRY_ASSERT (frame_ctx_p->pos <= with_end_oc); - } - - ret_value = with_completion; - - frame_ctx_p->lex_env_p = old_env_p; - - ecma_deref_object (new_env_p); - - ECMA_FINALIZE (obj_expr_value); - ECMA_FINALIZE (expr_value); - - return ret_value; -} /* opfunc_with */ - -/** - * 'Throw' opcode handler. - * - * See also: ECMA-262 v5, 12.13 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -ecma_completion_value_t -opfunc_throw_value (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ -{ - const vm_idx_t var_idx = instr.data.throw_value.var; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - ECMA_TRY_CATCH (var_value, - get_variable_value (frame_ctx_p, - var_idx, - false), - ret_value); - - ret_value = ecma_make_throw_completion_value (ecma_copy_value (var_value, true)); - - ECMA_FINALIZE (var_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_throw_value */ - -/** - * Evaluate argument of typeof. - * - * See also: ECMA-262 v5, 11.4.3 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value - */ -static ecma_completion_value_t -evaluate_arg_for_typeof (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - vm_idx_t var_idx) /**< arg variable identifier */ -{ - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - if (vm_is_reg_variable (var_idx)) - { - // 2.b - ret_value = get_variable_value (frame_ctx_p, - var_idx, - false); - JERRY_ASSERT (ecma_is_completion_value_normal (ret_value)); - } - else - { - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (var_idx, - frame_ctx_p->bytecode_header_p, - frame_ctx_p->pos); - JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); - - ecma_string_t *var_name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); - - ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, - var_name_string_p); - if (ref_base_lex_env_p == NULL) - { - ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED); - } - else - { - ret_value = ecma_op_get_value_lex_env_base (ref_base_lex_env_p, - var_name_string_p, - frame_ctx_p->is_strict); - } - - ecma_deref_ecma_string (var_name_string_p); - } - - return ret_value; -} /* evaluate_arg_for_typeof */ - /** * 'typeof' opcode handler. * @@ -1505,47 +179,37 @@ evaluate_arg_for_typeof (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context * * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_typeof (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +opfunc_typeof (ecma_value_t left_value) /**< left value */ { - const vm_idx_t dst_var_idx = instr.data.typeof.lhs; - const vm_idx_t obj_var_idx = instr.data.typeof.obj; - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (typeof_arg, - evaluate_arg_for_typeof (frame_ctx_p, - obj_var_idx), - ret_value); - - ecma_string_t *type_str_p = NULL; - if (ecma_is_value_undefined (typeof_arg)) + if (ecma_is_value_undefined (left_value)) { type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_UNDEFINED); } - else if (ecma_is_value_null (typeof_arg)) + else if (ecma_is_value_null (left_value)) { type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_OBJECT); } - else if (ecma_is_value_boolean (typeof_arg)) + else if (ecma_is_value_boolean (left_value)) { type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_BOOLEAN); } - else if (ecma_is_value_number (typeof_arg)) + else if (ecma_is_value_number (left_value)) { type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_NUMBER); } - else if (ecma_is_value_string (typeof_arg)) + else if (ecma_is_value_string (left_value)) { type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_STRING); } else { - JERRY_ASSERT (ecma_is_value_object (typeof_arg)); + JERRY_ASSERT (ecma_is_value_object (left_value)); - if (ecma_op_is_callable (typeof_arg)) + if (ecma_op_is_callable (left_value)) { type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_FUNCTION); } @@ -1555,230 +219,217 @@ opfunc_typeof (vm_instr_t instr, /**< instruction */ } } - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, - dst_var_idx, - ecma_make_string_value (type_str_p)); - - ecma_deref_ecma_string (type_str_p); - - ECMA_FINALIZE (typeof_arg); - - frame_ctx_p->pos++; + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (type_str_p)); return ret_value; } /* opfunc_typeof */ /** - * 'delete' opcode handler. - * - * See also: ECMA-262 v5, 11.4.1 - * - * @return completion value - * Returned value must be freed with ecma_free_completion_value + * Update getter or setter for object literals. */ -ecma_completion_value_t -opfunc_delete_var (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +void +opfunc_set_accessor (bool is_getter, /**< is getter accessor */ + ecma_value_t object, /**< object value */ + ecma_value_t accessor_name, /**< accessor name value */ + ecma_value_t accessor) /**< accessor value */ { - const vm_idx_t dst_var_idx = instr.data.delete_var.lhs; - const vm_idx_t name_lit_idx = instr.data.delete_var.name; - const vm_instr_counter_t lit_oc = frame_ctx_p->pos; - - frame_ctx_p->pos++; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + ecma_object_t *object_p = ecma_get_object_from_value (object); + ecma_string_t *accessor_name_p = ecma_get_string_from_value (accessor_name); + ecma_property_t *property_p = ecma_find_named_property (object_p, accessor_name_p); - lit_cpointer_t lit_cp = bc_get_literal_cp_by_uid (name_lit_idx, frame_ctx_p->bytecode_header_p, lit_oc); - JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); - - ecma_string_t *name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); - - ecma_reference_t ref = ecma_op_get_identifier_reference (frame_ctx_p->lex_env_p, - name_string_p, - frame_ctx_p->is_strict); - - if (ref.is_strict) + if (property_p != NULL && property_p->type != ECMA_PROPERTY_NAMEDACCESSOR) { - /* SyntaxError should be treated as an early error */ - JERRY_UNREACHABLE (); + ecma_delete_property (object_p, property_p); + property_p = NULL; } - else + + if (property_p == NULL) { - if (ecma_is_value_undefined (ref.base)) + ecma_object_t *getter_func_p = NULL; + ecma_object_t *setter_func_p = NULL; + + if (is_getter) { - ret_value = set_variable_value (frame_ctx_p, lit_oc, - dst_var_idx, - ecma_make_simple_value (ECMA_SIMPLE_VALUE_TRUE)); + getter_func_p = ecma_get_object_from_value (accessor); } else { - ecma_object_t *bindings_p = ecma_get_object_from_value (ref.base); - JERRY_ASSERT (ecma_is_lexical_environment (bindings_p)); - - ECMA_TRY_CATCH (delete_completion, - ecma_op_delete_binding (bindings_p, - ECMA_GET_NON_NULL_POINTER (ecma_string_t, - ref.referenced_name_cp)), - ret_value); - - ret_value = set_variable_value (frame_ctx_p, lit_oc, dst_var_idx, delete_completion); - - ECMA_FINALIZE (delete_completion); + setter_func_p = ecma_get_object_from_value (accessor); } - } - - ecma_free_reference (ref); - ecma_deref_ecma_string (name_string_p); + ecma_create_named_accessor_property (object_p, + accessor_name_p, + getter_func_p, + setter_func_p, + true, + true); + } + else if (is_getter) + { + ecma_object_t *getter_func_p = ecma_get_object_from_value (accessor); - return ret_value; -} /* opfunc_delete_var */ + ecma_set_named_accessor_property_getter (object_p, + property_p, + getter_func_p); + } + else + { + ecma_object_t *setter_func_p = ecma_get_object_from_value (accessor); + ecma_set_named_accessor_property_setter (object_p, + property_p, + setter_func_p); + } +} /* opfunc_set_accessor */ /** - * 'delete' opcode handler. - * - * See also: ECMA-262 v5, 11.4.1 + * Deletes an object property. * * @return completion value - * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t -opfunc_delete_prop (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p) /**< interpreter context */ +vm_op_delete_prop (ecma_value_t object, /**< base object */ + ecma_value_t property, /**< property name */ + bool is_strict) /**< strict mode */ { - const vm_idx_t dst_var_idx = instr.data.delete_prop.lhs; - const vm_idx_t base_var_idx = instr.data.delete_prop.base; - const vm_idx_t name_var_idx = instr.data.delete_prop.name; - - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + ecma_completion_value_t completion_value = ecma_make_empty_completion_value (); - ECMA_TRY_CATCH (base_value, - get_variable_value (frame_ctx_p, base_var_idx, false), - ret_value); - ECMA_TRY_CATCH (name_value, - get_variable_value (frame_ctx_p, name_var_idx, false), - ret_value); - ECMA_TRY_CATCH (check_coercible_ret, - ecma_op_check_object_coercible (base_value), - ret_value); - ECMA_TRY_CATCH (str_name_value, - ecma_op_to_string (name_value), - ret_value); - - JERRY_ASSERT (ecma_is_value_string (str_name_value)); - ecma_string_t *name_string_p = ecma_get_string_from_value (str_name_value); - - if (ecma_is_value_undefined (base_value)) + if (ecma_is_value_undefined (object)) { - if (frame_ctx_p->is_strict) - { - /* SyntaxError should be treated as an early error */ - JERRY_UNREACHABLE (); - } - else - { - ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_TRUE); - } + completion_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_TRUE); } else { - ECMA_TRY_CATCH (obj_value, ecma_op_to_object (base_value), ret_value); + completion_value = ecma_make_empty_completion_value (); + + ECMA_TRY_CATCH (check_coercible_ret, + ecma_op_check_object_coercible (object), + completion_value); + ECMA_TRY_CATCH (str_name_value, + ecma_op_to_string (property), + completion_value); + + JERRY_ASSERT (ecma_is_value_string (str_name_value)); + ecma_string_t *name_string_p = ecma_get_string_from_value (str_name_value); + + ECMA_TRY_CATCH (obj_value, ecma_op_to_object (object), completion_value); JERRY_ASSERT (ecma_is_value_object (obj_value)); ecma_object_t *obj_p = ecma_get_object_from_value (obj_value); JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); ECMA_TRY_CATCH (delete_op_ret_val, - ecma_op_object_delete (obj_p, name_string_p, frame_ctx_p->is_strict), - ret_value); + ecma_op_object_delete (obj_p, name_string_p, is_strict), + completion_value); - ret_value = set_variable_value (frame_ctx_p, frame_ctx_p->pos, dst_var_idx, delete_op_ret_val); + completion_value = ecma_make_normal_completion_value (delete_op_ret_val); ECMA_FINALIZE (delete_op_ret_val); ECMA_FINALIZE (obj_value); + ECMA_FINALIZE (str_name_value); + ECMA_FINALIZE (check_coercible_ret); } - ECMA_FINALIZE (str_name_value); - ECMA_FINALIZE (check_coercible_ret); - ECMA_FINALIZE (name_value); - ECMA_FINALIZE (base_value); - - frame_ctx_p->pos++; - - return ret_value; -} /* opfunc_delete_prop */ + return completion_value; +} /* vm_op_delete_prop */ /** - * 'meta' opcode handler. + * Deletes a variable. * - * @return implementation-defined meta completion value + * @return completion value */ ecma_completion_value_t -opfunc_meta (vm_instr_t instr, /**< instruction */ - vm_frame_ctx_t *frame_ctx_p __attr_unused___) /**< interpreter context */ +vm_op_delete_var (lit_cpointer_t name_literal, /**< name literal */ + ecma_object_t *lex_env_p, /**< lexical environment */ + bool is_strict) /**< strict mode */ { - const opcode_meta_type type = (opcode_meta_type) instr.data.meta.type; + ecma_completion_value_t completion_value = ecma_make_empty_completion_value (); - switch (type) + ecma_string_t *var_name_str_p; + + var_name_str_p = ecma_new_ecma_string_from_lit_cp (name_literal); + ecma_reference_t ref = ecma_op_get_identifier_reference (lex_env_p, + var_name_str_p, + is_strict); + + JERRY_ASSERT (!ref.is_strict); + + if (ecma_is_value_undefined (ref.base)) { - case OPCODE_META_TYPE_VARG: - case OPCODE_META_TYPE_VARG_PROP_DATA: - case OPCODE_META_TYPE_VARG_PROP_GETTER: - case OPCODE_META_TYPE_VARG_PROP_SETTER: - case OPCODE_META_TYPE_END_WITH: - case OPCODE_META_TYPE_CATCH: - case OPCODE_META_TYPE_FINALLY: - case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: - case OPCODE_META_TYPE_END_FOR_IN: - { - return ecma_make_meta_completion_value (); - } + completion_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_TRUE); + } + else + { + ecma_object_t *ref_base_lex_env_p = ecma_op_resolve_reference_base (lex_env_p, var_name_str_p); + + JERRY_ASSERT (ecma_is_lexical_environment (ref_base_lex_env_p)); + + ECMA_TRY_CATCH (delete_op_ret_val, + ecma_op_delete_binding (ref_base_lex_env_p, + ECMA_GET_NON_NULL_POINTER (ecma_string_t, + ref.referenced_name_cp)), + completion_value); + + completion_value = ecma_make_normal_completion_value (delete_op_ret_val); + + ECMA_FINALIZE (delete_op_ret_val); - case OPCODE_META_TYPE_UNDEFINED: - case OPCODE_META_TYPE_CALL_SITE_INFO: - case OPCODE_META_TYPE_FUNCTION_END: - case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: - { - JERRY_UNREACHABLE (); - } } - JERRY_UNREACHABLE (); -} /* opfunc_meta */ + ecma_free_reference (ref); + ecma_deref_ecma_string (var_name_str_p); + + return completion_value; +} /* vm_op_delete_var */ /** - * Calculate instruction counter from 'meta' instruction's data arguments. + * 'for-in' opcode handler + * + * See also: + * ECMA-262 v5, 12.6.4 * - * @return instruction counter + * @return completion value + * Returned value must be freed with ecma_free_completion_value */ -vm_instr_counter_t -vm_calc_instr_counter_from_idx_idx (const vm_idx_t oc_idx_1, /**< first idx */ - const vm_idx_t oc_idx_2) /**< second idx */ +ecma_collection_header_t * +opfunc_for_in (ecma_value_t left_value, /**< left value */ + ecma_value_t *result_obj_p) /**< expression object */ { - vm_instr_counter_t counter; + ecma_completion_value_t compl_val = ecma_make_empty_completion_value (); + ecma_collection_header_t *prop_names_p = NULL; - counter = oc_idx_1; - counter = (vm_instr_counter_t) (counter << (sizeof (vm_idx_t) * JERRY_BITSINBYTE)); - counter = (vm_instr_counter_t) (counter | oc_idx_2); + /* 3. */ + if (!ecma_is_value_undefined (left_value) + && !ecma_is_value_null (left_value)) + { + /* 4. */ + ECMA_TRY_CATCH (obj_expr_value, + ecma_op_to_object (left_value), + compl_val); - return counter; -} /* vm_calc_instr_counter_from_idx_idx */ + ecma_object_t *obj_p = ecma_get_object_from_value (obj_expr_value); + prop_names_p = ecma_op_object_get_property_names (obj_p, false, true, true); -/** - * Read instruction counter from specified instruction, - * that should be 'meta' instruction of specified type. - */ -vm_instr_counter_t -vm_read_instr_counter_from_meta (opcode_meta_type expected_type, /**< expected type of meta instruction */ - const bytecode_data_header_t *bytecode_header_p, /**< byte-code header */ - vm_instr_counter_t instr_pos) /**< position of the instruction */ -{ - vm_instr_t meta_opcode = vm_get_instr (bytecode_header_p->instrs_p, instr_pos); - JERRY_ASSERT (meta_opcode.data.meta.type == expected_type); + if (prop_names_p->unit_number != 0) + { + ecma_ref_object (obj_p); + *result_obj_p = ecma_make_object_value (obj_p); + } + else + { + ecma_dealloc_collection_header (prop_names_p); + prop_names_p = NULL; + } + + ECMA_FINALIZE (obj_expr_value); + } + + JERRY_ASSERT (ecma_is_completion_value_empty (compl_val)); - const vm_idx_t data_1 = meta_opcode.data.meta.data_1; - const vm_idx_t data_2 = meta_opcode.data.meta.data_2; + return prop_names_p; +} /* opfunc_for_in */ - return vm_calc_instr_counter_from_idx_idx (data_1, data_2); -} /* vm_read_instr_counter_from_meta */ +/** + * @} + * @} + */ diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 0cdbc98dd2..cc7707a9c3 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* 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. @@ -17,284 +18,119 @@ #define OPCODES_H #include "ecma-globals.h" -#include "jrt.h" -#include "vm-stack.h" +#include "vm-defines.h" -/* Maximum opcodes number in bytecode. */ -#define MAX_OPCODES (256*256 - 1) - -#define OP_0(action, name) \ - __##action (name, void, void, void) - -#define OP_1(action, name, field1) \ - __##action (name, field1, void, void) - -#define OP_2(action, name, field1, field2) \ - __##action (name, field1, field2, void) - -#define OP_3(action, name, field1, field2, field3) \ - __##action (name, field1, field2, field3) - -/** - * Instruction counter / position - */ -typedef uint16_t vm_instr_counter_t; - -/** - * Opcode / argument value in an instruction ("idx") +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_opcodes Opcodes + * @{ */ -typedef uint8_t vm_idx_t; /** - * Description of vm_idx_t possible value ranges and special values + * Number arithmetic operations. */ -enum : vm_idx_t +typedef enum { - VM_IDX_GENERAL_VALUE_FIRST = 0, /**< first idx value that can be used for any argument value */ - VM_IDX_GENERAL_VALUE_LAST = 252, /**< last idx value that can be used for any argument value */ - - /* - * Special values - */ - VM_IDX_REWRITE_GENERAL_CASE = 253, /**< intermediate value, used during byte-code generation, - * indicating that the idx would be rewritten with a value - * other than in-block literal identifier */ - VM_IDX_REWRITE_LITERAL_UID = 254, /**< intermediate value, used during byte-code generation, - * indicating that the idx would be rewritten with in-block - * literal identifier */ - VM_IDX_EMPTY = 255, /**< empty idx value, used when corresponding instruction argument is not set */ - - /* - * Literals (variable names / strings / numbers) ranges - */ - VM_IDX_LITERAL_FIRST = VM_IDX_GENERAL_VALUE_FIRST, /**< index of first possible literals-related idx value */ - VM_IDX_LITERAL_LAST = VM_IDX_LITERAL_FIRST + 127, /**< index of last possible literals-related idx value */ - - /* - * Registers (temp variables) ranges - */ - VM_IDX_REG_FIRST = VM_IDX_LITERAL_LAST + 1, /** identifier of first special register */ - VM_IDX_REG_LAST = VM_IDX_GENERAL_VALUE_LAST, /**< identifier of last register */ -}; + NUMBER_ARITHMETIC_ADDITION, /**< addition */ + NUMBER_ARITHMETIC_SUBSTRACTION, /**< substraction */ + NUMBER_ARITHMETIC_MULTIPLICATION, /**< multiplication */ + NUMBER_ARITHMETIC_DIVISION, /**< division */ + NUMBER_ARITHMETIC_REMAINDER, /**< remainder calculation */ +} number_arithmetic_op; /** - * Ranges of registers (temporary variables) + * Number bitwise logic operations. */ -typedef enum : vm_idx_t +typedef enum { - VM_REG_FIRST = VM_IDX_REG_FIRST, /** first register */ - VM_REG_LAST = VM_IDX_REG_LAST, /**< last register */ - - VM_REG_SPECIAL_FIRST = VM_REG_FIRST, /**< first special register */ + NUMBER_BITWISE_LOGIC_AND, /**< bitwise AND calculation */ + NUMBER_BITWISE_LOGIC_OR, /**< bitwise OR calculation */ + NUMBER_BITWISE_LOGIC_XOR, /**< bitwise XOR calculation */ + NUMBER_BITWISE_SHIFT_LEFT, /**< bitwise LEFT SHIFT calculation */ + NUMBER_BITWISE_SHIFT_RIGHT, /**< bitwise RIGHT_SHIFT calculation */ + NUMBER_BITWISE_SHIFT_URIGHT, /**< bitwise UNSIGNED RIGHT SHIFT calculation */ + NUMBER_BITWISE_NOT, /**< bitwise NOT calculation */ +} number_bitwise_logic_op; - VM_REG_SPECIAL_EVAL_RET = VM_REG_SPECIAL_FIRST, /**< eval return value */ - VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME, /**< variable, containing property name, - * at start of for-in loop body */ - VM_REG_SPECIAL_THIS_BINDING, /**< value of ThisBinding */ +ecma_completion_value_t +vm_var_decl (vm_frame_ctx_t *, ecma_string_t *); - VM_REG_SPECIAL_LAST = VM_REG_SPECIAL_THIS_BINDING, /**< last special register */ +ecma_completion_value_t +opfunc_call_n (ecma_value_t, + ecma_value_t, + const ecma_value_t *, + ecma_length_t); - VM_REG_GENERAL_FIRST, /** first non-special register */ - VM_REG_GENERAL_LAST = VM_IDX_REG_LAST /** last non-special register */ -} vm_reg_t; - -/** - * Number of special VM registers - */ -#define VM_SPECIAL_REGS_NUMBER (VM_REG_SPECIAL_LAST - VM_REG_SPECIAL_FIRST + 1u) +ecma_completion_value_t +opfunc_construct_n (ecma_value_t, uint8_t, ecma_value_t *); -/** - * Descriptor of assignment's second argument - * that specifies type of third argument. - */ -typedef enum -{ - OPCODE_ARG_TYPE_SIMPLE, /**< ecma_simple_value_t */ - OPCODE_ARG_TYPE_SMALLINT, /**< small integer: from 0 to 255 */ - OPCODE_ARG_TYPE_SMALLINT_NEGATE, /**< small integer: from -255 to -0 */ - OPCODE_ARG_TYPE_NUMBER, /**< index of number literal */ - OPCODE_ARG_TYPE_NUMBER_NEGATE, /**< index of number literal with negation */ - OPCODE_ARG_TYPE_STRING, /**< index of string literal */ - OPCODE_ARG_TYPE_VARIABLE, /**< index of string literal with variable name */ - OPCODE_ARG_TYPE_REGEXP /**< index of string literal with regular expression */ -} opcode_arg_type_operand; +ecma_completion_value_t +opfunc_equal_value (ecma_value_t, ecma_value_t); -/** - * Types of data in 'meta' opcode. - */ -typedef enum -{ - OPCODE_META_TYPE_UNDEFINED, /**< undefined meta (should be rewritten) */ - OPCODE_META_TYPE_CALL_SITE_INFO, /**< optional additional information about call site - * (includes opcode_call_flags_t and can include 'this' argument) */ - OPCODE_META_TYPE_VARG, /**< element (var_idx) of arguments' list */ - OPCODE_META_TYPE_VARG_PROP_DATA, /**< name (lit_idx) and value (var_idx) for a data property descriptor */ - OPCODE_META_TYPE_VARG_PROP_GETTER, /**< name (lit_idx) and getter (var_idx) for an accessor property descriptor */ - OPCODE_META_TYPE_VARG_PROP_SETTER, /**< name (lit_idx) and setter (var_idx) for an accessor property descriptor */ - OPCODE_META_TYPE_END_WITH, /**< end of with statement */ - OPCODE_META_TYPE_FUNCTION_END, /**< offset to function end */ - OPCODE_META_TYPE_CATCH, /**< mark of beginning of catch block containing pointer to end of catch block */ - OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER, /**< literal index containing name of variable with exception object */ - OPCODE_META_TYPE_FINALLY, /**< mark of beginning of finally block containing pointer to end of finally block */ - OPCODE_META_TYPE_END_TRY_CATCH_FINALLY, /**< mark of end of try-catch, try-finally, try-catch-finally blocks */ - OPCODE_META_TYPE_END_FOR_IN /**< end of for-in statement */ -} opcode_meta_type; +ecma_completion_value_t +opfunc_not_equal_value (ecma_value_t, ecma_value_t); -typedef enum : vm_idx_t -{ - OPCODE_CALL_FLAGS__EMPTY = (0u), /**< initializer for empty flag set */ - OPCODE_CALL_FLAGS_HAVE_THIS_ARG = (1u << 0), /**< flag, indicating that call is performed - * with 'this' argument specified */ - OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM = (1u << 1) /**< flag, indicating that call is performed - * in form 'eval (...)', i.e. through 'eval' string - * without object base (i.e. with lexical environment - * as base), so it can be a direct call to eval - * See also: ECMA-262 v5, 15.1.2.1.1 - */ -} opcode_call_flags_t; +ecma_completion_value_t +opfunc_equal_value_type (ecma_value_t, ecma_value_t); -/** - * Types of byte-code instruction arguments, used for instruction description - * - * See also: - * vm-opcodes.inc.h - */ -typedef enum -{ - VM_OP_ARG_TYPE_EMPTY = (1u << 0), /**< empty argument (no value) */ - VM_OP_ARG_TYPE_REGISTER = (1u << 1), /**< register variable (index) */ - VM_OP_ARG_TYPE_IDENTIFIER = (1u << 2), /**< identifier - named variable (string literal) */ - VM_OP_ARG_TYPE_STRING = (1u << 3), /**< string constant value (string literal) */ - VM_OP_ARG_TYPE_NUMBER = (1u << 4), /**< number constant value (number literal) */ - VM_OP_ARG_TYPE_INTEGER_CONST = (1u << 5), /**< a 8-bit integer constant (any vm_idx_t) */ - VM_OP_ARG_TYPE_TYPE_OF_NEXT = (1u << 6), /**< opcode_arg_type_operand value, - * representing type of argument encoded in next idx */ +ecma_completion_value_t +opfunc_not_equal_value_type (ecma_value_t, ecma_value_t); - /** variable - an identifier or a register */ - VM_OP_ARG_TYPE_VARIABLE = (VM_OP_ARG_TYPE_REGISTER | VM_OP_ARG_TYPE_IDENTIFIER) -} vm_op_arg_type_t; +ecma_completion_value_t +do_number_arithmetic (number_arithmetic_op, ecma_value_t, ecma_value_t); -/** - * Forward declaration of instruction structure - */ -struct vm_instr_t; +ecma_completion_value_t +opfunc_unary_plus (ecma_value_t); -/** - * Forward declaration of bytecode data header structure - */ -struct bytecode_data_header_t; +ecma_completion_value_t +opfunc_unary_minus (ecma_value_t); -/** - * Context of interpreter, related to a JS stack frame - */ -typedef struct -{ - const bytecode_data_header_t *bytecode_header_p; /**< currently executed byte-code data */ - vm_instr_counter_t pos; /**< current position instruction to execute */ - ecma_object_t *lex_env_p; /**< current lexical environment */ - bool is_strict; /**< is current code execution mode strict? */ - bool is_eval_code; /**< is current code executed with eval */ - bool is_call_in_direct_eval_form; /** flag, indicating if there is call of 'Direct call to eval' form in - * process (see also: OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) */ - ecma_number_t *tmp_num_p; /**< an allocated number (to reduce temporary allocations) */ - vm_stack_frame_t stack_frame; /**< stack frame associated with the context */ +ecma_completion_value_t +do_number_bitwise_logic (number_bitwise_logic_op, ecma_value_t, ecma_value_t); -#ifdef MEM_STATS - size_t context_peak_allocated_heap_bytes; - size_t context_peak_waste_heap_bytes; - size_t context_peak_pools_count; - size_t context_peak_allocated_pool_chunks; +ecma_completion_value_t +opfunc_addition (ecma_value_t, ecma_value_t); - mem_heap_stats_t heap_stats_context_enter; - mem_pools_stats_t pools_stats_context_enter; -#endif /* MEM_STATS */ -} vm_frame_ctx_t; +ecma_completion_value_t +opfunc_less_than (ecma_value_t, ecma_value_t); -/** - * Description of a run scope - * - * Note: - * Run scope represents boundaries of byte-code block to run. - * - * Jumps within of the current run scope are performed by just changing instruction counter, - * and outside of the run scope - by returning corresponding ECMA_COMPLETION_TYPE_BREAK_CONTINUE - * completion value. - */ -typedef struct -{ - const vm_instr_counter_t start_oc; /**< instruction counter of the first instruction of the scope */ - const vm_instr_counter_t end_oc; /**< instruction counter of the last instruction of the scope */ -} vm_run_scope_t; +ecma_completion_value_t +opfunc_greater_than (ecma_value_t, ecma_value_t); -vm_instr_counter_t vm_calc_instr_counter_from_idx_idx (const vm_idx_t, const vm_idx_t); -vm_instr_counter_t vm_read_instr_counter_from_meta (opcode_meta_type, - const bytecode_data_header_t *, - vm_instr_counter_t); +ecma_completion_value_t +opfunc_less_or_equal_than (ecma_value_t, ecma_value_t); -typedef struct vm_instr_t -{ - vm_idx_t op_idx; - union - { -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - struct \ - { \ - vm_idx_t arg1; \ - } opcode_name; +ecma_completion_value_t +opfunc_greater_or_equal_than (ecma_value_t, ecma_value_t); -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - struct \ - { \ - vm_idx_t arg1; \ - vm_idx_t arg2; \ - } opcode_name; -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - struct \ - { \ - vm_idx_t arg1; \ - vm_idx_t arg2; \ - vm_idx_t arg3; \ - } opcode_name; +ecma_completion_value_t +opfunc_in (ecma_value_t, ecma_value_t); -#include "vm-opcodes.inc.h" +ecma_completion_value_t +opfunc_instanceof (ecma_value_t, ecma_value_t); - /** - * Opcode-independent arguments accessor - * - * Note: - * If opcode is statically known, opcode-specific way of accessing arguments should be used. - */ - vm_idx_t raw_args[3]; - } data; -} vm_instr_t; +ecma_completion_value_t +opfunc_logical_not (ecma_value_t); -typedef enum -{ -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - VM_OP_ ## opcode_name_uppercase, -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - VM_OP_ ## opcode_name_uppercase, -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - VM_OP_ ## opcode_name_uppercase, -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - VM_OP_ ## opcode_name_uppercase, +ecma_completion_value_t +opfunc_typeof (ecma_value_t); -#include "vm-opcodes.inc.h" +void +opfunc_set_accessor (bool, ecma_value_t, ecma_value_t, ecma_value_t); - VM_OP__COUNT /**< number of opcodes */ -} vm_op_t; +ecma_completion_value_t +vm_op_delete_prop (ecma_value_t, ecma_value_t, bool); -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - ecma_completion_value_t opfunc_##opcode_name (vm_instr_t, vm_frame_ctx_t *); -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - ecma_completion_value_t opfunc_##opcode_name (vm_instr_t, vm_frame_ctx_t *); -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - ecma_completion_value_t opfunc_##opcode_name (vm_instr_t, vm_frame_ctx_t *); -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - ecma_completion_value_t opfunc_##opcode_name (vm_instr_t, vm_frame_ctx_t *); +ecma_completion_value_t +vm_op_delete_var (lit_cpointer_t, ecma_object_t *, bool); -#include "vm-opcodes.inc.h" +ecma_collection_header_t * +opfunc_for_in (ecma_value_t, ecma_value_t *); -typedef ecma_completion_value_t (*opfunc) (vm_instr_t, vm_frame_ctx_t *); +/** + * @} + * @} + */ -#endif /* OPCODES_H */ +#endif /* !OPCODES_H */ diff --git a/jerry-core/vm/pretty-printer.cpp b/jerry-core/vm/pretty-printer.cpp deleted file mode 100644 index d7b8ac23f3..0000000000 --- a/jerry-core/vm/pretty-printer.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef JERRY_ENABLE_PRETTY_PRINTER -#include - -#include "pretty-printer.h" -#include "jrt-libc-includes.h" -#include "lexer.h" -#include "ecma-helpers.h" -#include "ecma-globals.h" -#include "lit-literal.h" - -static const char* opcode_names[] = -{ -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - #opcode_name, -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - #opcode_name, -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - #opcode_name, -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - #opcode_name, - -#include "vm-opcodes.inc.h" -}; - -static uint8_t opcode_sizes[] = -{ -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - 0, -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - 1, -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - 2, -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - 3, - -#include "vm-opcodes.inc.h" -}; - -const bytecode_data_header_t *bc_to_print_header_p = NULL; - -static char buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; - -static void -clear_temp_buffer (void) -{ - memset (buff, 0, ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); -} - -static const char * -lit_cp_to_str (lit_cpointer_t cp) -{ - lit_literal_t lit = lit_get_literal_by_cp (cp); - return lit_literal_to_str_internal_buf (lit); -} - -static const char * -tmp_id_to_str (vm_idx_t id) -{ - JERRY_ASSERT (id != VM_IDX_REWRITE_LITERAL_UID); - JERRY_ASSERT (id >= 128); - clear_temp_buffer (); - strncpy (buff, "tmp", 3); - if (id / 100 != 0) - { - buff[3] = (char) (id / 100 + '0'); - buff[4] = (char) ((id % 100) / 10 + '0'); - buff[5] = (char) (id % 10 + '0'); - } - else if (id / 10 != 0) - { - buff[3] = (char) (id / 10 + '0'); - buff[4] = (char) (id % 10 + '0'); - } - else - { - buff[3] = (char) (id + '0'); - } - return buff; -} - -static const char * -var_to_str (vm_instr_t instr, lit_cpointer_t lit_ids[], vm_instr_counter_t oc, uint8_t current_arg) -{ - JERRY_ASSERT (current_arg >= 1 && current_arg <= 3); - - if (instr.data.raw_args[current_arg - 1] == VM_IDX_REWRITE_LITERAL_UID) - { - JERRY_ASSERT (lit_ids != NULL); - JERRY_ASSERT (lit_ids[current_arg - 1].packed_value != MEM_CP_NULL); - - return lit_cp_to_str (lit_ids[current_arg - 1]); - } - else if (instr.data.raw_args[current_arg - 1] >= 128) - { - return tmp_id_to_str (instr.data.raw_args[current_arg - 1]); - } - else - { - return lit_cp_to_str (bc_get_literal_cp_by_uid (instr.data.raw_args[current_arg - 1], - bc_to_print_header_p, - oc)); - } -} - -static void -pp_printf (const char *format, vm_instr_t instr, lit_cpointer_t lit_ids[], vm_instr_counter_t oc, uint8_t start_arg) -{ - uint8_t current_arg = start_arg; - JERRY_ASSERT (current_arg <= 3); - while (*format) - { - if (*format != '%') - { - jerry_port_putchar (*format); - format++; - continue; - } - - format++; - switch (*format) - { - case 'd': - { - JERRY_ASSERT (current_arg >= 1 && current_arg <= 3); - printf ("%d", instr.data.raw_args[current_arg - 1]); - break; - } - case 's': - { - printf ("%s", var_to_str (instr, lit_ids, oc, current_arg)); - break; - } - default: - { - jerry_port_putchar ('%'); - continue; - } - } - current_arg++; - format++; - } -} - -#define PP_OP(op_name, format) \ - case op_name: pp_printf (format, opm.op, opm.lit_id, oc, 1); break; -#define VAR(i) var_to_str (opm.op, opm.lit_id, oc, i) -#define OC(i, j) __extension__({ vm_calc_instr_counter_from_idx_idx (opm.op.data.raw_args[i - 1], \ - opm.op.data.raw_args[j - 1]); }) - -static int vargs_num = 0; -static int seen_vargs = 0; - -static void -dump_asm (vm_instr_counter_t oc, vm_instr_t instr) -{ - uint8_t i = 0; - uint8_t opcode_id = instr.op_idx; - printf ("%3d: %20s ", oc, opcode_names[opcode_id]); - - for (i = 1; i <= opcode_sizes[opcode_id]; i++) - { - printf ("%4d ", instr.data.raw_args[i - 1]); - } - - for (; i < 4; i++) - { - printf (" "); - } -} - -void -pp_op_meta (const bytecode_data_header_t *bytecode_data_p, - vm_instr_counter_t oc, - op_meta opm, - bool rewrite) -{ - bc_to_print_header_p = bytecode_data_p; - - dump_asm (oc, opm.op); - printf (" // "); - - switch (opm.op.op_idx) - { - PP_OP (VM_OP_ADDITION, "%s = %s + %s;"); - PP_OP (VM_OP_SUBSTRACTION, "%s = %s - %s;"); - PP_OP (VM_OP_DIVISION, "%s = %s / %s;"); - PP_OP (VM_OP_MULTIPLICATION, "%s = %s * %s;"); - PP_OP (VM_OP_REMAINDER, "%s = %s %% %s;"); - PP_OP (VM_OP_UNARY_MINUS, "%s = -%s;"); - PP_OP (VM_OP_UNARY_PLUS, "%s = +%s;"); - PP_OP (VM_OP_B_SHIFT_LEFT, "%s = %s << %s;"); - PP_OP (VM_OP_B_SHIFT_RIGHT, "%s = %s >> %s;"); - PP_OP (VM_OP_B_SHIFT_URIGHT, "%s = %s >>> %s;"); - PP_OP (VM_OP_B_AND, "%s = %s & %s;"); - PP_OP (VM_OP_B_OR, "%s = %s | %s;"); - PP_OP (VM_OP_B_XOR, "%s = %s ^ %s;"); - PP_OP (VM_OP_B_NOT, "%s = ~ %s;"); - PP_OP (VM_OP_LOGICAL_NOT, "%s = ! %s;"); - PP_OP (VM_OP_EQUAL_VALUE, "%s = %s == %s;"); - PP_OP (VM_OP_NOT_EQUAL_VALUE, "%s = %s != %s;"); - PP_OP (VM_OP_EQUAL_VALUE_TYPE, "%s = %s === %s;"); - PP_OP (VM_OP_NOT_EQUAL_VALUE_TYPE, "%s = %s !== %s;"); - PP_OP (VM_OP_LESS_THAN, "%s = %s < %s;"); - PP_OP (VM_OP_GREATER_THAN, "%s = %s > %s;"); - PP_OP (VM_OP_LESS_OR_EQUAL_THAN, "%s = %s <= %s;"); - PP_OP (VM_OP_GREATER_OR_EQUAL_THAN, "%s = %s >= %s;"); - PP_OP (VM_OP_INSTANCEOF, "%s = %s instanceof %s;"); - PP_OP (VM_OP_IN, "%s = %s in %s;"); - PP_OP (VM_OP_POST_INCR, "%s = %s++;"); - PP_OP (VM_OP_POST_DECR, "%s = %s--;"); - PP_OP (VM_OP_PRE_INCR, "%s = ++%s;"); - PP_OP (VM_OP_PRE_DECR, "%s = --%s;"); - PP_OP (VM_OP_THROW_VALUE, "throw %s;"); - PP_OP (VM_OP_REG_VAR_DECL, "%d tmp regs, %d local variable regs, %d argument variable regs"); - PP_OP (VM_OP_VAR_DECL, "var %s;"); - PP_OP (VM_OP_RETVAL, "return %s;"); - PP_OP (VM_OP_RET, "ret;"); - PP_OP (VM_OP_PROP_GETTER, "%s = %s[%s];"); - PP_OP (VM_OP_PROP_SETTER, "%s[%s] = %s;"); - PP_OP (VM_OP_DELETE_VAR, "%s = delete %s;"); - PP_OP (VM_OP_DELETE_PROP, "%s = delete %s.%s;"); - PP_OP (VM_OP_TYPEOF, "%s = typeof %s;"); - PP_OP (VM_OP_WITH, "with (%s);"); - PP_OP (VM_OP_FOR_IN, "for_in (%s);"); - case VM_OP_IS_TRUE_JMP_UP: printf ("if (%s) goto %d;", VAR (1), oc - OC (2, 3)); break; - case VM_OP_IS_FALSE_JMP_UP: printf ("if (%s == false) goto %d;", VAR (1), oc - OC (2, 3)); break; - case VM_OP_IS_TRUE_JMP_DOWN: printf ("if (%s) goto %d;", VAR (1), oc + OC (2, 3)); break; - case VM_OP_IS_FALSE_JMP_DOWN: printf ("if (%s == false) goto %d;", VAR (1), oc + OC (2, 3)); break; - case VM_OP_JMP_UP: printf ("goto %d;", oc - OC (1, 2)); break; - case VM_OP_JMP_DOWN: printf ("goto %d;", oc + OC (1, 2)); break; - case VM_OP_JMP_BREAK_CONTINUE: printf ("goto_nested %d;", oc + OC (1, 2)); break; - case VM_OP_TRY_BLOCK: printf ("try (end: %d);", oc + OC (1, 2)); break; - case VM_OP_ASSIGNMENT: - { - printf ("%s = ", VAR (1)); - switch (opm.op.data.assignment.type_value_right) - { - case OPCODE_ARG_TYPE_STRING: printf ("'%s': STRING;", VAR (3)); break; - case OPCODE_ARG_TYPE_NUMBER: printf ("%s: NUMBER;", VAR (3)); break; - case OPCODE_ARG_TYPE_NUMBER_NEGATE: printf ("-%s: NUMBER;", VAR (3)); break; - case OPCODE_ARG_TYPE_SMALLINT: printf ("%d: SMALLINT;", opm.op.data.assignment.value_right); break; - case OPCODE_ARG_TYPE_SMALLINT_NEGATE: printf ("-%d: SMALLINT;", opm.op.data.assignment.value_right); break; - case OPCODE_ARG_TYPE_VARIABLE: printf ("%s : TYPEOF(%s);", VAR (3), VAR (3)); break; - case OPCODE_ARG_TYPE_SIMPLE: - { - switch (opm.op.data.assignment.value_right) - { - case ECMA_SIMPLE_VALUE_NULL: printf ("null"); break; - case ECMA_SIMPLE_VALUE_FALSE: printf ("false"); break; - case ECMA_SIMPLE_VALUE_TRUE: printf ("true"); break; - case ECMA_SIMPLE_VALUE_UNDEFINED: printf ("undefined"); break; - case ECMA_SIMPLE_VALUE_ARRAY_HOLE: printf ("hole"); break; - default: JERRY_UNREACHABLE (); - } - printf (": SIMPLE;"); - break; - } - } - break; - } - case VM_OP_CALL_N: - { - vargs_num = opm.op.data.call_n.arg_list; - seen_vargs = 0; - - break; - } - case VM_OP_CONSTRUCT_N: - { - if (opm.op.data.construct_n.arg_list == 0) - { - pp_printf ("%s = new %s;", opm.op, opm.lit_id, oc, 1); - } - else - { - vargs_num = opm.op.data.construct_n.arg_list; - seen_vargs = 0; - } - break; - } - case VM_OP_FUNC_DECL_N: - { - if (opm.op.data.func_decl_n.arg_list == 0) - { - printf ("function %s ();", VAR (1)); - } - else - { - vargs_num = opm.op.data.func_decl_n.arg_list; - seen_vargs = 0; - } - break; - } - case VM_OP_FUNC_EXPR_REF: - { - printf ("%s = function ();", VAR (1)); - break; - } - case VM_OP_FUNC_EXPR_N: - { - if (opm.op.data.func_expr_n.arg_list == 0) - { - if (opm.op.data.func_expr_n.name_lit_idx == VM_IDX_EMPTY) - { - printf ("%s = function ();", VAR (1)); - } - else - { - pp_printf ("%s = function %s ();", opm.op, opm.lit_id, oc, 1); - } - } - else - { - vargs_num = opm.op.data.func_expr_n.arg_list; - seen_vargs = 0; - } - break; - } - case VM_OP_ARRAY_DECL: - { - if (opm.op.data.array_decl.list_1 == 0 - && opm.op.data.array_decl.list_2 == 0) - { - printf ("%s = [];", VAR (1)); - } - else - { - vargs_num = (((int) opm.op.data.array_decl.list_1 << JERRY_BITSINBYTE) - + (int) opm.op.data.array_decl.list_2); - seen_vargs = 0; - } - break; - } - case VM_OP_OBJ_DECL: - { - if (opm.op.data.obj_decl.list_1 == 0 - && opm.op.data.obj_decl.list_2 == 0) - { - printf ("%s = {};", VAR (1)); - } - else - { - vargs_num = (((int) opm.op.data.obj_decl.list_1 << JERRY_BITSINBYTE) - + (int) opm.op.data.obj_decl.list_2); - seen_vargs = 0; - } - break; - } - case VM_OP_META: - { - switch (opm.op.data.meta.type) - { - case OPCODE_META_TYPE_UNDEFINED: - { - printf ("unknown meta;"); - break; - } - case OPCODE_META_TYPE_CALL_SITE_INFO: - case OPCODE_META_TYPE_VARG: - case OPCODE_META_TYPE_VARG_PROP_DATA: - case OPCODE_META_TYPE_VARG_PROP_GETTER: - case OPCODE_META_TYPE_VARG_PROP_SETTER: - { - if (opm.op.data.meta.type != OPCODE_META_TYPE_CALL_SITE_INFO) - { - seen_vargs++; - } - - if (seen_vargs == vargs_num) - { - bool found = false; - vm_instr_counter_t start = oc; - while ((int16_t) start >= 0 && !found) - { - start--; - switch (bc_get_instr (bytecode_data_p, start).op_idx) - { - case VM_OP_CALL_N: - case VM_OP_CONSTRUCT_N: - case VM_OP_FUNC_DECL_N: - case VM_OP_FUNC_EXPR_N: - case VM_OP_ARRAY_DECL: - case VM_OP_OBJ_DECL: - { - found = true; - break; - } - } - } - vm_instr_t start_op = bc_get_instr (bytecode_data_p, start); - switch (start_op.op_idx) - { - case VM_OP_CALL_N: - { - pp_printf ("%s = %s (", start_op, NULL, start, 1); - break; - } - case VM_OP_CONSTRUCT_N: - { - pp_printf ("%s = new %s (", start_op, NULL, start, 1); - break; - } - case VM_OP_FUNC_DECL_N: - { - pp_printf ("function %s (", start_op, NULL, start, 1); - break; - } - case VM_OP_FUNC_EXPR_N: - { - if (start_op.data.func_expr_n.name_lit_idx == VM_IDX_EMPTY) - { - pp_printf ("%s = function (", start_op, NULL, start, 1); - } - else - { - pp_printf ("%s = function %s (", start_op, NULL, start, 1); - } - break; - } - case VM_OP_ARRAY_DECL: - { - pp_printf ("%s = [", start_op, NULL, start, 1); - break; - } - case VM_OP_OBJ_DECL: - { - pp_printf ("%s = {", start_op, NULL, start, 1); - break; - } - default: - { - JERRY_UNREACHABLE (); - } - } - for (vm_instr_counter_t counter = start; counter <= oc; counter++) - { - vm_instr_t meta_op = bc_get_instr (bytecode_data_p, counter); - - switch (meta_op.op_idx) - { - case VM_OP_META: - { - switch (meta_op.data.meta.type) - { - case OPCODE_META_TYPE_CALL_SITE_INFO: - { - opcode_call_flags_t call_flags = (opcode_call_flags_t) meta_op.data.meta.data_1; - - if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) - { - pp_printf ("this_arg = %s", meta_op, NULL, counter, 3); - } - if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) - { - printf ("['direct call to eval' form]"); - } - - break; - } - case OPCODE_META_TYPE_VARG: - { - pp_printf ("%s", meta_op, NULL, counter, 2); - break; - } - case OPCODE_META_TYPE_VARG_PROP_DATA: - { - pp_printf ("%s:%s", meta_op, NULL, counter, 2); - break; - } - case OPCODE_META_TYPE_VARG_PROP_GETTER: - { - pp_printf ("%s = get %s ();", meta_op, NULL, counter, 2); - break; - } - case OPCODE_META_TYPE_VARG_PROP_SETTER: - { - pp_printf ("%s = set (%s);", meta_op, NULL, counter, 2); - break; - } - default: - { - continue; - } - } - if (counter != oc) - { - printf (", "); - } - break; - } - } - } - switch (start_op.op_idx) - { - case VM_OP_ARRAY_DECL: - { - printf ("];"); - break; - } - case VM_OP_OBJ_DECL: - { - printf ("};"); - break; - } - default: - { - printf (");"); - } - } - } - break; - } - case OPCODE_META_TYPE_END_WITH: - { - printf ("end with;"); - break; - } - case OPCODE_META_TYPE_END_FOR_IN: - { - printf ("end for-in;"); - break; - } - case OPCODE_META_TYPE_FUNCTION_END: - { - printf ("function end: %d;", oc + OC (2, 3)); - break; - } - case OPCODE_META_TYPE_CATCH: - { - printf ("catch end: %d;", oc + OC (2, 3)); - break; - } - case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: - { - printf ("catch (%s);", VAR (2)); - break; - } - case OPCODE_META_TYPE_FINALLY: - { - printf ("finally end: %d;", oc + OC (2, 3)); - break; - } - case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: - { - printf ("end try"); - break; - } - default: - { - JERRY_UNREACHABLE (); - } - } - break; - } - default: - { - JERRY_UNREACHABLE (); - } - } - - if (rewrite) - { - printf (" // REWRITE"); - } - - printf ("\n"); -} -#endif /* JERRY_ENABLE_PRETTY_PRINTER */ diff --git a/jerry-core/vm/pretty-printer.h b/jerry-core/vm/pretty-printer.h deleted file mode 100644 index 9fb7a4fddc..0000000000 --- a/jerry-core/vm/pretty-printer.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PRETTY_PRINTER -#define PRETTY_PRINTER - -#include "jrt.h" -#ifdef JERRY_ENABLE_PRETTY_PRINTER -#include "bytecode-data.h" -#include "vm.h" -#include "scopes-tree.h" - -void pp_op_meta (const bytecode_data_header_t *, vm_instr_counter_t, op_meta, bool); -#endif // JERRY_ENABLE_PRETTY_PRINTER - -#endif // PRETTY_PRINTER diff --git a/jerry-core/vm/vm-defines.h b/jerry-core/vm/vm-defines.h new file mode 100644 index 0000000000..c607e5f58b --- /dev/null +++ b/jerry-core/vm/vm-defines.h @@ -0,0 +1,61 @@ +/* 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. + */ +#ifndef VM_DEFINES_H +#define VM_DEFINES_H + +#include "byte-code.h" +#include "ecma-globals.h" + +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_executor Executor + * @{ + */ + +/** + * Helpers for updating uint16_t values. + */ +#define VM_PLUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) + (value)) +#define VM_MINUS_EQUAL_U16(base, value) (base) = (uint16_t) ((base) - (value)) + +/** + * Instruction counter / position + */ +typedef const uint8_t *vm_instr_counter_t; + +/** + * Context of interpreter, related to a JS stack frame + */ +typedef struct +{ + const ecma_compiled_code_t *bytecode_header_p; /**< currently executed byte-code data */ + uint8_t *byte_code_p; /**< current byte code pointer */ + uint8_t *byte_code_start_p; /**< byte code start pointer */ + ecma_value_t *registers_p; /**< register start pointer */ + lit_cpointer_t *literal_start_p; /**< literal list start pointer */ + ecma_object_t *lex_env_p; /**< current lexical environment */ + ecma_value_t this_binding; /**< this binding */ + uint16_t context_depth; /**< current context depth */ + bool is_eval_code; /**< eval mode flag */ +} vm_frame_ctx_t; + +/** + * @} + * @} + */ + +#endif /* !VM_DEFINES_H */ diff --git a/jerry-core/vm/vm-opcodes.inc.h b/jerry-core/vm/vm-opcodes.inc.h deleted file mode 100644 index f3d1f7bf35..0000000000 --- a/jerry-core/vm/vm-opcodes.inc.h +++ /dev/null @@ -1,317 +0,0 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * List of VM opcodes - */ - -#ifndef VM_OP_0 -# define VM_OP_0(opcode_name, opcode_name_uppercase) -#endif /* !VM_OP_0 */ - -#ifndef VM_OP_1 -# define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) -#endif /* !VM_OP_1 */ - -#ifndef VM_OP_2 -# define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) -#endif /* !VM_OP_2 */ - -#ifndef VM_OP_3 -# define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) -#endif /* !VM_OP_3 */ - -VM_OP_3 (call_n, CALL_N, - lhs, VM_OP_ARG_TYPE_VARIABLE, - function_var_idx, VM_OP_ARG_TYPE_VARIABLE, - arg_list, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (construct_n, CONSTRUCT_N, - lhs, VM_OP_ARG_TYPE_VARIABLE, - name_lit_idx, VM_OP_ARG_TYPE_VARIABLE, - arg_list, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_2 (func_decl_n, FUNC_DECL_N, - name_lit_idx, VM_OP_ARG_TYPE_STRING, - arg_list, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (func_expr_n, FUNC_EXPR_N, - lhs, VM_OP_ARG_TYPE_VARIABLE, - name_lit_idx, VM_OP_ARG_TYPE_STRING | - VM_OP_ARG_TYPE_EMPTY, - arg_list, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (func_expr_ref, FUNC_EXPR_REF, - lhs, VM_OP_ARG_TYPE_VARIABLE, - idx1, VM_OP_ARG_TYPE_INTEGER_CONST, - idx2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_1 (retval, RETVAL, - ret_value, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_0 (ret, RET) - -VM_OP_3 (array_decl, ARRAY_DECL, - lhs, VM_OP_ARG_TYPE_VARIABLE, - list_1, VM_OP_ARG_TYPE_INTEGER_CONST, - list_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (obj_decl, OBJ_DECL, - lhs, VM_OP_ARG_TYPE_VARIABLE, - list_1, VM_OP_ARG_TYPE_INTEGER_CONST, - list_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (prop_getter, PROP_GETTER, - lhs, VM_OP_ARG_TYPE_VARIABLE, - obj, VM_OP_ARG_TYPE_VARIABLE, - prop, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (prop_setter, PROP_SETTER, - obj, VM_OP_ARG_TYPE_VARIABLE, - prop, VM_OP_ARG_TYPE_VARIABLE, - rhs, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (delete_var, DELETE_VAR, - lhs, VM_OP_ARG_TYPE_VARIABLE, - name, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (delete_prop, DELETE_PROP, - lhs, VM_OP_ARG_TYPE_VARIABLE, - base, VM_OP_ARG_TYPE_VARIABLE, - name, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (typeof, TYPEOF, - lhs, VM_OP_ARG_TYPE_VARIABLE, - obj, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (for_in, FOR_IN, - expr, VM_OP_ARG_TYPE_VARIABLE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (with, WITH, - expr, VM_OP_ARG_TYPE_VARIABLE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_2 (try_block, TRY_BLOCK, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_1 (throw_value, THROW_VALUE, - var, VM_OP_ARG_TYPE_VARIABLE) - - -VM_OP_3 (assignment, ASSIGNMENT, - var_left, VM_OP_ARG_TYPE_VARIABLE, - type_value_right, VM_OP_ARG_TYPE_TYPE_OF_NEXT, - value_right, VM_OP_ARG_TYPE_VARIABLE | - VM_OP_ARG_TYPE_STRING | - VM_OP_ARG_TYPE_NUMBER | - VM_OP_ARG_TYPE_INTEGER_CONST) - - -VM_OP_3 (b_shift_left, B_SHIFT_LEFT, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (b_shift_right, B_SHIFT_RIGHT, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (b_shift_uright, B_SHIFT_URIGHT, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (b_and, B_AND, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (b_or, B_OR, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (b_xor, B_XOR, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (b_not, B_NOT, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - - -VM_OP_2 (logical_not, LOGICAL_NOT, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - - -VM_OP_3 (equal_value, EQUAL_VALUE, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (not_equal_value, NOT_EQUAL_VALUE, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (equal_value_type, EQUAL_VALUE_TYPE, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (not_equal_value_type, NOT_EQUAL_VALUE_TYPE, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (less_than, LESS_THAN, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (greater_than, GREATER_THAN, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (less_or_equal_than, LESS_OR_EQUAL_THAN, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (greater_or_equal_than, GREATER_OR_EQUAL_THAN, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (instanceof, INSTANCEOF, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (in, IN, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (post_incr, POST_INCR, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (post_decr, POST_DECR, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (pre_incr, PRE_INCR, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (pre_decr, PRE_DECR, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (addition, ADDITION, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (substraction, SUBSTRACTION, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (division, DIVISION, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (multiplication, MULTIPLICATION, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_3 (remainder, REMAINDER, - dst, VM_OP_ARG_TYPE_VARIABLE, - var_left, VM_OP_ARG_TYPE_VARIABLE, - var_right, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (unary_minus, UNARY_MINUS, - dst, VM_OP_ARG_TYPE_VARIABLE, - var, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (unary_plus, UNARY_PLUS, - dst, VM_OP_ARG_TYPE_VARIABLE, - var, VM_OP_ARG_TYPE_VARIABLE) - -VM_OP_2 (jmp_up, JMP_UP, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_2 (jmp_down, JMP_DOWN, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_2 (jmp_break_continue, JMP_BREAK_CONTINUE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (is_true_jmp_up, IS_TRUE_JMP_UP, - value, VM_OP_ARG_TYPE_VARIABLE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (is_true_jmp_down, IS_TRUE_JMP_DOWN, - value, VM_OP_ARG_TYPE_VARIABLE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (is_false_jmp_up, IS_FALSE_JMP_UP, - value, VM_OP_ARG_TYPE_VARIABLE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (is_false_jmp_down, IS_FALSE_JMP_DOWN, - value, VM_OP_ARG_TYPE_VARIABLE, - oc_idx_1, VM_OP_ARG_TYPE_INTEGER_CONST, - oc_idx_2, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_1 (var_decl, VAR_DECL, - variable_name, VM_OP_ARG_TYPE_STRING) - -VM_OP_3 (reg_var_decl, REG_VAR_DECL, - tmp_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST, - local_var_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST, - arg_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST) - -VM_OP_3 (meta, META, - type, VM_OP_ARG_TYPE_INTEGER_CONST | - VM_OP_ARG_TYPE_TYPE_OF_NEXT, - data_1, VM_OP_ARG_TYPE_INTEGER_CONST | - VM_OP_ARG_TYPE_STRING | - VM_OP_ARG_TYPE_VARIABLE, - data_2, VM_OP_ARG_TYPE_INTEGER_CONST | - VM_OP_ARG_TYPE_VARIABLE) - -#undef VM_OP_0 -#undef VM_OP_1 -#undef VM_OP_2 -#undef VM_OP_3 diff --git a/jerry-core/vm/vm-stack.cpp b/jerry-core/vm/vm-stack.cpp index ac1cb46f5b..7fbd081eaf 100644 --- a/jerry-core/vm/vm-stack.cpp +++ b/jerry-core/vm/vm-stack.cpp @@ -1,4 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-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. @@ -13,8 +14,10 @@ * limitations under the License. */ -#include "ecma-globals.h" +#include "ecma-alloc.h" +#include "ecma-gc.h" #include "ecma-helpers.h" +#include "vm-defines.h" #include "vm-stack.h" /** \addtogroup vm Virtual machine @@ -25,287 +28,226 @@ */ /** - * Size of a stack frame's dynamic chunk - */ -#define VM_STACK_DYNAMIC_CHUNK_SIZE (mem_heap_recommend_allocation_size (sizeof (vm_stack_chunk_header_t) + \ - sizeof (ecma_value_t))) - -/** - * Number of value slots in a stack frame's dynamic chunk - */ -#define VM_STACK_SLOTS_IN_DYNAMIC_CHUNK ((VM_STACK_DYNAMIC_CHUNK_SIZE - sizeof (vm_stack_chunk_header_t)) / \ - sizeof (ecma_value_t)) - -/** - * The top-most stack frame - */ -vm_stack_frame_t* vm_stack_top_frame_p; - -/** - * Initialize stack - */ -void -vm_stack_init (void) -{ - vm_stack_top_frame_p = NULL; -} /* vm_stack_init */ - -/** - * Finalize stack - */ -void -vm_stack_finalize () -{ - JERRY_ASSERT (vm_stack_top_frame_p == NULL); -} /* vm_stack_finalize */ - -/** - * Get stack's top frame + * Abort (finalize) the current stack context, and remove it. * - * @return pointer to the top frame descriptor - */ -vm_stack_frame_t* -vm_stack_get_top_frame (void) -{ - return vm_stack_top_frame_p; -} /* vm_stack_get_top_frame */ - -/** - * Add the frame to stack + * @return new stack top */ -void -vm_stack_add_frame (vm_stack_frame_t *frame_p, /**< frame to initialize */ - ecma_value_t *regs_p, /**< array of register variables' values */ - uint32_t regs_num, /**< total number of register variables */ - uint32_t local_vars_regs_num, /**< number of register variables, - * used for local variables */ - uint32_t arg_regs_num, /**< number of register variables, - * used for arguments */ - ecma_collection_header_t *arguments_p) /**< collection of arguments - * (for case, their values - * are moved to registers) */ +ecma_value_t * +vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ + ecma_value_t *vm_stack_top_p) /**< current stack top */ { - frame_p->prev_frame_p = vm_stack_top_frame_p; - vm_stack_top_frame_p = frame_p; - - frame_p->top_chunk_p = NULL; - frame_p->dynamically_allocated_value_slots_p = frame_p->inlined_values; - frame_p->current_slot_index = 0; - frame_p->regs_p = regs_p; - frame_p->regs_number = regs_num; - - JERRY_ASSERT (regs_num >= VM_SPECIAL_REGS_NUMBER); - - for (uint32_t i = 0; i < regs_num - local_vars_regs_num - arg_regs_num; i++) - { - regs_p[i] = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); - } - - for (uint32_t i = regs_num - local_vars_regs_num - arg_regs_num; - i < regs_num; - i++) + switch (VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1])) { - regs_p[i] = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); - } - - if (arg_regs_num != 0) - { - ecma_collection_iterator_t args_iterator; - ecma_collection_iterator_init (&args_iterator, arguments_p); + case VM_CONTEXT_FINALLY_THROW: + case VM_CONTEXT_FINALLY_RETURN: + { + ecma_free_value (vm_stack_top_p[-2], true); - for (uint32_t i = regs_num - arg_regs_num; - i < regs_num && ecma_collection_iterator_next (&args_iterator); - i++) + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); + vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + break; + } + case VM_CONTEXT_FINALLY_JUMP: + case VM_CONTEXT_TRY: { - regs_p[i] = ecma_copy_value (*args_iterator.current_value_p, false); + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); + vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + break; } - } -} /* vm_stack_add_frame */ + case VM_CONTEXT_CATCH: + case VM_CONTEXT_WITH: + { + ecma_deref_object (frame_ctx_p->lex_env_p); + frame_ctx_p->lex_env_p = ecma_get_object_from_value (vm_stack_top_p[-2]); -/** - * Free the stack frame - * - * Note: - * the frame should be the top-most frame - */ -void -vm_stack_free_frame (vm_stack_frame_t *frame_p) /**< frame to initialize */ -{ - /* the frame should be the top-most frame */ - JERRY_ASSERT (vm_stack_top_frame_p == frame_p); + JERRY_ASSERT (PARSER_TRY_CONTEXT_STACK_ALLOCATION == PARSER_WITH_CONTEXT_STACK_ALLOCATION); - vm_stack_top_frame_p = frame_p->prev_frame_p; + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); + vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + break; + } + case VM_CONTEXT_FOR_IN: + { + mem_cpointer_t current = (uint16_t) vm_stack_top_p[-2]; - while (frame_p->top_chunk_p != NULL) - { - vm_stack_pop (frame_p); - } + while (current != MEM_CP_NULL) + { + ecma_collection_chunk_t *chunk_p = MEM_CP_GET_NON_NULL_POINTER (ecma_collection_chunk_t, + current); - for (uint32_t reg_index = 0; - reg_index < frame_p->regs_number; - reg_index++) - { - ecma_free_value (frame_p->regs_p[reg_index], false); - } -} /* vm_stack_free_frame */ + ecma_free_value (*(ecma_value_t *) chunk_p->data, true); -/** - * Get value of specified register variable - * - * @return ecma-value - */ -ecma_value_t -vm_stack_frame_get_reg_value (vm_stack_frame_t *frame_p, /**< frame */ - uint32_t reg_index) /**< index of register variable */ -{ - JERRY_ASSERT (reg_index >= VM_REG_FIRST && reg_index < VM_REG_FIRST + frame_p->regs_number); + current = chunk_p->next_chunk_cp; + ecma_dealloc_collection_chunk (chunk_p); + } - return frame_p->regs_p[reg_index - VM_REG_FIRST]; -} /* vm_stack_frame_get_reg_value */ + ecma_free_value (vm_stack_top_p[-3], true); -/** - * Set value of specified register variable - */ -void -vm_stack_frame_set_reg_value (vm_stack_frame_t *frame_p, /**< frame */ - uint32_t reg_index, /**< index of register variable */ - ecma_value_t value) /**< ecma-value */ -{ - JERRY_ASSERT (reg_index >= VM_REG_FIRST && reg_index < VM_REG_FIRST + frame_p->regs_number); + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); + vm_stack_top_p -= PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; + break; + } + default: + { + JERRY_UNREACHABLE (); + break; + } + } - frame_p->regs_p[reg_index - VM_REG_FIRST] = value; -} /* vm_stack_frame_set_reg_value */ + return vm_stack_top_p; +} /* vm_stack_context_abort */ /** - * Calculate number of value slots in the top-most chunk of the frame + * Decode branch offset. * - * @return number of value slots + * @return branch offset */ -static size_t -vm_stack_slots_in_top_chunk (vm_stack_frame_t *frame_p) /**< stack frame */ +static uint32_t +vm_decode_branch_offset (uint8_t *branch_offset_p, /**< start offset of byte code */ + uint32_t length) /**< length of the branch */ { - return ((frame_p->top_chunk_p == NULL) ? VM_STACK_FRAME_INLINED_VALUES_NUMBER : VM_STACK_SLOTS_IN_DYNAMIC_CHUNK); -} /* vm_stack_slots_in_top_chunk */ + uint32_t branch_offset = *branch_offset_p; -/** - * Longpath for vm_stack_push_value (for case current chunk may be doesn't have free slots) - */ -static void __attr_noinline___ -vm_stack_push_value_longpath (vm_stack_frame_t *frame_p) /**< stack frame */ -{ - JERRY_ASSERT (frame_p->current_slot_index >= JERRY_MIN (VM_STACK_FRAME_INLINED_VALUES_NUMBER, - VM_STACK_SLOTS_IN_DYNAMIC_CHUNK)); - - const size_t slots_in_top_chunk = vm_stack_slots_in_top_chunk (frame_p); + JERRY_ASSERT (length >= 1 && length <= 3); - if (frame_p->current_slot_index == slots_in_top_chunk) + switch (length) { - vm_stack_chunk_header_t *chunk_p; - chunk_p = (vm_stack_chunk_header_t *) mem_heap_alloc_block (VM_STACK_DYNAMIC_CHUNK_SIZE, - MEM_HEAP_ALLOC_SHORT_TERM); - - ECMA_SET_POINTER (chunk_p->prev_chunk_p, frame_p->top_chunk_p); - - frame_p->top_chunk_p = chunk_p; - frame_p->dynamically_allocated_value_slots_p = (ecma_value_t*) (frame_p->top_chunk_p + 1); - frame_p->current_slot_index = 0; - } -} /* vm_stack_push_value_longpath */ - -/** - * Push ecma-value to stack - */ -void -vm_stack_push_value (vm_stack_frame_t *frame_p, /**< stack frame */ - ecma_value_t value) /**< ecma-value */ -{ - frame_p->current_slot_index++; - - if (frame_p->current_slot_index >= JERRY_MIN (VM_STACK_FRAME_INLINED_VALUES_NUMBER, - VM_STACK_SLOTS_IN_DYNAMIC_CHUNK)) - { - vm_stack_push_value_longpath (frame_p); + case 3: + { + branch_offset <<= 8; + branch_offset |= *(branch_offset_p++); + /* FALLTHRU */ + } + case 2: + { + branch_offset <<= 8; + branch_offset |= *(branch_offset_p++); + break; + } } - JERRY_ASSERT (frame_p->current_slot_index < vm_stack_slots_in_top_chunk (frame_p)); - - frame_p->dynamically_allocated_value_slots_p[frame_p->current_slot_index] = value; -} /* vm_stack_push_value */ - -/** - * Get top value from stack - */ -ecma_value_t __attr_always_inline___ -vm_stack_top_value (vm_stack_frame_t *frame_p) /**< stack frame */ -{ - const size_t slots_in_top_chunk = vm_stack_slots_in_top_chunk (frame_p); - - JERRY_ASSERT (frame_p->current_slot_index < slots_in_top_chunk); - - return frame_p->dynamically_allocated_value_slots_p[frame_p->current_slot_index]; -} /* vm_stack_top_value */ + return branch_offset; +} /* vm_decode_branch_offset */ /** - * Longpath for vm_stack_pop (for case a dynamically allocated chunk needs to be deallocated) - */ -static void __attr_noinline___ -vm_stack_pop_longpath (vm_stack_frame_t *frame_p) /**< stack frame */ + * Find a finally up to the end position. + * + * @return true if 'finally' found, + * false otherwise + */ +bool +vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ + ecma_value_t **vm_stack_top_ref_p, /**< current stack top */ + vm_stack_context_type_t finally_type, /**< searching this finally */ + uint32_t search_limit) /**< search up-to this byte code */ { - JERRY_ASSERT (frame_p->current_slot_index == 0 && frame_p->top_chunk_p != NULL); + ecma_value_t *vm_stack_top_p = *vm_stack_top_ref_p; - vm_stack_chunk_header_t *chunk_to_free_p = frame_p->top_chunk_p; - frame_p->top_chunk_p = ECMA_GET_POINTER (vm_stack_chunk_header_t, - frame_p->top_chunk_p->prev_chunk_p); + JERRY_ASSERT (finally_type <= VM_CONTEXT_FINALLY_RETURN); - if (frame_p->top_chunk_p != NULL) + if (finally_type != VM_CONTEXT_FINALLY_JUMP) { - frame_p->dynamically_allocated_value_slots_p = (ecma_value_t*) (frame_p->top_chunk_p + 1); - frame_p->current_slot_index = (uint32_t) (VM_STACK_SLOTS_IN_DYNAMIC_CHUNK - 1u); - } - else - { - frame_p->dynamically_allocated_value_slots_p = frame_p->inlined_values; - frame_p->current_slot_index = (uint32_t) (VM_STACK_FRAME_INLINED_VALUES_NUMBER - 1u); + search_limit = 0xffffffffu; } - mem_heap_free_block (chunk_to_free_p); -} /* vm_stack_pop_longpath */ + while (frame_ctx_p->context_depth > 0) + { + vm_stack_context_type_t context_type; + uint32_t context_end = VM_GET_CONTEXT_END (vm_stack_top_p[-1]); -/** - * Pop top value from stack and free it - */ -void -vm_stack_pop (vm_stack_frame_t *frame_p) /**< stack frame */ -{ - JERRY_ASSERT (frame_p->current_slot_index < vm_stack_slots_in_top_chunk (frame_p)); + if (search_limit < context_end) + { + *vm_stack_top_ref_p = vm_stack_top_p; + return false; + } - ecma_value_t value = vm_stack_top_value (frame_p); + context_type = VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]); + if (context_type == VM_CONTEXT_TRY || context_type == VM_CONTEXT_CATCH) + { + uint8_t *byte_code_p; + uint32_t branch_offset_length; + uint32_t branch_offset; + + if (search_limit == context_end) + { + *vm_stack_top_ref_p = vm_stack_top_p; + return false; + } + + byte_code_p = frame_ctx_p->byte_code_start_p + VM_GET_CONTEXT_END (vm_stack_top_p[-1]); + + if (context_type == VM_CONTEXT_TRY) + { + JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE); + + if (byte_code_p[1] >= CBC_EXT_CATCH + && byte_code_p[1] <= CBC_EXT_CATCH_3) + { + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (byte_code_p[1]); + branch_offset = vm_decode_branch_offset (byte_code_p + 2, + branch_offset_length); + + if (finally_type == VM_CONTEXT_FINALLY_THROW) + { + branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); + + vm_stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_CATCH, branch_offset); + + byte_code_p += 2 + branch_offset_length; + frame_ctx_p->byte_code_p = byte_code_p; + + *vm_stack_top_ref_p = vm_stack_top_p; + return true; + } + + byte_code_p += branch_offset; + + if (*byte_code_p == CBC_CONTEXT_END) + { + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); + vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + continue; + } + } + } + else + { + ecma_deref_object (frame_ctx_p->lex_env_p); + frame_ctx_p->lex_env_p = ecma_get_object_from_value (vm_stack_top_p[-2]); + + if (byte_code_p[0] == CBC_CONTEXT_END) + { + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); + vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + continue; + } + } + + JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE); + JERRY_ASSERT (byte_code_p[1] >= CBC_EXT_FINALLY + && byte_code_p[1] <= CBC_EXT_FINALLY_3); + + branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (byte_code_p[1]); + branch_offset = vm_decode_branch_offset (byte_code_p + 2, + branch_offset_length); + + branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p); + + vm_stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset); + + byte_code_p += 2 + branch_offset_length; + frame_ctx_p->byte_code_p = byte_code_p; + + *vm_stack_top_ref_p = vm_stack_top_p; + return true; + } - if (unlikely (frame_p->current_slot_index == 0 - && frame_p->top_chunk_p != NULL)) - { - vm_stack_pop_longpath (frame_p); - } - else - { - frame_p->current_slot_index--; + vm_stack_top_p = vm_stack_context_abort (frame_ctx_p, vm_stack_top_p); } - ecma_free_value (value, true); -} /* vm_stack_pop */ - -/** - * Pop multiple top values from stack and free them - */ -void -vm_stack_pop_multiple (vm_stack_frame_t *frame_p, /**< stack frame */ - uint32_t number) /**< number of elements to pop */ -{ - for (uint32_t i = 0; i < number; i++) - { - vm_stack_pop (frame_p); - } -} /* vm_stack_pop_multiple */ + *vm_stack_top_ref_p = vm_stack_top_p; + return false; +} /* vm_stack_find_finally */ /** * @} diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index 609b607602..d39b9bb841 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -1,4 +1,5 @@ -/* Copyright 2015 Samsung Electronics Co., Ltd. +/* 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. @@ -36,7 +37,7 @@ */ typedef struct { - uint16_t prev_chunk_p; /**< previous chunk of same frame */ + uint16_t prev_chunk_p; /**< previous chunk of same frame */ } vm_stack_chunk_header_t; /** @@ -44,30 +45,31 @@ typedef struct */ typedef struct vm_stack_frame_t { - struct vm_stack_frame_t *prev_frame_p; /**< previous frame */ - vm_stack_chunk_header_t *top_chunk_p; /**< the top-most chunk of the frame */ - ecma_value_t *dynamically_allocated_value_slots_p; /**< pointer to dynamically allocated value slots - * in the top-most chunk */ - uint32_t current_slot_index; /**< index of first free slot in the top chunk */ - ecma_value_t inlined_values[VM_STACK_FRAME_INLINED_VALUES_NUMBER]; /**< place for values inlined into stack frame - * (instead of being placed on heap) */ - ecma_value_t *regs_p; /**< register variables */ - uint32_t regs_number; /**< number of register variables */ + struct vm_stack_frame_t *prev_frame_p; /**< previous frame */ + uint32_t regs_number; /**< number of register variables */ } vm_stack_frame_t; -extern void vm_stack_init (void); -extern void vm_stack_finalize (void); -extern vm_stack_frame_t * -vm_stack_get_top_frame (void); -extern void -vm_stack_add_frame (vm_stack_frame_t *, ecma_value_t *, uint32_t, uint32_t, uint32_t, ecma_collection_header_t *); -extern void vm_stack_free_frame (vm_stack_frame_t *); -extern ecma_value_t vm_stack_frame_get_reg_value (vm_stack_frame_t *, uint32_t); -extern void vm_stack_frame_set_reg_value (vm_stack_frame_t *, uint32_t, ecma_value_t); -extern void vm_stack_push_value (vm_stack_frame_t *, ecma_value_t); -extern ecma_value_t vm_stack_top_value (vm_stack_frame_t *); -extern void vm_stack_pop (vm_stack_frame_t *); -extern void vm_stack_pop_multiple (vm_stack_frame_t *, uint32_t); +#define VM_CREATE_CONTEXT(type, end_offset) ((ecma_value_t) ((type) | (end_offset) << 4)) +#define VM_GET_CONTEXT_TYPE(value) ((vm_stack_context_type_t) ((value) & 0xf)) +#define VM_GET_CONTEXT_END(value) ((value) >> 4) + +/** + * Context types for the vm stack. + */ +typedef enum +{ + VM_CONTEXT_FINALLY_JUMP, /**< finally context with a jump */ + VM_CONTEXT_FINALLY_THROW, /**< finally context with a throw */ + VM_CONTEXT_FINALLY_RETURN, /**< finally context with a return */ + VM_CONTEXT_TRY, /**< try context */ + VM_CONTEXT_CATCH, /**< catch context */ + VM_CONTEXT_WITH, /**< with context */ + VM_CONTEXT_FOR_IN, /**< for-in context */ +} vm_stack_context_type_t; + +extern ecma_value_t *vm_stack_context_abort (vm_frame_ctx_t *, ecma_value_t *); +extern bool vm_stack_find_finally (vm_frame_ctx_t *, ecma_value_t **, + vm_stack_context_type_t, uint32_t); /** * @} diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 8e28de0a1e..e61c1c0713 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* 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. @@ -13,407 +14,187 @@ * limitations under the License. */ -#include "bytecode-data.h" +#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-globals.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" -#include "jrt.h" -#include "jrt-libc-includes.h" -#include "mem-allocator.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 "opcodes.h" -#include "opcodes-ecma-support.h" + +#include + +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_executor Executor + * @{ + */ /** * Top (current) interpreter context */ -vm_frame_ctx_t *vm_top_context_p = NULL; - -static const opfunc __opfuncs[VM_OP__COUNT] = -{ -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - [ VM_OP_ ## opcode_name_uppercase ] = opfunc_ ## opcode_name, -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - [ VM_OP_ ## opcode_name_uppercase ] = opfunc_ ## opcode_name, -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - [ VM_OP_ ## opcode_name_uppercase ] = opfunc_ ## opcode_name, -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - [ VM_OP_ ## opcode_name_uppercase ] = opfunc_ ## opcode_name, - -#include "vm-opcodes.inc.h" -}; +static vm_frame_ctx_t *vm_top_context_p = NULL; -JERRY_STATIC_ASSERT (sizeof (vm_instr_t) <= 4); - -const bytecode_data_header_t *__program = NULL; - -#ifdef MEM_STATS -static const char *__op_names[VM_OP__COUNT] = -{ -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - #opcode_name, -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - #opcode_name, -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - #opcode_name, -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - #opcode_name, - -#include "vm-opcodes.inc.h" -}; +/** + * Direct call from eval; + */ +static bool is_direct_eval_form_call = false; -#define INTERP_MEM_PRINT_INDENTATION_STEP (5) -#define INTERP_MEM_PRINT_INDENTATION_MAX (125) -static uint32_t interp_mem_stats_print_indentation = 0; -static bool interp_mem_stats_enabled = false; +/** + * Program bytecode pointer + */ +static ecma_compiled_code_t *__program = NULL; -static void -interp_mem_stats_print_legend (void) -{ - if (likely (!interp_mem_stats_enabled)) - { - return; - } - - printf ("----- Legend of memory usage trace during interpretation -----\n\n" - "\tEntering block = beginning execution of initial (global) scope or function.\n\n" - "\tInformation on each value is formatted as following: (p -> n ( [+-]c, local l, peak g), where:\n" - "\t p - value just before starting of item's execution;\n" - "\t n - value just after end of item's execution;\n" - "\t [+-c] - difference between n and p;\n" - "\t l - temporary usage of memory during item's execution;\n" - "\t g - global peak of the value during program's execution.\n\n" - "\tChunks are items allocated in a pool." - " If there is no pool with a free chunk upon chunk allocation request,\n" - "\tthen new pool is allocated on the heap (that causes increase of number of allocated heap bytes).\n\n"); -} - -static void -interp_mem_get_stats (mem_heap_stats_t *out_heap_stats_p, - mem_pools_stats_t *out_pool_stats_p, - bool reset_peak_before, - bool reset_peak_after) +/** + * 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 (likely (!interp_mem_stats_enabled)) - { - return; - } - - /* Requesting to free as much memory as we currently can */ - ecma_try_to_give_back_some_memory (MEM_TRY_GIVE_MEMORY_BACK_SEVERITY_HIGH); - - if (reset_peak_before) + if (unlikely (ecma_is_value_undefined (object) || ecma_is_value_null (object))) { - mem_heap_stats_reset_peak (); - mem_pools_stats_reset_peak (); + ecma_object_t *error = ecma_new_standard_error (ECMA_ERROR_TYPE); + return ecma_make_throw_obj_completion_value (error); } - mem_heap_get_stats (out_heap_stats_p); - mem_pools_get_stats (out_pool_stats_p); - - if (reset_peak_after) - { - mem_heap_stats_reset_peak (); - mem_pools_stats_reset_peak (); - } -} + ecma_completion_value_t completion_value = ecma_make_empty_completion_value (); -static void -interp_mem_stats_context_enter (vm_frame_ctx_t *frame_ctx_p, - vm_instr_counter_t block_position) -{ - if (likely (!interp_mem_stats_enabled)) - { - return; - } - - const uint32_t indentation = JERRY_MIN (interp_mem_stats_print_indentation, - INTERP_MEM_PRINT_INDENTATION_MAX); - - char indent_prefix[INTERP_MEM_PRINT_INDENTATION_MAX + 2]; - memset (indent_prefix, ' ', sizeof (indent_prefix)); - indent_prefix[indentation] = '|'; - indent_prefix[indentation + 1] = '\0'; - - frame_ctx_p->context_peak_allocated_heap_bytes = 0; - frame_ctx_p->context_peak_waste_heap_bytes = 0; - frame_ctx_p->context_peak_pools_count = 0; - frame_ctx_p->context_peak_allocated_pool_chunks = 0; - - interp_mem_get_stats (&frame_ctx_p->heap_stats_context_enter, - &frame_ctx_p->pools_stats_context_enter, - false, false); - - printf ("\n%s--- Beginning interpretation of a block at position %u ---\n" - "%s Allocated heap bytes: %5u\n" - "%s Waste heap bytes: %5u\n" - "%s Pools: %5u\n" - "%s Allocated pool chunks: %5u\n\n", - indent_prefix, (uint32_t) block_position, - indent_prefix, (uint32_t) frame_ctx_p->heap_stats_context_enter.allocated_bytes, - indent_prefix, (uint32_t) frame_ctx_p->heap_stats_context_enter.waste_bytes, - indent_prefix, (uint32_t) frame_ctx_p->pools_stats_context_enter.pools_count, - indent_prefix, (uint32_t) frame_ctx_p->pools_stats_context_enter.allocated_chunks); -} - -static void -interp_mem_stats_context_exit (vm_frame_ctx_t *frame_ctx_p, - vm_instr_counter_t block_position) -{ - if (likely (!interp_mem_stats_enabled)) - { - return; - } - - const uint32_t indentation = JERRY_MIN (interp_mem_stats_print_indentation, - INTERP_MEM_PRINT_INDENTATION_MAX); - - char indent_prefix[INTERP_MEM_PRINT_INDENTATION_MAX + 2]; - memset (indent_prefix, ' ', sizeof (indent_prefix)); - indent_prefix[indentation] = '|'; - indent_prefix[indentation + 1] = '\0'; - - mem_heap_stats_t heap_stats_context_exit; - mem_pools_stats_t pools_stats_context_exit; - - interp_mem_get_stats (&heap_stats_context_exit, - &pools_stats_context_exit, - false, true); - - frame_ctx_p->context_peak_allocated_heap_bytes -= JERRY_MAX (frame_ctx_p->heap_stats_context_enter.allocated_bytes, - heap_stats_context_exit.allocated_bytes); - frame_ctx_p->context_peak_waste_heap_bytes -= JERRY_MAX (frame_ctx_p->heap_stats_context_enter.waste_bytes, - heap_stats_context_exit.waste_bytes); - frame_ctx_p->context_peak_pools_count -= JERRY_MAX (frame_ctx_p->pools_stats_context_enter.pools_count, - pools_stats_context_exit.pools_count); - frame_ctx_p->context_peak_allocated_pool_chunks -= JERRY_MAX (frame_ctx_p->pools_stats_context_enter.allocated_chunks, - pools_stats_context_exit.allocated_chunks); - - printf ("%sAllocated heap bytes in the context: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) frame_ctx_p->heap_stats_context_enter.allocated_bytes, - (uint32_t) heap_stats_context_exit.allocated_bytes, - (uint32_t) (heap_stats_context_exit.allocated_bytes - frame_ctx_p->heap_stats_context_enter.allocated_bytes), - (uint32_t) frame_ctx_p->context_peak_allocated_heap_bytes, - (uint32_t) heap_stats_context_exit.global_peak_allocated_bytes); - - printf ("%sWaste heap bytes in the context: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) frame_ctx_p->heap_stats_context_enter.waste_bytes, - (uint32_t) heap_stats_context_exit.waste_bytes, - (uint32_t) (heap_stats_context_exit.waste_bytes - frame_ctx_p->heap_stats_context_enter.waste_bytes), - (uint32_t) frame_ctx_p->context_peak_waste_heap_bytes, - (uint32_t) heap_stats_context_exit.global_peak_waste_bytes); - - printf ("%sPools count in the context: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) frame_ctx_p->pools_stats_context_enter.pools_count, - (uint32_t) pools_stats_context_exit.pools_count, - (uint32_t) (pools_stats_context_exit.pools_count - frame_ctx_p->pools_stats_context_enter.pools_count), - (uint32_t) frame_ctx_p->context_peak_pools_count, - (uint32_t) pools_stats_context_exit.global_peak_pools_count); - - printf ("%sAllocated pool chunks in the context: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) frame_ctx_p->pools_stats_context_enter.allocated_chunks, - (uint32_t) pools_stats_context_exit.allocated_chunks, - (uint32_t) (pools_stats_context_exit.allocated_chunks - - frame_ctx_p->pools_stats_context_enter.allocated_chunks), - (uint32_t) frame_ctx_p->context_peak_allocated_pool_chunks, - (uint32_t) pools_stats_context_exit.global_peak_allocated_chunks); - - printf ("\n%s--- End of interpretation of a block at position %u ---\n\n", - indent_prefix, (uint32_t) block_position); -} - -static void -interp_mem_stats_opcode_enter (const vm_instr_t *instrs_p, - vm_instr_counter_t instr_position, - mem_heap_stats_t *out_heap_stats_p, - mem_pools_stats_t *out_pools_stats_p) -{ - if (likely (!interp_mem_stats_enabled)) - { - return; - } + ECMA_TRY_CATCH (property_val, + ecma_op_to_string (property), + completion_value); - const uint32_t indentation = JERRY_MIN (interp_mem_stats_print_indentation, - INTERP_MEM_PRINT_INDENTATION_MAX); + ecma_string_t *property_p = ecma_get_string_from_value (property_val); - char indent_prefix[INTERP_MEM_PRINT_INDENTATION_MAX + 2]; - memset (indent_prefix, ' ', sizeof (indent_prefix)); - indent_prefix[indentation] = '|'; - indent_prefix[indentation + 1] = '\0'; + ecma_reference_t reference = ecma_make_reference (object, property_p, is_strict); - interp_mem_get_stats (out_heap_stats_p, - out_pools_stats_p, - true, false); + completion_value = ecma_op_get_value_object_base (reference); - vm_instr_t instr = vm_get_instr (instrs_p, instr_position); + ecma_free_reference (reference); - printf ("%s-- Opcode: %s (position %u) --\n", - indent_prefix, __op_names[instr.op_idx], (uint32_t) instr_position); + ECMA_FINALIZE (property_val); - interp_mem_stats_print_indentation += INTERP_MEM_PRINT_INDENTATION_STEP; -} + return completion_value; +} /* vm_op_get_value */ -static void -interp_mem_stats_opcode_exit (vm_frame_ctx_t *frame_ctx_p, - vm_instr_counter_t instr_position, - mem_heap_stats_t *heap_stats_before_p, - mem_pools_stats_t *pools_stats_before_p) +/** + * 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 */ { - if (likely (!interp_mem_stats_enabled)) - { - return; - } - - interp_mem_stats_print_indentation -= INTERP_MEM_PRINT_INDENTATION_STEP; - - const uint32_t indentation = JERRY_MIN (interp_mem_stats_print_indentation, - INTERP_MEM_PRINT_INDENTATION_MAX); - - char indent_prefix[INTERP_MEM_PRINT_INDENTATION_MAX + 2]; - memset (indent_prefix, ' ', sizeof (indent_prefix)); - indent_prefix[indentation] = '|'; - indent_prefix[indentation + 1] = '\0'; - - mem_heap_stats_t heap_stats_after; - mem_pools_stats_t pools_stats_after; + ecma_completion_value_t completion_value = ecma_make_empty_completion_value (); - interp_mem_get_stats (&heap_stats_after, - &pools_stats_after, - false, true); + ECMA_TRY_CATCH (obj_val, + ecma_op_to_object (object), + completion_value); - frame_ctx_p->context_peak_allocated_heap_bytes = JERRY_MAX (frame_ctx_p->context_peak_allocated_heap_bytes, - heap_stats_after.allocated_bytes); - frame_ctx_p->context_peak_waste_heap_bytes = JERRY_MAX (frame_ctx_p->context_peak_waste_heap_bytes, - heap_stats_after.waste_bytes); - frame_ctx_p->context_peak_pools_count = JERRY_MAX (frame_ctx_p->context_peak_pools_count, - pools_stats_after.pools_count); - frame_ctx_p->context_peak_allocated_pool_chunks = JERRY_MAX (frame_ctx_p->context_peak_allocated_pool_chunks, - pools_stats_after.allocated_chunks); + ECMA_TRY_CATCH (property_val, + ecma_op_to_string (property), + completion_value); - vm_instr_t instr = vm_get_instr (frame_ctx_p->bytecode_header_p->instrs_p, instr_position); + ecma_object_t *object_p = ecma_get_object_from_value (obj_val); + ecma_string_t *property_p = ecma_get_string_from_value (property_val); - printf ("%s Allocated heap bytes: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) heap_stats_before_p->allocated_bytes, - (uint32_t) heap_stats_after.allocated_bytes, - (uint32_t) (heap_stats_after.allocated_bytes - heap_stats_before_p->allocated_bytes), - (uint32_t) (heap_stats_after.peak_allocated_bytes - JERRY_MAX (heap_stats_before_p->allocated_bytes, - heap_stats_after.allocated_bytes)), - (uint32_t) heap_stats_after.global_peak_allocated_bytes); - - if (heap_stats_before_p->waste_bytes != heap_stats_after.waste_bytes) + if (ecma_is_lexical_environment (object_p)) { - printf ("%s Waste heap bytes: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) heap_stats_before_p->waste_bytes, - (uint32_t) heap_stats_after.waste_bytes, - (uint32_t) (heap_stats_after.waste_bytes - heap_stats_before_p->waste_bytes), - (uint32_t) (heap_stats_after.peak_waste_bytes - JERRY_MAX (heap_stats_before_p->waste_bytes, - heap_stats_after.waste_bytes)), - (uint32_t) heap_stats_after.global_peak_waste_bytes); + completion_value = ecma_op_put_value_lex_env_base (object_p, + property_p, + is_strict, + value); } - - if (pools_stats_before_p->pools_count != pools_stats_after.pools_count) + else { - printf ("%s Pools: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) pools_stats_before_p->pools_count, - (uint32_t) pools_stats_after.pools_count, - (uint32_t) (pools_stats_after.pools_count - pools_stats_before_p->pools_count), - (uint32_t) (pools_stats_after.peak_pools_count - JERRY_MAX (pools_stats_before_p->pools_count, - pools_stats_after.pools_count)), - (uint32_t) pools_stats_after.global_peak_pools_count); + completion_value = ecma_op_object_put (object_p, + property_p, + value, + is_strict); } - if (pools_stats_before_p->allocated_chunks != pools_stats_after.allocated_chunks) - { - printf ("%s Allocated pool chunks: %5u -> %5u (%+5d, local %5u, peak %5u)\n", - indent_prefix, - (uint32_t) pools_stats_before_p->allocated_chunks, - (uint32_t) pools_stats_after.allocated_chunks, - (uint32_t) (pools_stats_after.allocated_chunks - pools_stats_before_p->allocated_chunks), - (uint32_t) (pools_stats_after.peak_allocated_chunks - JERRY_MAX (pools_stats_before_p->allocated_chunks, - pools_stats_after.allocated_chunks)), - (uint32_t) pools_stats_after.global_peak_allocated_chunks); - } + ECMA_FINALIZE (property_val); + ECMA_FINALIZE (obj_val); - printf ("%s-- End of execution of opcode %s (position %u) --\n\n", - indent_prefix, __op_names[instr.op_idx], instr_position); -} -#endif /* MEM_STATS */ + return completion_value; +} /* vm_op_set_value */ /** * Initialize interpreter. */ void -vm_init (const bytecode_data_header_t *program_p, /**< pointer to byte-code data */ - bool dump_mem_stats) /** dump per-instruction memory usage change statistics */ +vm_init (ecma_compiled_code_t *program_p, /**< pointer to byte-code data */ + bool dump_mem_stats) /**< dump per-instruction memory usage change statistics */ { -#ifdef MEM_STATS - interp_mem_stats_enabled = dump_mem_stats; -#else /* MEM_STATS */ JERRY_ASSERT (!dump_mem_stats); -#endif /* !MEM_STATS */ JERRY_ASSERT (__program == NULL); - vm_stack_init (); - __program = program_p; } /* vm_init */ +#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg4, + /** - * Cleanup interpreter + * Decode table for opcodes. */ -void -vm_finalize (void) +uint32_t vm_decode_table[] = { - vm_stack_finalize (); + CBC_OPCODE_LIST +}; - __program = NULL; -} /* vm_finalize */ +/** + * 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_ASSERT (__program != NULL); - JERRY_ASSERT (vm_top_context_p == NULL); - -#ifdef MEM_STATS - interp_mem_stats_print_legend (); -#endif /* MEM_STATS */ + jerry_completion_code_t ret_code; - bool is_strict = __program->is_strict; - vm_instr_counter_t start_pos = 0; + 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_from_pos (__program, - start_pos, - ecma_make_object_value (glob_obj_p), - lex_env_p, - is_strict, - false, - NULL); - - jerry_completion_code_t ret_code; + ecma_completion_value_t completion = vm_run (__program, + ecma_make_object_value (glob_obj_p), + lex_env_p, + false, + NULL); if (ecma_is_completion_value_return (completion)) { - JERRY_ASSERT (ecma_is_value_undefined (ecma_get_completion_value_value (completion))); - ret_code = JERRY_COMPLETION_CODE_OK; } else @@ -428,8 +209,6 @@ vm_run_global (void) ecma_deref_object (glob_obj_p); ecma_deref_object (lex_env_p); - JERRY_ASSERT (vm_top_context_p == NULL); - return ret_code; } /* vm_run_global */ @@ -439,12 +218,10 @@ vm_run_global (void) * @return completion value */ ecma_completion_value_t -vm_run_eval (const bytecode_data_header_t *bytecode_data_p, /**< byte-code data header */ +vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ bool is_direct) /**< is eval called in direct mode? */ { - vm_instr_counter_t first_instr_index = 0u; - - bool is_strict = bytecode_data_p->is_strict; + 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; @@ -452,8 +229,9 @@ vm_run_eval (const bytecode_data_header_t *bytecode_data_p, /**< byte-code data /* ECMA-262 v5, 10.4.2 */ if (is_direct) { - this_binding = vm_get_this_binding (); - lex_env_p = vm_get_lex_env (); + 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 { @@ -469,13 +247,11 @@ vm_run_eval (const bytecode_data_header_t *bytecode_data_p, /**< byte-code data lex_env_p = strict_lex_env_p; } - ecma_completion_value_t completion = vm_run_from_pos (bytecode_data_p, - first_instr_index, - this_binding, - lex_env_p, - is_strict, - true, - NULL); + ecma_completion_value_t completion = vm_run (bytecode_data_p, + this_binding, + lex_env_p, + true, + NULL); if (ecma_is_completion_value_return (completion)) { @@ -488,256 +264,2272 @@ vm_run_eval (const bytecode_data_header_t *bytecode_data_p, /**< byte-code data ecma_deref_object (lex_env_p); ecma_free_value (this_binding, true); + ecma_bytecode_deref (bytecode_data_p); return completion; } /* vm_run_eval */ /** - * Run interpreter loop using specified context + * 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 * - * Note: - * The interpreter loop stops upon receiving completion value that is normal completion 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 If the received completion value is not meta completion value (ECMA_COMPLETION_TYPE_META), then - * the completion value is returned as is; - * Otherwise - the completion value is discarded and normal empty completion value is returned. + * @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, /**< interpreter context */ - vm_run_scope_t *run_scope_p) /**< current run scope, - * or NULL - if there is no active run scope */ +vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { - ecma_completion_value_t completion; + 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; + } -#ifdef MEM_STATS - mem_heap_stats_t heap_stats_before; - mem_pools_stats_t pools_stats_before; + 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; + } - memset (&heap_stats_before, 0, sizeof (heap_stats_before)); - memset (&pools_stats_before, 0, sizeof (pools_stats_before)); -#endif /* MEM_STATS */ + stack_top_p = frame_ctx_p->registers_p + register_end; + /* Outer loop for exception handling. */ while (true) { - do + /* Internal loop for byte code execution. */ + while (true) { - JERRY_ASSERT (run_scope_p == NULL - || (run_scope_p->start_oc <= frame_ctx_p->pos - && frame_ctx_p->pos <= run_scope_p->end_oc)); + 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; + } + } - const vm_instr_t *curr = &frame_ctx_p->bytecode_header_p->instrs_p[frame_ctx_p->pos]; + 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; + } + } + } + } -#ifdef MEM_STATS - const vm_instr_counter_t instr_pos = frame_ctx_p->pos; + 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; + /* FALLTHRU */ + } + case VM_OC_PUSH_TWO: + { + *(stack_top_p++) = left_value; + *(stack_top_p++) = right_value; + continue; + /* FALLTHRU */ + } + 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; + /* FALLTHRU */ + } + 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; + /* FALLTHRU */ + } + 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], + (uint8_t) right_value, + stack_top_p); + + /* 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; + } + } - interp_mem_stats_opcode_enter (frame_ctx_p->bytecode_header_p->instrs_p, - instr_pos, - &heap_stats_before, - &pools_stats_before); -#endif /* MEM_STATS */ + 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; + } + } - completion = __opfuncs[curr->op_idx] (*curr, frame_ctx_p); + if (free_flags & VM_FREE_LEFT_VALUE) + { + ecma_free_value (left_value, true); + } -#ifdef CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE - ecma_gc_run (); -#endif /* CONFIG_VM_RUN_GC_AFTER_EACH_OPCODE */ + if (free_flags & VM_FREE_RIGHT_VALUE) + { + ecma_free_value (right_value, true); + } + } +error: -#ifdef MEM_STATS - interp_mem_stats_opcode_exit (frame_ctx_p, - instr_pos, - &heap_stats_before, - &pools_stats_before); -#endif /* MEM_STATS */ + if (free_flags & VM_FREE_LEFT_VALUE) + { + ecma_free_value (left_value, true); + } - JERRY_ASSERT (!ecma_is_completion_value_normal (completion) - || ecma_is_completion_value_empty (completion)); + if (free_flags & VM_FREE_RIGHT_VALUE) + { + ecma_free_value (right_value, true); } - while (ecma_is_completion_value_normal (completion)); - if (ecma_is_completion_value_jump (completion)) + if (unlikely (ecma_is_completion_value_throw (last_completion_value))) { - vm_instr_counter_t target = ecma_get_jump_target_from_completion_value (completion); - - /* - * TODO: - * Implement instantiation of run scopes for global scope, functions and eval scope. - * Currently, correctness of jumps without run scope set is guaranteed through byte-code semantics. - */ - if (run_scope_p == NULL /* if no run scope set */ - || (target >= run_scope_p->start_oc /* or target is within the current run scope */ - && target <= run_scope_p->end_oc)) + 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++) { - frame_ctx_p->pos = target; + 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; } } - - if (ecma_is_completion_value_meta (completion)) + else { - completion = ecma_make_empty_completion_value (); + 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; + } } - return completion; + ecma_free_value (block_result, true); + return last_completion_value; } } /* vm_loop */ +#undef READ_LITERAL +#undef READ_LITERAL_INDEX + /** - * Run the code, starting from specified instruction position + * Execute code block. + * + * @return completion value */ -ecma_completion_value_t -vm_run_from_pos (const bytecode_data_header_t *header_p, /**< byte-code data header */ - vm_instr_counter_t start_pos, /**< position of starting instruction */ - ecma_value_t this_binding_value, /**< value of 'ThisBinding' */ - ecma_object_t *lex_env_p, /**< lexical environment to use */ - bool is_strict, /**< is the code is strict mode code (ECMA-262 v5, 10.1.1) */ - bool is_eval_code, /**< is the code is eval code (ECMA-262 v5, 10.1) */ - ecma_collection_header_t *arg_collection_p) /**< - * - collection of function call arguments, - * if arguments for the called function - * are placed on registers; - * - NULL - otherwise. - */ +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 */ { - ecma_completion_value_t completion = ecma_make_empty_completion_value (); + 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; - const vm_instr_t *instrs_p = header_p->instrs_p; - const vm_instr_t *curr = &instrs_p[start_pos]; - JERRY_ASSERT (curr->op_idx == VM_OP_REG_VAR_DECL); - - mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, header_p->declarations_cp); - for (uint16_t func_scope_index = 0; - func_scope_index < header_p->func_scopes_count && ecma_is_completion_value_empty (completion); - func_scope_index++) + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { - bytecode_data_header_t *func_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, - declarations_p[func_scope_index]); + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; - if (func_bc_header_p->instrs_p[0].op_idx == VM_OP_FUNC_DECL_N) - { - completion = vm_function_declaration (func_bc_header_p, - is_strict, - is_eval_code, - lex_env_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; } - lit_cpointer_t *lit_ids_p = (lit_cpointer_t *) (declarations_p + header_p->func_scopes_count); - for (uint16_t var_decl_index = 0; - var_decl_index < header_p->var_decls_count && ecma_is_completion_value_empty (completion); - var_decl_index++) + if (arg_list_len == 0) { - lit_cpointer_t lit_cp = lit_ids_p[var_decl_index]; + ecma_collection_header_t *arg_collection_p = (ecma_collection_header_t *) arg_p; + ecma_collection_iterator_t arguments_iterator; - if (lit_cp.packed_value != NOT_A_LITERAL.packed_value) + arg_list_len = arg_collection_p->unit_number; + if (arg_list_len > argument_end) { - ecma_string_t *var_name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); + arg_list_len = argument_end; + } - if (!ecma_op_has_binding (lex_env_p, var_name_string_p)) - { - const bool is_configurable_bindings = is_eval_code; + ecma_collection_iterator_init (&arguments_iterator, arg_collection_p); - completion = ecma_op_create_mutable_binding (lex_env_p, - var_name_string_p, - is_configurable_bindings); + for (uint32_t i = 0; i < arg_list_len; i++) + { + ecma_value_t value; - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); + 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 --; - /* Skipping SetMutableBinding as we have already checked that there were not - * any binding with specified name in current lexical environment - * and CreateMutableBinding sets the created binding's value to undefined */ - JERRY_ASSERT (ecma_is_completion_value_normal_simple_value (ecma_op_get_binding_value (lex_env_p, - var_name_string_p, - true), - ECMA_SIMPLE_VALUE_UNDEFINED)); - } + if (arg_list_len > argument_end) + { + arg_list_len = argument_end; + } - ecma_deref_ecma_string (var_name_string_p); + for (uint32_t i = 0; i < arg_list_len; i++) + { + frame_ctx_p->registers_p[i] = ecma_copy_value (src_p[i], true); } } - if (!ecma_is_completion_value_empty (completion)) + /* The arg_list_len contains the end of the copied arguments. + * Fill everything else with undefined. */ + if (register_end > arg_list_len) { - JERRY_ASSERT (ecma_is_completion_value_throw (completion)); + 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); + } } - else - { - const uint32_t tmp_regs_num = curr->data.reg_var_decl.tmp_regs_num; - const uint32_t local_var_regs_num = curr->data.reg_var_decl.local_var_regs_num; - const uint32_t arg_regs_num = curr->data.reg_var_decl.arg_regs_num; - uint32_t regs_num = VM_SPECIAL_REGS_NUMBER + tmp_regs_num + local_var_regs_num + arg_regs_num; + is_direct_eval_form_call = false; - MEM_DEFINE_LOCAL_ARRAY (regs, regs_num, ecma_value_t); + prev_context_p = vm_top_context_p; + vm_top_context_p = frame_ctx_p; - vm_frame_ctx_t frame_ctx; - frame_ctx.bytecode_header_p = header_p; - frame_ctx.pos = (vm_instr_counter_t) (start_pos + 1); - frame_ctx.lex_env_p = lex_env_p; - frame_ctx.is_strict = is_strict; - frame_ctx.is_eval_code = is_eval_code; - frame_ctx.is_call_in_direct_eval_form = false; - frame_ctx.tmp_num_p = ecma_alloc_number (); + vm_init_loop (frame_ctx_p); - vm_stack_add_frame (&frame_ctx.stack_frame, regs, regs_num, local_var_regs_num, arg_regs_num, arg_collection_p); - vm_stack_frame_set_reg_value (&frame_ctx.stack_frame, - VM_REG_SPECIAL_THIS_BINDING, - ecma_copy_value (this_binding_value, false)); + completion_value = vm_loop (frame_ctx_p); - vm_frame_ctx_t *prev_context_p = vm_top_context_p; - vm_top_context_p = &frame_ctx; + /* Free arguments and registers */ + for (uint32_t i = 0; i < register_end; i++) + { + ecma_free_value (frame_ctx_p->registers_p[i], true); + } -#ifdef MEM_STATS - interp_mem_stats_context_enter (&frame_ctx, start_pos); -#endif /* MEM_STATS */ + JERRY_ASSERT (ecma_is_completion_value_throw (completion_value) + || ecma_is_completion_value_return (completion_value)); - completion = vm_loop (&frame_ctx, NULL); + vm_top_context_p = prev_context_p; + return completion_value; +} /* vm_execute */ - JERRY_ASSERT (ecma_is_completion_value_throw (completion) - || ecma_is_completion_value_return (completion)); +#define INLINE_STACK_SIZE 16 - vm_top_context_p = prev_context_p; +/** + * 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]; - vm_stack_free_frame (&frame_ctx.stack_frame); + frame_ctx_p->registers_p = inline_stack; - ecma_dealloc_number (frame_ctx.tmp_num_p); + return vm_execute (frame_ctx_p, arg_p, arg_list_len); +} /* vm_run_with_inline_stack */ -#ifdef MEM_STATS - interp_mem_stats_context_exit (&frame_ctx, start_pos); -#endif /* MEM_STATS */ +/** + * 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); - MEM_FINALIZE_LOCAL_ARRAY (regs); - } + ecma_value_t *stack = (ecma_value_t *) alloca (size); - return completion; -} /* vm_run_from_pos */ + frame_ctx_p->registers_p = stack; + + return vm_execute (frame_ctx_p, arg_p, arg_list_len); +} /* vm_run_with_alloca */ /** - * Get specified instruction from the program. + * Run the code. + * + * @return completion value */ -vm_instr_t -vm_get_instr (const vm_instr_t *instrs_p, /**< byte-code array */ - vm_instr_counter_t counter) /**< instruction counter */ +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) */ + ecma_collection_header_t *arg_collection_p) /**< arguments list */ { - return instrs_p[ counter ]; -} /* vm_get_instr */ + 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 + sizeof (cbc_uint16_arguments_t)); + + literal_p = (lit_cpointer_t *) byte_p; + 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 + sizeof (cbc_uint8_arguments_t)); + + literal_p = (lit_cpointer_t *) byte_p; + 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; + + ecma_length_t arg_list_len = 0; + + if (arg_collection_p == NULL) + { + arg_list_len = 1; + } + + if (call_stack_size <= INLINE_STACK_SIZE) + { + return vm_run_with_inline_stack (&frame_ctx, + arg_collection_p, + arg_list_len); + } + else + { + return vm_run_with_alloca (&frame_ctx, + arg_collection_p, + arg_list_len, + call_stack_size); + } +} /* vm_run */ /** - * Get arguments number, encoded in specified reg_var_decl instruction + * Run the code. * - * @return value of "arguments number" reg_var_decl's parameter + * @return completion value */ -uint8_t -vm_get_scope_args_num (const bytecode_data_header_t *bytecode_header_p, /**< byte-code data */ - vm_instr_counter_t reg_var_decl_oc) /**< position of reg_var_decl instruction */ +ecma_completion_value_t +vm_run_array_args (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 */ { - const vm_instr_t *instrs_p = bytecode_header_p->instrs_p; - const vm_instr_t *reg_var_decl_instr_p = &instrs_p[reg_var_decl_oc]; - JERRY_ASSERT (reg_var_decl_instr_p->op_idx == VM_OP_REG_VAR_DECL); + 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); + } - return reg_var_decl_instr_p->data.reg_var_decl.arg_regs_num; -} /* vm_get_scope_args_num */ + 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_array_args */ /** * Check whether currently executed code is strict mode code @@ -750,7 +2542,7 @@ vm_is_strict_mode (void) { JERRY_ASSERT (vm_top_context_p != NULL); - return vm_top_context_p->is_strict; + return vm_top_context_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE; } /* vm_is_strict_mode */ /** @@ -768,46 +2560,10 @@ vm_is_strict_mode (void) bool vm_is_direct_eval_form_call (void) { - if (vm_top_context_p != NULL) - { - return vm_top_context_p->is_call_in_direct_eval_form; - } - else - { - /* - * There is no any interpreter context, so call is performed not from a script. - * This implies that the call is indirect. - */ - return false; - } + return is_direct_eval_form_call; } /* vm_is_direct_eval_form_call */ /** - * Get this binding of current execution context - * - * @return ecma-value + * @} + * @} */ -ecma_value_t -vm_get_this_binding (void) -{ - JERRY_ASSERT (vm_top_context_p != NULL); - - return ecma_copy_value (vm_stack_frame_get_reg_value (&vm_top_context_p->stack_frame, - VM_REG_SPECIAL_THIS_BINDING), - true); -} /* vm_get_this_binding */ - -/** - * Get top lexical environment (variable environment) of current execution context - * - * @return lexical environment - */ -ecma_object_t* -vm_get_lex_env (void) -{ - JERRY_ASSERT (vm_top_context_p != NULL); - - ecma_ref_object (vm_top_context_p->lex_env_p); - - return vm_top_context_p->lex_env_p; -} /* vm_get_lex_env */ diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 6fda72ef3c..dcaa4237e6 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -1,4 +1,5 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. +/* Copyright 2014-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. @@ -18,25 +19,196 @@ #include "ecma-globals.h" #include "jrt.h" -#include "opcodes.h" +#include "vm-defines.h" -extern void vm_init (const bytecode_data_header_t *, bool); +/** \addtogroup vm Virtual machine + * @{ + * + * \addtogroup vm_executor Executor + * @{ + */ + +#define VM_OC_GET_DATA_SHIFT 24 +#define VM_OC_GET_DATA_MASK 0x1f +#define VM_OC_GET_DATA_CREATE_ID(V) \ + (((V) & VM_OC_GET_DATA_MASK) << VM_OC_GET_DATA_SHIFT) +#define VM_OC_GET_DATA_GET_ID(O) \ + (((O) >> VM_OC_GET_DATA_SHIFT) & VM_OC_GET_DATA_MASK) + +/** + * Argument getters that are part of the opcodes. + */ +typedef enum +{ + VM_OC_GET_NONE = VM_OC_GET_DATA_CREATE_ID (0), /**< do nothing */ + VM_OC_GET_STACK = VM_OC_GET_DATA_CREATE_ID (1), /**< pop one elemnet from the stack */ + VM_OC_GET_STACK_STACK = VM_OC_GET_DATA_CREATE_ID (2), /**< pop two elemnets from the stack */ + VM_OC_GET_BYTE = VM_OC_GET_DATA_CREATE_ID (3), /**< read a byte */ + + VM_OC_GET_LITERAL = VM_OC_GET_DATA_CREATE_ID (4), /**< resolve literal */ + VM_OC_GET_STACK_LITERAL = VM_OC_GET_DATA_CREATE_ID (5), /**< pop one elemnet from the stack and resolve a literal*/ + VM_OC_GET_LITERAL_BYTE = VM_OC_GET_DATA_CREATE_ID (6), /**< pop one elemnet from stack and read a byte */ + VM_OC_GET_LITERAL_LITERAL = VM_OC_GET_DATA_CREATE_ID (7), /**< resolve two literals */ + VM_OC_GET_THIS_LITERAL = VM_OC_GET_DATA_CREATE_ID (8), /**< get this and resolve a literal */ +} vm_oc_get_types; + +#define VM_OC_GROUP_MASK 0xff +#define VM_OC_GROUP_GET_INDEX(O) \ + ((O) & VM_OC_GROUP_MASK) + +/** + * Opcodes. + */ +typedef enum +{ + VM_OC_NONE, /**< do nothing */ + VM_OC_POP, /**< pop from stack */ + VM_OC_POP_BLOCK, /**< pop block */ + VM_OC_PUSH, /**< push one element */ + VM_OC_PUSH_TWO, /**< push two elements onto the stack */ + VM_OC_PUSH_THREE, /**< push three elements onto the stack */ + VM_OC_PUSH_UNDEFINED, /**< push undefined value */ + VM_OC_PUSH_TRUE, /**< push true value */ + VM_OC_PUSH_FALSE, /**< push false value */ + VM_OC_PUSH_NULL, /**< push null value */ + VM_OC_PUSH_THIS, /**< push this */ + VM_OC_PUSH_NUMBER, /**< push number */ + VM_OC_PUSH_OBJECT, /**< push object */ + VM_OC_SET_PROPERTY, /**< set property */ + VM_OC_SET_GETTER, /**< set getter */ + VM_OC_SET_SETTER, /**< set setter */ + VM_OC_PUSH_UNDEFINED_BASE, /**< push undefined base */ + VM_OC_PUSH_ARRAY, /**< push array */ + VM_OC_PUSH_ELISON, /**< push elison */ + VM_OC_APPEND_ARRAY, /**< append array */ + VM_OC_IDENT_REFERENCE, /**< ident reference */ + VM_OC_PROP_REFERENCE, /**< prop reference */ + VM_OC_PROP_GET, /**< prop get */ + + /* These eight opcodes must be in this order. */ + VM_OC_PROP_PRE_INCR, /**< prefix increment of a property */ + VM_OC_PRE_INCR, /**< prefix increment */ + VM_OC_PROP_PRE_DECR, /**< prop prefix decrement of a property */ + VM_OC_PRE_DECR, /**< prefix decrement */ + VM_OC_PROP_POST_INCR, /**< prop postfix increment of a property */ + VM_OC_POST_INCR, /**< postfix increment */ + VM_OC_PROP_POST_DECR, /**< prop postfix decrement of a property */ + VM_OC_POST_DECR, /**< postfix decrement */ + + VM_OC_PROP_DELETE, /**< delete property */ + VM_OC_DELETE, /**< delete */ + + VM_OC_ASSIGN, /**< assign */ + VM_OC_ASSIGN_PROP, /**< assign property */ + VM_OC_ASSIGN_PROP_THIS, /**< assign prop this */ + + VM_OC_RET, /**< return */ + VM_OC_THROW, /**< throw */ + VM_OC_THROW_REFERENCE_ERROR, /**< throw reference error */ + + /* The PROP forms must get the highest opcodes. */ + VM_OC_EVAL, /**< eval */ + VM_OC_CALL_N, /**< call n */ + VM_OC_CALL, /**< call */ + VM_OC_CALL_PROP_N, /**< call property n */ + VM_OC_CALL_PROP, /**< call property */ + + VM_OC_NEW_N, /**< new n */ + VM_OC_NEW, /**< new */ + + VM_OC_JUMP, /**< jump */ + VM_OC_BRANCH_IF_STRICT_EQUAL, /**< branch if stric equal */ + + /* These four opcodes must be in this order. */ + VM_OC_BRANCH_IF_TRUE, /**< branch if true */ + VM_OC_BRANCH_IF_FALSE, /**< branch if false */ + VM_OC_BRANCH_IF_LOGICAL_TRUE, /**< branch if logical true */ + VM_OC_BRANCH_IF_LOGICAL_FALSE, /**< branch if logical false */ + + VM_OC_PLUS, /**< unary plus */ + VM_OC_MINUS, /**< unary minus */ + VM_OC_NOT, /**< not */ + VM_OC_BIT_NOT, /**< bitwise not */ + VM_OC_VOID, /**< void */ + VM_OC_TYPEOF_IDENT, /**< typeof identifier */ + VM_OC_TYPEOF, /**< typeof */ + + VM_OC_ADD, /**< binary add */ + VM_OC_SUB, /**< binary sub */ + VM_OC_MUL, /**< mul */ + VM_OC_DIV, /**< div */ + VM_OC_MOD, /**< mod */ + + VM_OC_EQUAL, /**< equal */ + VM_OC_NOT_EQUAL, /**< not equal */ + VM_OC_STRICT_EQUAL, /**< strict equal */ + VM_OC_STRICT_NOT_EQUAL, /**< strict not equal */ + VM_OC_LESS, /**< less */ + VM_OC_GREATER, /**< greater */ + VM_OC_LESS_EQUAL, /**< less equal */ + VM_OC_GREATER_EQUAL, /**< greater equal */ + VM_OC_IN, /**< in */ + VM_OC_INSTANCEOF, /**< instanceof */ + + VM_OC_BIT_OR, /**< bitwise or */ + VM_OC_BIT_XOR, /**< bitwise xor */ + VM_OC_BIT_AND, /**< bitwise and */ + VM_OC_LEFT_SHIFT, /**< left shift */ + VM_OC_RIGHT_SHIFT, /**< right shift */ + VM_OC_UNS_RIGHT_SHIFT, /**< unsigned right shift */ + + VM_OC_WITH, /**< with */ + VM_OC_FOR_IN_CREATE_CONTEXT, /**< for in create context */ + VM_OC_FOR_IN_GET_NEXT, /**< get next */ + VM_OC_FOR_IN_HAS_NEXT, /**< has next */ + VM_OC_TRY, /**< try */ + VM_OC_CATCH, /**< catch */ + VM_OC_FINALLY, /**< finally */ + VM_OC_CONTEXT_END, /**< context end */ + VM_OC_JUMP_AND_EXIT_CONTEXT, /**< jump and exit context */ +} vm_oc_types; + +#define VM_OC_PUT_DATA_SHIFT 12 +#define VM_OC_PUT_DATA_MASK 0xf +#define VM_OC_PUT_DATA_CREATE_FLAG(V) \ + (((V) & VM_OC_PUT_DATA_MASK) << VM_OC_PUT_DATA_SHIFT) + +/** + * Result writers that are part of the opcodes. + */ +typedef enum +{ + VM_OC_PUT_IDENT = VM_OC_PUT_DATA_CREATE_FLAG (0x1), + VM_OC_PUT_REFERENCE = VM_OC_PUT_DATA_CREATE_FLAG (0x2), + VM_OC_PUT_STACK = VM_OC_PUT_DATA_CREATE_FLAG (0x4), + VM_OC_PUT_BLOCK = VM_OC_PUT_DATA_CREATE_FLAG (0x8), +} vm_oc_put_types; + +extern void vm_init (ecma_compiled_code_t *, bool); extern void vm_finalize (void); extern jerry_completion_code_t vm_run_global (void); -extern ecma_completion_value_t vm_run_eval (const bytecode_data_header_t *, bool); +extern ecma_completion_value_t vm_run_eval (ecma_compiled_code_t *, bool); -extern ecma_completion_value_t vm_loop (vm_frame_ctx_t *, vm_run_scope_t *); -extern ecma_completion_value_t vm_run_from_pos (const bytecode_data_header_t *, vm_instr_counter_t, - ecma_value_t, ecma_object_t *, bool, bool, ecma_collection_header_t *); +extern ecma_completion_value_t vm_loop (vm_frame_ctx_t *); +extern ecma_completion_value_t vm_run (const ecma_compiled_code_t *, + ecma_value_t, + ecma_object_t *, + bool, + ecma_collection_header_t *); -extern vm_instr_t vm_get_instr (const vm_instr_t *, vm_instr_counter_t); -extern uint8_t vm_get_scope_args_num (const bytecode_data_header_t *, vm_instr_counter_t); +extern ecma_completion_value_t vm_run_array_args (const ecma_compiled_code_t *, + ecma_value_t, + ecma_object_t *, + bool, + const ecma_value_t *, + ecma_length_t); extern bool vm_is_strict_mode (void); extern bool vm_is_direct_eval_form_call (void); -extern ecma_value_t vm_get_this_binding (void); -extern ecma_object_t *vm_get_lex_env (void); - -#endif /* VM_H */ +/** + * @} + * @} + */ +#endif /* !VM_H */ diff --git a/tests/jerry/arithmetics-bignums.js b/tests/jerry/arithmetics-bignums.js index 29f4c099ff..78f0f53f7d 100644 --- a/tests/jerry/arithmetics-bignums.js +++ b/tests/jerry/arithmetics-bignums.js @@ -1,4 +1,4 @@ -// Copyright 2014 Samsung Electronics Co., Ltd. +// Copyright 2014-2016 Samsung Electronics Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,6 @@ assert(big == 2147483648); // overflow on 32bit numbers big++; assert(big == 2147483649); // overflow on 32bit numbers -assert ((1152921504606846900).toString() === "1152921504606847000") +assert ((1152921504606846976).toString() === "1152921504606847000") assert (1.797693134862315808e+308 === Infinity); diff --git a/tests/jerry/regression-test-issue-245.js b/tests/jerry/regression-test-issue-245.js index 907d5d6335..001ce1487a 100644 --- a/tests/jerry/regression-test-issue-245.js +++ b/tests/jerry/regression-test-issue-245.js @@ -1,5 +1,5 @@ -// Copyright 2015 Samsung Electronics Co., Ltd. -// Copyright 2015 University of Szeged. +// 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. @@ -15,7 +15,7 @@ try { - v_0 = /N(?![^6](?:.)|(?!C[^k-o]*|p){0,}|H)|\\xDF\\db{0,}|i\\0?)/; + v_0 = new RegExp("N(?![^6](?:.)|(?!C[^k-o]*|p){0,}|H)|\\\\xDF\\db{0,}|i\\\\0?)"); assert (false); } catch (e) diff --git a/tests/unit/test-parser.cpp b/tests/unit/test-parser.cpp deleted file mode 100644 index 328e93ddae..0000000000 --- a/tests/unit/test-parser.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* Copyright 2014-2015 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bytecode-data.h" -#include "mem-allocator.h" -#include "opcodes.h" -#include "parser.h" -#include "test-common.h" - -static bool -instrs_equal (const vm_instr_t *instrs1, vm_instr_t *instrs2, uint16_t size) -{ - static const uint8_t instr_fields_num[] = - { -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - 1, -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg1, arg1_type) \ - 2, -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type) \ - 3, -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg1, arg1_type, arg2, arg2_type, arg3, arg3_type) \ - 4, - -#include "vm-opcodes.inc.h" - }; - - uint16_t i; - for (i = 0; i < size; i++) - { - if (memcmp (&instrs1[i], &instrs2[i], instr_fields_num[instrs1[i].op_idx] * sizeof (vm_idx_t)) != 0) - { - return false; - } - } - - return true; -} - -#define VM_OP_0(opcode_name, opcode_name_uppercase) \ - static vm_instr_t __attr_unused___ getop_##opcode_name (void) \ - { \ - vm_instr_t instr; \ - instr.op_idx = VM_OP_##opcode_name_uppercase; \ - instr.data.raw_args[0] = VM_IDX_EMPTY; \ - instr.data.raw_args[1] = VM_IDX_EMPTY; \ - instr.data.raw_args[2] = VM_IDX_EMPTY; \ - return instr; \ - } -#define VM_OP_1(opcode_name, opcode_name_uppercase, arg_1, arg1_type) \ - static vm_instr_t __attr_unused___ getop_##opcode_name (vm_idx_t arg1_v) \ - { \ - vm_instr_t instr; \ - instr.op_idx = VM_OP_##opcode_name_uppercase; \ - instr.data.raw_args[0] = arg1_v; \ - instr.data.raw_args[1] = VM_IDX_EMPTY; \ - instr.data.raw_args[2] = VM_IDX_EMPTY; \ - return instr; \ - } -#define VM_OP_2(opcode_name, opcode_name_uppercase, arg_1, arg1_type, arg_2, arg2_type) \ - static vm_instr_t __attr_unused___ getop_##opcode_name (vm_idx_t arg1_v, vm_idx_t arg2_v) \ - { \ - vm_instr_t instr; \ - instr.op_idx = VM_OP_##opcode_name_uppercase; \ - instr.data.raw_args[0] = arg1_v; \ - instr.data.raw_args[1] = arg2_v; \ - instr.data.raw_args[2] = VM_IDX_EMPTY; \ - return instr; \ - } -#define VM_OP_3(opcode_name, opcode_name_uppercase, arg_1, arg1_type, arg_2, arg2_type, arg3_name, arg3_type) \ - static vm_instr_t __attr_unused___ getop_##opcode_name (vm_idx_t arg1_v, vm_idx_t arg2_v, vm_idx_t arg3_v) \ - { \ - vm_instr_t instr; \ - instr.op_idx = VM_OP_##opcode_name_uppercase; \ - instr.data.raw_args[0] = arg1_v; \ - instr.data.raw_args[1] = arg2_v; \ - instr.data.raw_args[2] = arg3_v; \ - return instr; \ - } - -#include "vm-opcodes.inc.h" - -/** - * Unit test's main function. - */ -int -main (int __attr_unused___ argc, - char __attr_unused___ **argv) -{ - TEST_INIT (); - - const bytecode_data_header_t *bytecode_data_p; - jsp_status_t parse_status; - - mem_init (); - - // #1 - char program1[] = "a=1;var a;"; - - lit_init (); - parser_set_show_instrs (true); - parse_status = parser_parse_script ((jerry_api_char_t *) program1, strlen (program1), &bytecode_data_p); - - JERRY_ASSERT (parse_status == JSP_STATUS_OK && bytecode_data_p != NULL); - - vm_instr_t instrs[] = - { - getop_reg_var_decl (1u, 0u, 0u), - getop_assignment (0, 1, 1), // a = 1 (SMALLINT); - getop_ret () // return; - }; - - JERRY_ASSERT (instrs_equal (bytecode_data_p->instrs_p, instrs, 3)); - - lit_finalize (); - bc_finalize (); - - // #2 - char program2[] = "var var;"; - - lit_init (); - parser_set_show_instrs (true); - parse_status = parser_parse_script ((jerry_api_char_t *) program2, strlen (program2), &bytecode_data_p); - - JERRY_ASSERT (parse_status == JSP_STATUS_SYNTAX_ERROR && bytecode_data_p == NULL); - - lit_finalize (); - bc_finalize (); - - mem_finalize (false); - - return 0; -} /* main */ diff --git a/tools/runners/run-test-pass.sh b/tools/runners/run-test-pass.sh index af18950364..0d40ccac63 100755 --- a/tools/runners/run-test-pass.sh +++ b/tools/runners/run-test-pass.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2014-2015 Samsung Electronics Co., Ltd. +# Copyright 2014-2016 Samsung Electronics Co., Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -TIMEOUT=${TIMEOUT:=30} +TIMEOUT=${TIMEOUT:=5} START_DIR=`pwd`