From b988fe2fce7d1d2fad512c8f742931ecc36cbcc4 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Tue, 23 Jun 2015 19:03:21 +0300 Subject: [PATCH] Introduce for-in opcode; implement handler for the opcode. JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- jerry-core/ecma/base/ecma-globals.h | 5 + jerry-core/ecma/base/ecma-lcache.cpp | 2 +- jerry-core/vm/opcodes-for-in.cpp | 277 +++++++++++++++++++++++++++ jerry-core/vm/opcodes.cpp | 1 + jerry-core/vm/opcodes.h | 8 +- tests/unit/test-parser.cpp | 6 +- 6 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 jerry-core/vm/opcodes-for-in.cpp diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 072ddeb1f..f5c3f3041 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -793,6 +793,11 @@ typedef uint32_t ecma_magic_string_ex_id_t; */ typedef uint8_t ecma_string_hash_t; +/** + * Length of string hash, in bits + */ +#define ECMA_STRING_HASH_BITS (sizeof (ecma_string_hash_t) * JERRY_BITSINBYTE) + /** * Number of string's last characters to use for hash calculation */ diff --git a/jerry-core/ecma/base/ecma-lcache.cpp b/jerry-core/ecma/base/ecma-lcache.cpp index 7b63da691..8534dc871 100644 --- a/jerry-core/ecma/base/ecma-lcache.cpp +++ b/jerry-core/ecma/base/ecma-lcache.cpp @@ -50,7 +50,7 @@ JERRY_STATIC_ASSERT (sizeof (ecma_lcache_hash_entry_t) == sizeof (uint64_t)); /** * LCache hash value length, in bits */ -#define ECMA_LCACHE_HASH_BITS (sizeof (ecma_string_hash_t) * JERRY_BITSINBYTE) +#define ECMA_LCACHE_HASH_BITS (ECMA_STRING_HASH_BITS) /** * Number of rows in LCache's hash table diff --git a/jerry-core/vm/opcodes-for-in.cpp b/jerry-core/vm/opcodes-for-in.cpp new file mode 100644 index 000000000..f495f809f --- /dev/null +++ b/jerry-core/vm/opcodes-for-in.cpp @@ -0,0 +1,277 @@ +/* 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 "opcodes.h" +#include "opcodes-ecma-support.h" + +/** + * Enumerate properties and construct collection with their + * names for further iteration in for-in opcode handler. + * + * See also: + * ECMA-262 v5, 12.6.4 + * + * @return header of constructed strings collection (should be freed with ecma_free_values_collection), + * or NULL - if there are no properties to enumerate in for-in. + */ +static ecma_collection_header_t * +vm_helper_for_in_enumerate_properties_names (ecma_object_t *obj_p) /**< starting object - result of ToObject + * conversion (ECMA-262 v5, 12.6.4, step 4) */ +{ + const size_t bitmap_row_size = sizeof (uint32_t) * JERRY_BITSINBYTE; + uint32_t names_hashes_bitmap[(1u << ECMA_STRING_HASH_BITS) / bitmap_row_size]; + + memset (names_hashes_bitmap, 0, sizeof (names_hashes_bitmap)); + + ecma_length_t all_properties_count = 0; + + /* First pass: counting properties */ + for (ecma_object_t *prototype_chain_iter_p = obj_p; + prototype_chain_iter_p != NULL; + prototype_chain_iter_p = ecma_get_object_prototype (prototype_chain_iter_p)) + { + for (ecma_property_t *prop_iter_p = ecma_get_property_list (prototype_chain_iter_p); + prop_iter_p != NULL; + prop_iter_p = ECMA_GET_POINTER (ecma_property_t, prop_iter_p->next_property_p)) + { + if (prop_iter_p->type == ECMA_PROPERTY_NAMEDDATA + || prop_iter_p->type == ECMA_PROPERTY_NAMEDACCESSOR) + { + all_properties_count++; + } + else + { + JERRY_ASSERT (prop_iter_p->type == ECMA_PROPERTY_INTERNAL); + } + } + } + + if (all_properties_count == 0) + { + return NULL; + } + + ecma_collection_header_t *ret_p = NULL; + + /* Second pass: collecting properties names */ + MEM_DEFINE_LOCAL_ARRAY (names_p, all_properties_count, ecma_string_t*); + + ecma_length_t enumerated_properties_count = 0; + ecma_length_t non_enumerated_properties_count = 0; + + for (ecma_object_t *prototype_chain_iter_p = obj_p; + prototype_chain_iter_p != NULL; + prototype_chain_iter_p = ecma_get_object_prototype (prototype_chain_iter_p)) + { + for (ecma_property_t *prop_iter_p = ecma_get_property_list (prototype_chain_iter_p); + prop_iter_p != NULL; + prop_iter_p = ECMA_GET_POINTER (ecma_property_t, prop_iter_p->next_property_p)) + { + if (prop_iter_p->type == ECMA_PROPERTY_NAMEDDATA + || prop_iter_p->type == ECMA_PROPERTY_NAMEDACCESSOR) + { + bool is_enumerated; + + ecma_string_t *prop_name_p; + + if (prop_iter_p->type == ECMA_PROPERTY_NAMEDDATA) + { + prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_iter_p->u.named_data_property.name_p); + } + else + { + prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_iter_p->u.named_accessor_property.name_p); + } + + ecma_string_hash_t hash = prop_name_p->hash; + uint32_t bitmap_row = hash / bitmap_row_size; + uint32_t bitmap_column = hash % bitmap_row_size; + + if (ecma_is_property_enumerable (prop_iter_p)) + { + if ((names_hashes_bitmap[bitmap_row] & (1u << bitmap_column)) == 0) + { + /* no name with the hash occured during the iteration session */ + is_enumerated = true; + } + else + { + /* name with same hash already occured */ + bool is_equal_found = false; + + for (uint32_t index = 0; + !is_equal_found && index < enumerated_properties_count; + index++) + { + if (ecma_compare_ecma_strings (prop_name_p, + names_p[index])) + { + is_equal_found = true; + } + } + + for (uint32_t index = 0; + !is_equal_found && index < non_enumerated_properties_count; + index++) + { + if (ecma_compare_ecma_strings (prop_name_p, + names_p[all_properties_count - index - 1])) + { + is_equal_found = true; + } + } + + is_enumerated = !is_equal_found; + } + } + else + { + is_enumerated = false; + } + + names_hashes_bitmap[bitmap_row] |= (1u << bitmap_column); + + if (is_enumerated) + { + names_p[enumerated_properties_count++] = prop_name_p; + } + else + { + names_p[all_properties_count - non_enumerated_properties_count++ - 1] = prop_name_p; + } + + JERRY_ASSERT (enumerated_properties_count + non_enumerated_properties_count <= all_properties_count); + } + else + { + JERRY_ASSERT (prop_iter_p->type == ECMA_PROPERTY_INTERNAL); + } + } + } + + if (enumerated_properties_count != 0) + { + ret_p = ecma_new_strings_collection (names_p, enumerated_properties_count); + } + + MEM_FINALIZE_LOCAL_ARRAY (names_p); + + return ret_p; +} /* vm_helper_for_in_enumerate_properties_names */ + +/** + * '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 (opcode_t opdata, /**< operation data */ + int_data_t *int_data_p) /**< interpreter context */ +{ + const idx_t expr_idx = opdata.data.for_in.expr; + const idx_t block_end_oc_idx_1 = opdata.data.for_in.oc_idx_1; + const idx_t block_end_oc_idx_2 = opdata.data.for_in.oc_idx_2; + const opcode_counter_t for_in_end_oc = (opcode_counter_t) ( + calc_opcode_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++; + + opcode_t meta_opcode = vm_get_opcode (int_data_p->opcodes_p, for_in_end_oc); + JERRY_ASSERT (meta_opcode.op_idx == __op__idx_meta); + JERRY_ASSERT (meta_opcode.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 = vm_helper_for_in_enumerate_properties_names (obj_p); + + if (names_p != NULL) + { + ecma_collection_iterator_init (&names_iterator, names_p); + + const opcode_counter_t for_in_body_begin_oc = int_data_p->pos; + const opcode_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, + OPCODE_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_normal (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 = (opcode_counter_t) (for_in_end_oc + 1u); + + ECMA_FINALIZE (expr_value); + + return ret_value; +} /* opfunc_for_in */ + diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index d1e3a6fc5..09d494b6e 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -1748,6 +1748,7 @@ opfunc_meta (opcode_t opdata, /**< operation data */ 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 (); } diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index c6ada99aa..5c19aec90 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -72,8 +72,9 @@ typedef enum 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_SCOPE_CODE_FLAGS /**< set of flags indicating various properties of the scope's code - * (See also: opcode_scope_code_flags_t) */ + OPCODE_META_TYPE_SCOPE_CODE_FLAGS, /**< set of flags indicating various properties of the scope's code + * (See also: opcode_scope_code_flags_t) */ + OPCODE_META_TYPE_END_FOR_IN /**< end of for-in statement */ } opcode_meta_type; typedef enum : idx_t @@ -109,6 +110,8 @@ typedef enum : idx_t { OPCODE_REG_FIRST = 128, /** identifier of first special register */ OPCODE_REG_SPECIAL_EVAL_RET = OPCODE_REG_FIRST, /**< eval return value */ + OPCODE_REG_SPECIAL_FOR_IN_PROPERTY_NAME, /**< variable, containing property name, + * at start of for-in loop body */ OPCODE_REG_GENERAL_FIRST, /** identifier of first non-special register */ OPCODE_REG_GENERAL_LAST = 253, /** identifier of last non-special register */ OPCODE_REG_LAST = OPCODE_REG_GENERAL_FIRST /**< identifier of last register */ @@ -186,6 +189,7 @@ opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, int_d p##_2 (a, delete_var, lhs, name) \ p##_3 (a, delete_prop, lhs, base, name) \ p##_2 (a, typeof, lhs, obj) \ + p##_3 (a, for_in, expr, oc_idx_1, oc_idx_2) \ p##_3 (a, with, expr, oc_idx_1, oc_idx_2) \ p##_2 (a, try_block, oc_idx_1, oc_idx_2) \ p##_1 (a, throw_value, var) diff --git a/tests/unit/test-parser.cpp b/tests/unit/test-parser.cpp index e1b1604c6..3af72cb74 100644 --- a/tests/unit/test-parser.cpp +++ b/tests/unit/test-parser.cpp @@ -91,10 +91,10 @@ main (int __attr_unused___ argc, OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER, INVALID_VALUE), - getop_reg_var_decl (128, 129), // var tmp128 .. tmp129; + getop_reg_var_decl (OPCODE_REG_FIRST, OPCODE_REG_GENERAL_FIRST), getop_var_decl (0), // var a; - getop_assignment (129, 1, 1), // tmp129 = 1: SMALLINT; - getop_assignment (0, 6, 129), // a = tmp129 : TYPEOF (tmp129); + getop_assignment (130, 1, 1), // $tmp0 = 1; + getop_assignment (0, 6, 130), // a = $tmp0; getop_exitval (0) // exit 0; };