diff --git a/jerry-core/config.h b/jerry-core/config.h index 1de1193d5..2c861eaf6 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -127,6 +127,11 @@ */ // #define CONFIG_ECMA_LCACHE_DISABLE +/** + * Disable ECMA property hashmap + */ +// #define CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + /** * Share of newly allocated since last GC objects among all currently allocated objects, * after achieving which, GC is started upon low severity try-give-memory-back requests. diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index d8a93c898..7bc848d07 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -22,6 +22,7 @@ #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-lcache.h" +#include "ecma-property-hashmap.h" #include "jrt.h" #include "jrt-libc-includes.h" #include "jrt-bit-fields.h" @@ -372,6 +373,13 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ { ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p); + if (prop_iter_p != NULL + && ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } + while (prop_iter_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); @@ -428,6 +436,13 @@ ecma_gc_sweep (ecma_object_t *object_p) /**< object to free */ { ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p); + if (prop_iter_p != NULL + && ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + ecma_property_hashmap_free (object_p); + prop_iter_p = ecma_get_property_list (object_p); + } + while (prop_iter_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index da16928e3..3cce0c1bd 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -226,7 +226,10 @@ typedef enum ECMA_PROPERTY_TYPE_PROPERTY_PAIR__MAX = ECMA_PROPERTY_TYPE_NAMEDACCESSOR, /**< highest value for * property pair types. */ - ECMA_PROPERTY_TYPE__MAX = ECMA_PROPERTY_TYPE_NAMEDACCESSOR, /**< highest value for property types. */ + + ECMA_PROPERTY_TYPE_HASHMAP, /**< hash map for fast property access */ + + ECMA_PROPERTY_TYPE__MAX = ECMA_PROPERTY_TYPE_HASHMAP, /**< highest value for property types. */ } ecma_property_types_t; /** diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index b65d6d71f..a3228a9f4 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -19,6 +19,7 @@ #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lcache.h" +#include "ecma-property-hashmap.h" #include "jrt-bit-fields.h" #include "byte-code.h" #include "re-compiler.h" @@ -392,15 +393,32 @@ ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-boun static ecma_property_t * ecma_create_property (ecma_object_t *object_p, /**< the object */ ecma_string_t *name_p, /**< property name */ - uint8_t type_and_flags) /**< type and flags, see ecma_property_info_t */ + uint8_t type_and_flags, /**< type and flags, see ecma_property_info_t */ + ecma_property_value_t value) /**< property value */ { JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2); - if (object_p->property_list_or_bound_object_cp != ECMA_NULL_POINTER) + mem_cpointer_t *property_list_head_p = &object_p->property_list_or_bound_object_cp; + bool has_hashmap = false; + + if (*property_list_head_p != ECMA_NULL_POINTER) { - /* If the first entry is free (deleted), we simply use its value. */ + /* If the first entry is a hashmap, it is skipped. */ ecma_property_header_t *first_property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, - object_p->property_list_or_bound_object_cp); + *property_list_head_p); + + if (ECMA_PROPERTY_GET_TYPE (first_property_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + property_list_head_p = &first_property_p->next_property_cp; + has_hashmap = true; + } + } + + if (*property_list_head_p != ECMA_NULL_POINTER) + { + /* If the first entry is free (deleted), it is reused. */ + ecma_property_header_t *first_property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, + *property_list_head_p); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (first_property_p)); @@ -415,6 +433,21 @@ ecma_create_property (ecma_object_t *object_p, /**< the object */ JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p) == first_property_pair_p->values + 0); + first_property_pair_p->values[0] = value; + + /* The property must be fully initialized before ecma_property_hashmap_insert + * is called, because the insert operation may reallocate the hashmap, and + * that triggers garbage collection which scans all properties of all objects. + * A not fully initialized but queued property may cause a crash. */ + + if (has_hashmap && name_p != NULL) + { + ecma_property_hashmap_insert (object_p, + name_p, + first_property_pair_p, + 0); + } + return property_p; } } @@ -423,18 +456,30 @@ ecma_create_property (ecma_object_t *object_p, /**< the object */ ecma_property_pair_t *first_property_pair_p = ecma_alloc_property_pair (); /* Just copy the previous value (no need to decompress, compress). */ - first_property_pair_p->header.next_property_cp = object_p->property_list_or_bound_object_cp; + first_property_pair_p->header.next_property_cp = *property_list_head_p; first_property_pair_p->header.types[0].type_and_flags = ECMA_PROPERTY_TYPE_DELETED; first_property_pair_p->header.types[1].type_and_flags = type_and_flags; first_property_pair_p->names_cp[0] = ECMA_NULL_POINTER; ECMA_SET_POINTER (first_property_pair_p->names_cp[1], name_p); - ecma_set_property_list (object_p, &first_property_pair_p->header); + ECMA_SET_NON_NULL_POINTER (*property_list_head_p, &first_property_pair_p->header); ecma_property_t *property_p = first_property_pair_p->header.types + 1; JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p) == first_property_pair_p->values + 1); + first_property_pair_p->values[1] = value; + + /* See the comment before the other ecma_property_hashmap_insert above. */ + + if (has_hashmap && name_p != NULL) + { + ecma_property_hashmap_insert (object_p, + name_p, + first_property_pair_p, + 1); + } + return property_p; } /* ecma_create_property */ @@ -453,11 +498,10 @@ ecma_create_internal_property (ecma_object_t *object_p, /**< the object */ uint8_t id_byte = (uint8_t) (property_id << ECMA_PROPERTY_FLAG_SHIFT); uint8_t type_and_flags = (uint8_t) (ECMA_PROPERTY_TYPE_INTERNAL | id_byte); - ecma_property_t *property_p = ecma_create_property (object_p, NULL, type_and_flags); + ecma_property_value_t value; + value.value = ECMA_NULL_POINTER; - ECMA_PROPERTY_VALUE_PTR (property_p)->value = ECMA_NULL_POINTER; - - return property_p; + return ecma_create_property (object_p, NULL, type_and_flags, value); } /* ecma_create_internal_property */ /** @@ -477,6 +521,13 @@ ecma_find_internal_property (ecma_object_t *object_p, /**< object descriptor */ ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p); + if (prop_iter_p != NULL + && ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } + while (prop_iter_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); @@ -551,13 +602,12 @@ ecma_create_named_data_property (ecma_object_t *object_p, /**< object */ name_p = ecma_copy_or_ref_ecma_string (name_p); - ecma_property_t *property_p = ecma_create_property (object_p, name_p, type_and_flags); - - ecma_set_named_data_property_value (property_p, ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); - ecma_lcache_invalidate (object_p, name_p, NULL); - return property_p; + ecma_property_value_t value; + value.value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + + return ecma_create_property (object_p, name_p, type_and_flags, value); } /* ecma_create_named_data_property */ /** @@ -588,17 +638,13 @@ ecma_create_named_accessor_property (ecma_object_t *object_p, /**< object */ name_p = ecma_copy_or_ref_ecma_string (name_p); - ecma_property_t *property_p = ecma_create_property (object_p, name_p, type_and_flags); - - /* - * Should be performed after linking the property into object's property list, because the setters assert that. - */ - ecma_set_named_accessor_property_getter (object_p, property_p, get_p); - ecma_set_named_accessor_property_setter (object_p, property_p, set_p); - ecma_lcache_invalidate (object_p, name_p, NULL); - return property_p; + ecma_property_value_t value; + ECMA_SET_POINTER (value.getter_setter_pair.getter_p, get_p); + ECMA_SET_POINTER (value.getter_setter_pair.setter_p, set_p); + + return ecma_create_property (object_p, name_p, type_and_flags, value); } /* ecma_create_named_accessor_property */ /** @@ -621,9 +667,22 @@ ecma_find_named_property (ecma_object_t *obj_p, /**< object to find property in 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) + { + property_p = ecma_property_hashmap_find ((ecma_property_hashmap_t *) prop_iter_p, name_p); + ecma_lcache_insert (obj_p, name_p, property_p); + + return property_p; + } +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + property_p = NULL; - ecma_property_header_t *prop_iter_p = ecma_get_property_list (obj_p); + uint32_t steps = 0; while (prop_iter_p != NULL) { @@ -657,10 +716,17 @@ ecma_find_named_property (ecma_object_t *obj_p, /**< object to find property in } } + 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); + } + ecma_lcache_insert (obj_p, name_p, property_p); return property_p; @@ -884,6 +950,16 @@ ecma_delete_property (ecma_object_t *object_p, /**< object */ { ecma_property_header_t *cur_prop_p = ecma_get_property_list (object_p); ecma_property_header_t *prev_prop_p = NULL; + bool has_hashmap = false; + + if (cur_prop_p != NULL + && ECMA_PROPERTY_GET_TYPE (cur_prop_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + prev_prop_p = cur_prop_p; + cur_prop_p = ECMA_GET_POINTER (ecma_property_header_t, + cur_prop_p->next_property_cp); + has_hashmap = true; + } while (true) { @@ -900,13 +976,20 @@ ecma_delete_property (ecma_object_t *object_p, /**< object */ ecma_free_property (object_p, name_p, cur_prop_p->types + i); - prop_pair_p->names_cp[i] = ECMA_NULL_POINTER; - if (name_p != NULL) { + if (has_hashmap) + { + ecma_property_hashmap_delete (object_p, + name_p, + prop_p); + } + ecma_deref_ecma_string (name_p); } + prop_pair_p->names_cp[i] = ECMA_NULL_POINTER; + JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2); if (cur_prop_p->types[1 - i].type_and_flags != ECMA_PROPERTY_TYPE_DELETED) @@ -938,7 +1021,7 @@ ecma_delete_property (ecma_object_t *object_p, /**< object */ } /* ecma_delete_property */ /** - * Check that + * Check whether the object contains a property */ static void ecma_assert_object_contains_the_property (const ecma_object_t *object_p, /**< ecma-object */ @@ -947,6 +1030,14 @@ ecma_assert_object_contains_the_property (const ecma_object_t *object_p, /**< ec #ifndef JERRY_NDEBUG ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p); + JERRY_ASSERT (prop_iter_p != NULL); + + if (ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } + while (prop_iter_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); diff --git a/jerry-core/ecma/base/ecma-property-hashmap.c b/jerry-core/ecma/base/ecma-property-hashmap.c new file mode 100644 index 000000000..da4942f8f --- /dev/null +++ b/jerry-core/ecma/base/ecma-property-hashmap.c @@ -0,0 +1,511 @@ +/* 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. + * 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-globals.h" +#include "ecma-helpers.h" +#include "ecma-property-hashmap.h" +#include "jrt-libc-includes.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmapropertyhashmap Property hashmap + * @{ + */ + +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + +/** + * Compute the total size of the property hashmap. + */ +#define ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE(max_property_count) \ + (sizeof (ecma_property_hashmap_t) + (max_property_count * sizeof (mem_cpointer_t)) + (max_property_count >> 3)) + +/** + * Number of items in the stepping table. + */ +#define ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS 8 + +/** + * Stepping values for searching items in the hashmap. + */ +static const uint32_t ecma_property_hashmap_steps[ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS] = +{ + 3, 5, 7, 11, 13, 17, 19, 23 +}; + +/** + * Get the value of a bit in a bitmap. + */ +#define ECMA_PROPERTY_HASHMAP_GET_BIT(byte_p, index) \ + ((byte_p)[(index) >> 3] & (1 << ((index) & 0x7))) + +/** + * Clear the value of a bit in a bitmap. + */ +#define ECMA_PROPERTY_HASHMAP_CLEAR_BIT(byte_p, index) \ + ((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] & ~(1 << ((index) & 0x7)))) + +/** + * Set the value of a bit in a bitmap. + */ +#define ECMA_PROPERTY_HASHMAP_SET_BIT(byte_p, index) \ + ((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] | (1 << ((index) & 0x7)))) + +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + +/** + * Create a new property hashmap for the object. + * The object must not have a property hashmap. + */ +void +ecma_property_hashmap_create (ecma_object_t *object_p) /**< object */ +{ +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + JERRY_ASSERT (ecma_get_property_list (object_p) != NULL); + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (ecma_get_property_list (object_p))); + + uint32_t named_property_count = 0; + + ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p); + + while (prop_iter_p != NULL) + { + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + ecma_property_types_t type = ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + i); + + if (type == ECMA_PROPERTY_TYPE_NAMEDDATA || type == ECMA_PROPERTY_TYPE_NAMEDACCESSOR) + { + named_property_count++; + } + } + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } + + /* The max_property_count must be power of 2. */ + uint32_t max_property_count = ECMA_PROPERTY_HASMAP_MINIMUM_SIZE; + + /* At least 1/3 items must be NULL. */ + while (max_property_count < (named_property_count + (named_property_count >> 1))) + { + max_property_count <<= 1; + } + + size_t total_size = ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (max_property_count); + + ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) mem_heap_alloc_block (total_size); + memset (hashmap_p, 0, total_size); + + hashmap_p->header.types[0].type_and_flags = ECMA_PROPERTY_TYPE_HASHMAP; + hashmap_p->header.next_property_cp = object_p->property_list_or_bound_object_cp; + hashmap_p->max_property_count = max_property_count; + hashmap_p->null_count = max_property_count - named_property_count; + + mem_cpointer_t *pair_list_p = (mem_cpointer_t *) (hashmap_p + 1); + uint8_t *bits_p = (uint8_t *) (pair_list_p + max_property_count); + uint32_t mask = max_property_count - 1; + + uint8_t shift_counter = 0; + + if (max_property_count <= (1u << LIT_STRING_HASH_BITS)) + { + hashmap_p->header.types[1].type_and_flags = 0; + } + else + { + while (max_property_count > (1u << LIT_STRING_HASH_BITS)) + { + shift_counter++; + max_property_count >>= 1; + } + } + + hashmap_p->header.types[1].type_and_flags = shift_counter; + + prop_iter_p = ecma_get_property_list (object_p); + ECMA_SET_POINTER (object_p->property_list_or_bound_object_cp, hashmap_p); + + while (prop_iter_p != NULL) + { + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + ecma_property_types_t type = ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + i); + + if (!(type == ECMA_PROPERTY_TYPE_NAMEDDATA || type == ECMA_PROPERTY_TYPE_NAMEDACCESSOR)) + { + continue; + } + + ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p; + ecma_string_t *name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_pair_p->names_cp[i]); + + uint32_t entry_index = name_p->hash; + uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; + + if (mask < (1u << LIT_STRING_HASH_BITS)) + { + entry_index &= mask; + } + else + { + entry_index <<= shift_counter; + JERRY_ASSERT (entry_index <= mask); + } + +#ifndef JERRY_NDEBUG + /* Because max_property_count (power of 2) and step (a prime + * number) are relative primes, all entries of the hasmap are + * visited exactly once before the start entry index is reached + * again. Furthermore because at least one NULL is present in + * the hashmap, the while loop must be terminated before the + * the starting index is reached again. */ + uint32_t start_entry_index = entry_index; +#endif /* !JERRY_NDEBUG */ + + while (pair_list_p[entry_index] != ECMA_NULL_POINTER) + { + entry_index = (entry_index + step) & mask; + +#ifndef JERRY_NDEBUG + JERRY_ASSERT (entry_index != start_entry_index); +#endif /* !JERRY_NDEBUG */ + } + + ECMA_SET_POINTER (pair_list_p[entry_index], property_pair_p); + + if (i != 0) + { + ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index); + } + } + + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } +#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + (void) object_p; +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ +} /* ecma_property_hashmap_create */ + +/** + * Free the hashmap of the object. + * The object must have a property hashmap. + */ +void +ecma_property_hashmap_free (ecma_object_t *object_p) /**< object */ +{ +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + /* Property hash must be exists and must be the first property. */ + ecma_property_header_t *property_p = ecma_get_property_list (object_p); + + JERRY_ASSERT (property_p != NULL + && ECMA_PROPERTY_GET_TYPE (property_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP); + + ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) property_p; + + object_p->property_list_or_bound_object_cp = property_p->next_property_cp; + + mem_heap_free_block (hashmap_p, + ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (hashmap_p->max_property_count)); +#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + (void) object_p; +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ +} /* ecma_property_hashmap_free */ + +/** + * Insert named property into the hashmap. + */ +void +ecma_property_hashmap_insert (ecma_object_t *object_p, /**< object */ + ecma_string_t *name_p, /**< name of the property */ + ecma_property_pair_t *property_pair_p, /**< property pair */ + int property_index) /**< property index in the pair (0 or 1) */ +{ +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t, + object_p->property_list_or_bound_object_cp); + + JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (hashmap_p->header.types + 0) == ECMA_PROPERTY_TYPE_HASHMAP); + + /* The NULLs are reduced below 1/8 of the hashmap. */ + if (hashmap_p->null_count < (hashmap_p->max_property_count >> 3)) + { + ecma_property_hashmap_free (object_p); + ecma_property_hashmap_create (object_p); + return; + } + + JERRY_ASSERT (property_index < ECMA_PROPERTY_PAIR_ITEM_COUNT); + + uint32_t entry_index = name_p->hash; + uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; + uint32_t mask = hashmap_p->max_property_count - 1; + + if (mask < (1u << LIT_STRING_HASH_BITS)) + { + entry_index &= mask; + } + else + { + entry_index <<= hashmap_p->header.types[1].type_and_flags; + } + +#ifndef JERRY_NDEBUG + /* See the comment for this variable in ecma_property_hashmap_create. */ + uint32_t start_entry_index = entry_index; +#endif /* !JERRY_NDEBUG */ + + mem_cpointer_t *pair_list_p = (mem_cpointer_t *) (hashmap_p + 1); + + while (pair_list_p[entry_index] != ECMA_NULL_POINTER) + { + entry_index = (entry_index + step) & mask; + +#ifndef JERRY_NDEBUG + JERRY_ASSERT (entry_index != start_entry_index); +#endif /* !JERRY_NDEBUG */ + } + + ECMA_SET_POINTER (pair_list_p[entry_index], property_pair_p); + + uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count); + bits_p += (entry_index >> 3); + mask = (uint32_t) (1 << (entry_index & 0x7)); + + if (!(*bits_p & mask)) + { + /* Deleted entries also has ECMA_NULL_POINTER + * value, but they are not NULL values. */ + hashmap_p->null_count--; + JERRY_ASSERT (hashmap_p->null_count > 0); + } + + if (property_index == 0) + { + *bits_p = (uint8_t) ((*bits_p) & ~mask); + } + else + { + *bits_p = (uint8_t) ((*bits_p) | mask); + } +#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + (void) object_p; + (void) name_p; + (void) property_pair_p; + (void) property_index; +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ +} /* ecma_property_hashmap_insert */ + +/** + * Delete named property from the hashmap. + */ +void +ecma_property_hashmap_delete (ecma_object_t *object_p, /**< object */ + ecma_string_t *name_p, /**< name of the property */ + ecma_property_t *property_p) /**< property */ +{ +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t, + object_p->property_list_or_bound_object_cp); + + JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (hashmap_p->header.types + 0) == ECMA_PROPERTY_TYPE_HASHMAP); + + uint32_t entry_index = name_p->hash; + uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; + uint32_t mask = hashmap_p->max_property_count - 1; + mem_cpointer_t *pair_list_p = (mem_cpointer_t *) (hashmap_p + 1); + uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count); + + if (mask < (1u << LIT_STRING_HASH_BITS)) + { + entry_index &= mask; + } + else + { + entry_index <<= hashmap_p->header.types[1].type_and_flags; + JERRY_ASSERT (entry_index <= mask); + } + +#ifndef JERRY_NDEBUG + /* See the comment for this variable in ecma_property_hashmap_create. */ + uint32_t start_entry_index = entry_index; +#endif /* !JERRY_NDEBUG */ + + while (true) + { + if (pair_list_p[entry_index] != ECMA_NULL_POINTER) + { + size_t offset = 0; + if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) + { + offset = 1; + } + + ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t, + pair_list_p[entry_index]); + + if ((property_pair_p->header.types + offset) == property_p) + { + JERRY_ASSERT (ecma_compare_ecma_strings (ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_pair_p->names_cp[offset]), + name_p)); + + pair_list_p[entry_index] = ECMA_NULL_POINTER; + ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index); + return; + } + } + else + { + /* Must be a deleted entry. */ + JERRY_ASSERT (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)); + } + + entry_index = (entry_index + step) & mask; + +#ifndef JERRY_NDEBUG + JERRY_ASSERT (entry_index != start_entry_index); +#endif /* !JERRY_NDEBUG */ + } +#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + (void) object_p; + (void) name_p; + (void) property_p; +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ +} /* ecma_property_hashmap_delete */ + +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE +/** + * Find a named property. + * + * @return pointer to the property if found or NULL otherwise + */ +ecma_property_t * +ecma_property_hashmap_find (ecma_property_hashmap_t *hashmap_p, /**< hashmap */ + ecma_string_t *name_p) /**< property name */ +{ +#ifndef JERRY_NDEBUG + /* A sanity check in debug mode: a named property must be present + * in both the property hashmap and in the property chain, or missing + * from both data collection. The following code checks the property + * chain, and sets the property_found variable. */ + bool property_found = false; + ecma_property_header_t *prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + hashmap_p->header.next_property_cp); + + while (prop_iter_p != NULL && !property_found) + { + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); + + ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + if (prop_pair_p->names_cp[i] != ECMA_NULL_POINTER) + { + ecma_string_t *property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + prop_pair_p->names_cp[i]); + + if (ecma_compare_ecma_strings (name_p, property_name_p)) + { + property_found = true; + break; + } + } + } + + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } +#endif /* !JERRY_NDEBUG */ + + uint32_t entry_index = name_p->hash; + uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)]; + uint32_t mask = hashmap_p->max_property_count - 1; + mem_cpointer_t *pair_list_p = (mem_cpointer_t *) (hashmap_p + 1); + uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count); + + if (mask < (1u << LIT_STRING_HASH_BITS)) + { + entry_index &= mask; + } + else + { + entry_index <<= hashmap_p->header.types[1].type_and_flags; + JERRY_ASSERT (entry_index <= mask); + } + +#ifndef JERRY_NDEBUG + /* See the comment for this variable in ecma_property_hashmap_create. */ + uint32_t start_entry_index = entry_index; +#endif /* !JERRY_NDEBUG */ + + while (true) + { + if (pair_list_p[entry_index] != ECMA_NULL_POINTER) + { + size_t offset = 0; + if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) + { + offset = 1; + } + + ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t, + pair_list_p[entry_index]); + + ecma_string_t *property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_pair_p->names_cp[offset]); + + if (ecma_compare_ecma_strings (name_p, property_name_p)) + { +#ifndef JERRY_NDEBUG + JERRY_ASSERT (property_found); +#endif /* !JERRY_NDEBUG */ + return property_pair_p->header.types + offset; + } + } + else + { + if (!ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index)) + { +#ifndef JERRY_NDEBUG + JERRY_ASSERT (!property_found); +#endif /* !JERRY_NDEBUG */ + return NULL; + } + /* Otherwise it is a deleted entry. */ + } + + entry_index = (entry_index + step) & mask; + +#ifndef JERRY_NDEBUG + JERRY_ASSERT (entry_index != start_entry_index); +#endif /* !JERRY_NDEBUG */ + } +} /* ecma_property_hashmap_find */ +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + +/** + * @} + * @} + */ diff --git a/jerry-core/ecma/base/ecma-property-hashmap.h b/jerry-core/ecma/base/ecma-property-hashmap.h new file mode 100644 index 000000000..0370a79ff --- /dev/null +++ b/jerry-core/ecma/base/ecma-property-hashmap.h @@ -0,0 +1,70 @@ +/* Copyright 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. + * 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 ECMA_PROPERTY_HASHMAP_H +#define ECMA_PROPERTY_HASHMAP_H + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmapropertyhashmap Property hashmap + * @{ + */ + +/** + * Recommended minimum number of items in a property cache. + */ +#define ECMA_PROPERTY_HASMAP_MINIMUM_SIZE 32 + +/** + * Property hash. + */ +typedef struct +{ + ecma_property_header_t header; /**< header of the property */ + uint32_t max_property_count; /**< maximum property count (power of 2) */ + uint32_t null_count; /**< number of NULLs in the map */ + + /* + * The hash is followed by max_property_count ecma_cpointer_t + * compressed pointers and (max_property_count + 7) / 8 bytes + * which stores a flag for each compressed pointer. + * + * If the compressed pointer is equal to ECMA_NULL_POINTER + * - flag is cleared if the entry is NULL + * - flag is set if the entry is deleted + * + * If the compressed pointer is not equal to ECMA_NULL_POINTER + * - flag is cleared if the first entry of a property pair is referenced + * - flag is set if the second entry of a property pair is referenced + */ +} ecma_property_hashmap_t; + +extern void ecma_property_hashmap_create (ecma_object_t *); +extern void ecma_property_hashmap_free (ecma_object_t *); +extern void ecma_property_hashmap_insert (ecma_object_t *, ecma_string_t *, ecma_property_pair_t *, int); +extern void ecma_property_hashmap_delete (ecma_object_t *, ecma_string_t *, ecma_property_t *); + +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE +extern ecma_property_t *ecma_property_hashmap_find (ecma_property_hashmap_t *, ecma_string_t *); +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + +/** + * @} + * @} + */ + +#endif /* !ECMA_PROPERTY_HASHMAP_H */ diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 38962a350..e98e79152 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -626,6 +626,13 @@ ecma_op_object_get_property_names (ecma_object_t *obj_p, /**< object */ ecma_property_header_t *prop_iter_p = ecma_get_property_list (prototype_chain_iter_p); + if (prop_iter_p != NULL + && ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP) + { + prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t, + prop_iter_p->next_property_cp); + } + while (prop_iter_p != NULL) { JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));