/* 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_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 = (opcode_counter_t) (for_in_end_oc + 1u); ECMA_FINALIZE (expr_value); return ret_value; } /* opfunc_for_in */