/** * Invalidate LCache entries associated with given object and property name / property */ void ecma_lcache_invalidate (ecma_object_t *object_p, /**< object */ jmem_cpointer_t name_cp, /**< property name */ ecma_property_t *prop_p) /**< property */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_p != NULL && ecma_is_property_lcached (prop_p)); JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*prop_p) == ECMA_PROPERTY_TYPE_NAMEDDATA || ECMA_PROPERTY_GET_TYPE (*prop_p) == ECMA_PROPERTY_TYPE_NAMEDACCESSOR); #ifndef CONFIG_ECMA_LCACHE_DISABLE jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); lit_string_hash_t name_hash = ecma_string_get_property_name_hash (*prop_p, name_cp); size_t row_index = ecma_lcache_row_index (object_cp, name_hash); ecma_lcache_hash_entry_t *entry_p = JERRY_HASH_TABLE_CONTEXT (table) [row_index]; for (uint32_t entry_index = 0; entry_index < ECMA_LCACHE_HASH_ROW_LENGTH; entry_index++) { if (entry_p->object_cp != ECMA_NULL_POINTER && entry_p->prop_p == prop_p) { JERRY_ASSERT (entry_p->object_cp == object_cp); ecma_lcache_invalidate_entry (entry_p); return; } entry_p++; } /* The property must be present. */ JERRY_UNREACHABLE (); #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_invalidate */
/** * Invalidate LCache entries associated with given object and property name / property * * Note: * Either property name argument or property argument should be NULL, * and another should be non-NULL. * In case property name argument is NULL, property's name is taken * from property's description. */ void ecma_lcache_invalidate (ecma_object_t *object_p, /**< object */ ecma_string_t *prop_name_p, /**< property's name (See also: Note) */ ecma_property_t *prop_p) /**< property (See also: Note) */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); #ifndef CONFIG_ECMA_LCACHE_DISABLE if (prop_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (prop_p) == ECMA_PROPERTY_TYPE_NAMEDDATA || ECMA_PROPERTY_GET_TYPE (prop_p) == ECMA_PROPERTY_TYPE_NAMEDACCESSOR); bool is_cached = ecma_is_property_lcached (prop_p); if (!is_cached) { return; } ecma_set_property_lcached (prop_p, false); } unsigned int object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); lit_string_hash_t hash_key = ecma_string_hash (prop_name_p); /* Property's name has was computed. * Given (object, property name) pair should be in the row corresponding to computed hash. */ ecma_lcache_invalidate_row_for_object_property_pair (hash_key, object_cp, prop_p); #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_invalidate */
/** * Free property values and change their type to deleted. */ void ecma_free_property (ecma_object_t *object_p, /**< object the property belongs to */ jmem_cpointer_t name_cp, /**< name of the property or ECMA_NULL_POINTER */ ecma_property_t *property_p) /**< property */ { JERRY_ASSERT (object_p != NULL && property_p != NULL); switch (ECMA_PROPERTY_GET_TYPE (*property_p)) { case ECMA_PROPERTY_TYPE_NAMEDDATA: { if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_STRING_CONTAINER_MAGIC_STRING) { if (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_HANDLE || name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER) { ecma_free_native_pointer (property_p); break; } } ecma_free_value_if_not_object (ECMA_PROPERTY_VALUE_PTR (property_p)->value); break; } case ECMA_PROPERTY_TYPE_NAMEDACCESSOR: { #ifdef JERRY_CPOINTER_32_BIT ecma_getter_setter_pointers_t *getter_setter_pair_p; getter_setter_pair_p = ECMA_GET_POINTER (ecma_getter_setter_pointers_t, ECMA_PROPERTY_VALUE_PTR (property_p)->getter_setter_pair_cp); jmem_pools_free (getter_setter_pair_p, sizeof (ecma_getter_setter_pointers_t)); #endif /* JERRY_CPOINTER_32_BIT */ break; } default: { JERRY_UNREACHABLE (); return; } } if (ecma_is_property_lcached (property_p)) { ecma_lcache_invalidate (object_p, name_cp, property_p); } if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_PROPERTY_NAME_TYPE_STRING) { ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, name_cp); ecma_deref_ecma_string (prop_name_p); } *property_p = ECMA_PROPERTY_TYPE_DELETED; } /* ecma_free_property */
/** * Free property values and change their type to deleted. */ void ecma_free_property (ecma_object_t *object_p, /**< object the property belongs to */ ecma_string_t *name_p, /**< name of the property or NULL */ ecma_property_t *property_p) /**< property */ { JERRY_ASSERT (object_p != NULL && property_p != NULL); switch (ECMA_PROPERTY_GET_TYPE (property_p)) { case ECMA_PROPERTY_TYPE_NAMEDDATA: { ecma_free_named_data_property (object_p, property_p); if (ecma_is_property_lcached (property_p)) { ecma_lcache_invalidate (object_p, name_p, property_p); } break; } case ECMA_PROPERTY_TYPE_NAMEDACCESSOR: { if (ecma_is_property_lcached (property_p)) { ecma_lcache_invalidate (object_p, name_p, property_p); } break; } case ECMA_PROPERTY_TYPE_INTERNAL: { JERRY_ASSERT (name_p == NULL); ecma_free_internal_property (property_p); break; } default: { JERRY_UNREACHABLE (); break; } } property_p->type_and_flags = ECMA_PROPERTY_TYPE_DELETED; } /* ecma_free_property */
/** * Insert an entry into LCache */ void ecma_lcache_insert (ecma_object_t *object_p, /**< object */ jmem_cpointer_t name_cp, /**< property name */ ecma_property_t *prop_p) /**< property */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_p != NULL && !ecma_is_property_lcached (prop_p)); JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*prop_p) == ECMA_PROPERTY_TYPE_NAMEDDATA || ECMA_PROPERTY_GET_TYPE (*prop_p) == ECMA_PROPERTY_TYPE_NAMEDACCESSOR); #ifndef CONFIG_ECMA_LCACHE_DISABLE jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); lit_string_hash_t name_hash = ecma_string_get_property_name_hash (*prop_p, name_cp); size_t row_index = ecma_lcache_row_index (object_cp, name_hash); ecma_lcache_hash_entry_t *entries_p = JERRY_HASH_TABLE_CONTEXT (table)[row_index]; int32_t entry_index; for (entry_index = 0; entry_index < ECMA_LCACHE_HASH_ROW_LENGTH; entry_index++) { if (entries_p[entry_index].object_cp == ECMA_NULL_POINTER) { break; } } if (entry_index == ECMA_LCACHE_HASH_ROW_LENGTH) { /* Invalidate the last entry. */ ecma_lcache_invalidate_entry (entries_p + ECMA_LCACHE_HASH_ROW_LENGTH - 1); /* Shift other entries towards the end. */ for (uint32_t i = ECMA_LCACHE_HASH_ROW_LENGTH - 1; i > 0; i--) { entries_p[i] = entries_p[i - 1]; } entry_index = 0; } ecma_lcache_hash_entry_t *entry_p = entries_p + entry_index; ECMA_SET_NON_NULL_POINTER (entry_p->object_cp, object_p); entry_p->prop_name_cp = name_cp; entry_p->prop_p = prop_p; ecma_set_property_lcached (entry_p->prop_p, true); #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_insert */
/** * Lookup property in the LCache * * @return true - if (object, property name) pair is registered in LCache, * false - probably, not registered. */ inline bool __attr_always_inline___ ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ const ecma_string_t *prop_name_p, /**< property's name */ ecma_property_t **prop_p_p) /**< [out] if return value is true, * then here will be pointer to property, * if the object contains property with specified name, * or, otherwise - NULL; * if return value is false, * then the output parameter is not set */ { #ifndef CONFIG_ECMA_LCACHE_DISABLE lit_string_hash_t hash_key = ecma_string_hash (prop_name_p); unsigned int object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); for (uint32_t i = 0; i < ECMA_LCACHE_HASH_ROW_LENGTH; i++) { if (ecma_lcache_hash_table[hash_key][i].object_cp == object_cp) { ecma_string_t *entry_prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, ecma_lcache_hash_table[hash_key][i].prop_name_cp); JERRY_ASSERT (prop_name_p->hash == entry_prop_name_p->hash); if (ECMA_STRING_GET_CONTAINER (prop_name_p) == ECMA_STRING_GET_CONTAINER (entry_prop_name_p) && prop_name_p->u.common_field == entry_prop_name_p->u.common_field) { ecma_property_t *prop_p = ecma_lcache_hash_table[hash_key][i].prop_p; JERRY_ASSERT (prop_p == NULL || ecma_is_property_lcached (prop_p)); *prop_p_p = prop_p; return true; } else { /* may be equal but it is long to compare it here */ } } } #else /* CONFIG_ECMA_LCACHE_DISABLE */ (void) object_p; (void) prop_name_p; (void) prop_p_p; #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ return false; } /* ecma_lcache_lookup */
/** * Lookup property in the LCache * * @return a pointer to an ecma_property_t if the lookup is successful * NULL otherwise */ inline ecma_property_t * __attr_always_inline___ ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ const ecma_string_t *prop_name_p) /**< property's name */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); #ifndef CONFIG_ECMA_LCACHE_DISABLE jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); size_t row_index = ecma_lcache_row_index (object_cp, prop_name_p); ecma_lcache_hash_entry_t *entry_p = JERRY_HASH_TABLE_CONTEXT (table) [row_index]; ecma_lcache_hash_entry_t *entry_end_p = entry_p + ECMA_LCACHE_HASH_ROW_LENGTH; ecma_string_container_t prop_container = ECMA_STRING_GET_CONTAINER (prop_name_p); while (entry_p < entry_end_p) { if (entry_p->object_cp == object_cp) { ecma_string_t *entry_prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, entry_p->prop_name_cp); JERRY_ASSERT ((prop_name_p->hash & ECMA_LCACHE_HASH_MASK) == (entry_prop_name_p->hash & ECMA_LCACHE_HASH_MASK)); if (prop_name_p == entry_prop_name_p || (prop_container > ECMA_STRING_CONTAINER_HEAP_LONG_UTF8_STRING && prop_container == ECMA_STRING_GET_CONTAINER (entry_prop_name_p) && prop_name_p->u.common_field == entry_prop_name_p->u.common_field)) { ecma_property_t *prop_p = entry_p->prop_p; JERRY_ASSERT (prop_p != NULL && ecma_is_property_lcached (prop_p)); return prop_p; } else { /* They can be equal, but generic string comparison is too costly. */ } } entry_p++; } #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ return NULL; } /* ecma_lcache_lookup */
/** * Lookup property in the LCache * * @return a pointer to an ecma_property_t if the lookup is successful * NULL otherwise */ inline ecma_property_t * __attr_always_inline___ ecma_lcache_lookup (ecma_object_t *object_p, /**< object */ const ecma_string_t *prop_name_p) /**< property's name */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); #ifndef CONFIG_ECMA_LCACHE_DISABLE jmem_cpointer_t object_cp; ECMA_SET_NON_NULL_POINTER (object_cp, object_p); size_t row_index = ecma_lcache_row_index (object_cp, ecma_string_hash (prop_name_p)); ecma_lcache_hash_entry_t *entry_p = JERRY_HASH_TABLE_CONTEXT (table) [row_index]; ecma_lcache_hash_entry_t *entry_end_p = entry_p + ECMA_LCACHE_HASH_ROW_LENGTH; ecma_property_t prop_name_type; jmem_cpointer_t prop_name_cp = ecma_string_to_lcache_property_name (prop_name_p, &prop_name_type); while (entry_p < entry_end_p) { if (entry_p->object_cp == object_cp && entry_p->prop_name_cp == prop_name_cp) { ecma_property_t *prop_p = entry_p->prop_p; JERRY_ASSERT (prop_p != NULL && ecma_is_property_lcached (prop_p)); if (ECMA_PROPERTY_GET_NAME_TYPE (*prop_p) == prop_name_type) { return prop_p; } } else { /* They can be equal, but generic string comparison is too costly. */ } entry_p++; } #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ return NULL; } /* ecma_lcache_lookup */
/** * Get value of function's argument mapped to index of Arguments object. * * Note: * The procedure emulates execution of function described by MakeArgGetter * * @return ecma value * Returned value must be freed with ecma_free_value */ void ecma_arguments_update_mapped_arg_value (ecma_object_t *object_p, /**< the object */ ecma_string_t *property_name_p, /**< property name */ ecma_property_t *property_p) /**< property value */ { ecma_value_t *map_prop_p = ecma_get_internal_property (object_p, ECMA_INTERNAL_PROPERTY_PARAMETERS_MAP); ecma_object_t *map_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, *map_prop_p); ecma_value_t arg_name = ecma_op_object_find (map_p, property_name_p); if (!ecma_is_value_found (arg_name)) { return; } ecma_value_t *scope_prop_p = ecma_get_internal_property (map_p, ECMA_INTERNAL_PROPERTY_SCOPE); ecma_object_t *lex_env_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, *scope_prop_p); JERRY_ASSERT (lex_env_p != NULL && ecma_is_lexical_environment (lex_env_p)); ecma_string_t *arg_name_p = ecma_get_string_from_value (arg_name); ecma_value_t value = ecma_op_get_binding_value (lex_env_p, arg_name_p, true); ecma_deref_ecma_string (arg_name_p); JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (value)); ecma_named_data_property_assign_value (object_p, property_p, value); ecma_free_value (value); /* These properties cannot be cached. This is a temporary * workaround until the property management is fully rewritten. */ if (ecma_is_property_lcached (property_p)) { ecma_lcache_invalidate (object_p, property_name_p, property_p); } } /* ecma_arguments_update_mapped_arg_value */
/** * Insert an entry into LCache */ void ecma_lcache_insert (ecma_object_t *object_p, /**< object */ ecma_string_t *prop_name_p, /**< property's name */ ecma_property_t *prop_p) /**< pointer to associated property or NULL * (NULL indicates that the object doesn't have property * with the name specified) */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (prop_name_p != NULL); #ifndef CONFIG_ECMA_LCACHE_DISABLE prop_name_p = ecma_copy_or_ref_ecma_string (prop_name_p); lit_string_hash_t hash_key = ecma_string_hash (prop_name_p); if (prop_p != NULL) { if (unlikely (ecma_is_property_lcached (prop_p))) { int32_t entry_index; for (entry_index = 0; entry_index < ECMA_LCACHE_HASH_ROW_LENGTH; entry_index++) { if (ecma_lcache_hash_table[hash_key][entry_index].object_cp != ECMA_NULL_POINTER && ecma_lcache_hash_table[hash_key][entry_index].prop_p == prop_p) { #ifndef JERRY_NDEBUG ecma_object_t *obj_in_entry_p; obj_in_entry_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, ecma_lcache_hash_table[hash_key][entry_index].object_cp); JERRY_ASSERT (obj_in_entry_p == object_p); #endif /* !JERRY_NDEBUG */ break; } } JERRY_ASSERT (entry_index != ECMA_LCACHE_HASH_ROW_LENGTH); ecma_lcache_invalidate_entry (&ecma_lcache_hash_table[hash_key][entry_index]); } JERRY_ASSERT (!ecma_is_property_lcached (prop_p)); ecma_set_property_lcached (prop_p, true); } int32_t entry_index; for (entry_index = 0; entry_index < ECMA_LCACHE_HASH_ROW_LENGTH; entry_index++) { if (ecma_lcache_hash_table[hash_key][entry_index].object_cp == ECMA_NULL_POINTER) { break; } } if (entry_index == ECMA_LCACHE_HASH_ROW_LENGTH) { /* No empty entry was found, invalidating the whole row */ for (uint32_t i = 0; i < ECMA_LCACHE_HASH_ROW_LENGTH; i++) { ecma_lcache_invalidate_entry (&ecma_lcache_hash_table[hash_key][i]); } entry_index = 0; } ecma_ref_object (object_p); ECMA_SET_NON_NULL_POINTER (ecma_lcache_hash_table[ hash_key ][ entry_index ].object_cp, object_p); ECMA_SET_NON_NULL_POINTER (ecma_lcache_hash_table[ hash_key ][ entry_index ].prop_name_cp, prop_name_p); ecma_lcache_hash_table[ hash_key ][ entry_index ].prop_p = prop_p; #else /* CONFIG_ECMA_LCACHE_DISABLE */ (void) prop_p; #endif /* !CONFIG_ECMA_LCACHE_DISABLE */ } /* ecma_lcache_insert */
/** * Find named data property or named access property in specified object. * * @return pointer to the property, if it is found, * NULL - otherwise. */ ecma_property_t * ecma_find_named_property (ecma_object_t *obj_p, /**< object to find property in */ ecma_string_t *name_p) /**< property's name */ { JERRY_ASSERT (obj_p != NULL); JERRY_ASSERT (name_p != NULL); ecma_property_t *property_p = ecma_lcache_lookup (obj_p, name_p); if (property_p != NULL) { return property_p; } ecma_property_header_t *prop_iter_p = ecma_get_property_list (obj_p); #ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE if (prop_iter_p != NULL && ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) { ecma_string_t *property_real_name_p; property_p = ecma_property_hashmap_find ((ecma_property_hashmap_t *) prop_iter_p, name_p, &property_real_name_p); if (property_p != NULL && !ecma_is_property_lcached (property_p)) { ecma_lcache_insert (obj_p, property_real_name_p, property_p); } return property_p; } #endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ property_p = NULL; ecma_string_t *property_name_p = NULL; uint32_t steps = 0; while (prop_iter_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2); if (prop_pair_p->names_cp[0] != ECMA_NULL_POINTER) { property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_pair_p->names_cp[0]); if (ecma_compare_ecma_strings (name_p, property_name_p)) { property_p = prop_iter_p->types + 0; break; } } if (prop_pair_p->names_cp[1] != ECMA_NULL_POINTER) { property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, prop_pair_p->names_cp[1]); if (ecma_compare_ecma_strings (name_p, property_name_p)) { property_p = prop_iter_p->types + 1; break; } } steps++; prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, prop_iter_p->next_property_cp); } if (steps > (ECMA_PROPERTY_HASMAP_MINIMUM_SIZE / 4)) { ecma_property_hashmap_create (obj_p); } if (property_p != NULL && !ecma_is_property_lcached (property_p)) { ecma_lcache_insert (obj_p, property_name_p, property_p); } return property_p; } /* ecma_find_named_property */