From 51a9575fd03f40a459789056e4c66832cf371465 Mon Sep 17 00:00:00 2001 From: Roland Takacs <1487864+rtakacs@users.noreply.github.com> Date: Tue, 17 Mar 2020 19:30:55 +0100 Subject: [PATCH] Use array based storage in container objects (#3600) Currently, collections use object based solutions for storing elements and iterating on them. If an element is deleted and re-inserted, the storage position is the same as before so the iteration order is wrong. This patch replaces the object based storage with an array based solution that helps to store and iterate elements as expected. JerryScript-DCO-1.0-Signed-off-by: Roland Takacs rtakacs@inf.u-szeged.hu --- jerry-core/api/jerry.c | 4 +- jerry-core/ecma/base/ecma-gc.c | 220 +++--- jerry-core/ecma/base/ecma-globals.h | 48 +- jerry-core/ecma/base/ecma-helpers-string.c | 60 -- .../ecma/operations/ecma-container-object.c | 716 +++++++++++------- .../ecma/operations/ecma-container-object.h | 2 + jerry-core/lit/lit-magic-strings.h | 1 - tests/jerry/es2015/map-iterators.js | 64 +- tests/jerry/es2015/set-iterators.js | 46 ++ tests/jerry/es2015/weakmap.js | 2 +- 10 files changed, 726 insertions(+), 437 deletions(-) diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 7b58433b5..bfb78287e 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -2981,9 +2981,7 @@ bool jerry_object_is_valid_foreach (ecma_object_t *object_p) /**< object to test switch (ext_object_p->u.class_prop.class_id) { /* An object's internal property object should not be iterable by foreach. */ - case LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT: - /* Containers are internal data, do not iterate on them. */ - case LIT_INTERNAL_MAGIC_STRING_CONTAINER: return false; + case LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT: return false; } } diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 7cdda2e0c..354424fbd 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -295,73 +295,106 @@ ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */ -#if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) - +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) /** - * Mark objects referenced by Map/Set built-in. + * Mark objects referenced by Map built-in. */ static void -ecma_gc_mark_container_object (ecma_object_t *object_p) /**< object */ +ecma_gc_mark_map_object (ecma_object_t *object_p) /**< object */ { + JERRY_ASSERT (object_p != NULL); + ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; - ecma_object_t *internal_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, - map_object_p->u.class_prop.u.value); - ecma_extended_object_t *internal_ext_p = (ecma_extended_object_t *) internal_obj_p; + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); - ecma_gc_set_object_visited (internal_obj_p); - - jmem_cpointer_t prop_iter_cp = internal_obj_p->u1.property_list_cp; - -#if ENABLED (JERRY_PROPRETY_HASHMAP) - if (prop_iter_cp != JMEM_CP_NULL) + for (uint32_t i = 0; i < entry_count; i+= ECMA_CONTAINER_PAIR_SIZE) { - ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, - prop_iter_cp); - if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) + ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); + + if (ecma_is_value_empty (entry_p->key)) { - prop_iter_cp = prop_iter_p->next_property_cp; - } - } -#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ - - while (prop_iter_cp != JMEM_CP_NULL) - { - ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); - JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); - - ecma_gc_mark_properties ((ecma_property_pair_t *) prop_iter_p); - - if ((internal_ext_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) == 0) - { - ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; - - for (uint32_t i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) - { - ecma_property_t *property_p = (ecma_property_t *) (prop_iter_p->types + i); - - if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_PTR) - { - jmem_cpointer_t name_cp = prop_pair_p->names_cp[i]; - ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, name_cp); - - if (ECMA_STRING_GET_CONTAINER (prop_name_p) == ECMA_STRING_CONTAINER_MAP_KEY) - { - ecma_value_t key_arg = ((ecma_extended_string_t *) prop_name_p)->u.value; - - if (ecma_is_value_object (key_arg)) - { - ecma_gc_set_object_visited (ecma_get_object_from_value (key_arg)); - } - } - } - } + continue; } - prop_iter_cp = prop_iter_p->next_property_cp; - } -} /* ecma_gc_mark_container_object */ + if (ecma_is_value_object (entry_p->key)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->key)); + } -#endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ + if (ecma_is_value_object (entry_p->value)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->value)); + } + } +} /* ecma_gc_mark_map_object */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ + +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) +/** + * Mark objects referenced by WeakMap built-in. + */ +static void +ecma_gc_mark_weakmap_object (ecma_object_t *object_p) /**< object */ +{ + JERRY_ASSERT (object_p != NULL); + + ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + + for (uint32_t i = 0; i < entry_count; i+= ECMA_CONTAINER_PAIR_SIZE) + { + ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); + + if (ecma_is_value_empty (entry_p->key)) + { + continue; + } + + if (ecma_is_value_object (entry_p->value)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->value)); + } + } +} /* ecma_gc_mark_weakmap_object */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ + +#if ENABLED (JERRY_ES2015_BUILTIN_SET) +/** + * Mark objects referenced by Set built-in. + */ +static void +ecma_gc_mark_set_object (ecma_object_t *object_p) /**< object */ +{ + JERRY_ASSERT (object_p != NULL); + + ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + + for (uint32_t i = 0; i < entry_count; i+= ECMA_CONTAINER_VALUE_SIZE) + { + ecma_value_t *entry_p = start_p + i; + + if (ecma_is_value_empty (*entry_p)) + { + continue; + } + + if (ecma_is_value_object (*entry_p)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (*entry_p)); + } + } +} /* ecma_gc_mark_set_object */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ #if ENABLED (JERRY_ES2015) @@ -534,22 +567,33 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ } #endif /* ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW) */ #if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) -#if ENABLED (JERRY_ES2015_BUILTIN_MAP) - case LIT_MAGIC_STRING_MAP_UL: -#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) + case LIT_MAGIC_STRING_WEAKSET_UL: + { + break; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ #if ENABLED (JERRY_ES2015_BUILTIN_SET) case LIT_MAGIC_STRING_SET_UL: + { + ecma_gc_mark_set_object (object_p); + break; + } #endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ #if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) case LIT_MAGIC_STRING_WEAKMAP_UL: -#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ -#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) - case LIT_MAGIC_STRING_WEAKSET_UL: -#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ { - ecma_gc_mark_container_object (object_p); + ecma_gc_mark_weakmap_object (object_p); break; } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) + case LIT_MAGIC_STRING_MAP_UL: + { + ecma_gc_mark_map_object (object_p); + break; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ #endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ #if ENABLED (JERRY_ES2015) case LIT_MAGIC_STRING_GENERATOR_UL: @@ -1125,50 +1169,12 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ case LIT_MAGIC_STRING_WEAKSET_UL: #endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ { - break; - } - case LIT_INTERNAL_MAGIC_STRING_CONTAINER: - { -#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) - ecma_extended_object_t *container_p = (ecma_extended_object_t *) object_p; + ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + ecma_op_container_free_entries (object_p); + ecma_collection_destroy (container_p); - if (container_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) - { - jmem_cpointer_t prop_iter_cp = container_p->object.u1.property_list_cp; - while (prop_iter_cp != JMEM_CP_NULL) - { - ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); - JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); - - /* Both cannot be deleted. */ - JERRY_ASSERT (prop_iter_p->types[0] != ECMA_PROPERTY_TYPE_DELETED - || prop_iter_p->types[1] != ECMA_PROPERTY_TYPE_DELETED); - - 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++) - { - ecma_property_t *property_p = (ecma_property_t *) (prop_iter_p->types + i); - - if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_PTR) - { - jmem_cpointer_t name_cp = prop_pair_p->names_cp[i]; - ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, name_cp); - - if (JERRY_LIKELY (ECMA_STRING_GET_CONTAINER (prop_name_p) == ECMA_STRING_CONTAINER_MAP_KEY)) - { - ecma_value_t key_arg = ((ecma_extended_string_t *) prop_name_p)->u.value; - ecma_object_t *key_object_p = ecma_get_object_from_value (key_arg); - - ecma_op_container_unref_weak (key_object_p, ecma_make_object_value (object_p)); - } - } - } - - prop_iter_cp = prop_iter_p->next_property_cp; - } - } -#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ break; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 8317f2258..cabb67672 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -989,6 +989,50 @@ typedef enum ECMA_CONTAINER_FLAGS_EMPTY = (0), /** empty flags */ ECMA_CONTAINER_FLAGS_WEAK = (1 << 0) /** container object is weak */ } ecma_container_flags_t; + +/** + * Description of map collection. + */ +typedef struct +{ + ecma_value_t key; /**< key value */ + ecma_value_t value; /**< value of the key */ +} ecma_container_pair_t; + +/** + * Size of a single element (in ecma_value_t unit). + */ +#define ECMA_CONTAINER_VALUE_SIZE 1 + +/** + * Size of a key - value pair (in ecma_value_t unit). + */ +#define ECMA_CONTAINER_PAIR_SIZE 2 + +/** + * Size of the internal buffer. + */ +#define ECMA_CONTAINER_GET_SIZE(container_p) \ + (container_p->buffer_p[0]) + +/** + * Remove the size field of the internal buffer. + */ +#define ECMA_CONTAINER_SET_SIZE(container_p, size) \ + (container_p->buffer_p[0] = (ecma_value_t) (size)) + +/** + * Number of entries of the internal buffer. + */ +#define ECMA_CONTAINER_ENTRY_COUNT(collection_p) \ + (collection_p->item_count - 1) + +/** + * Pointer to the first entry of the internal buffer. + */ +#define ECMA_CONTAINER_START(collection_p) \ + (collection_p->buffer_p + 1) + #endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ typedef enum @@ -1429,9 +1473,7 @@ typedef enum ECMA_STRING_CONTAINER_SYMBOL, /**< the ecma-string is a symbol */ - ECMA_STRING_CONTAINER_MAP_KEY, /**< the ecma-string is a map key string */ - - ECMA_STRING_CONTAINER__MAX = ECMA_STRING_CONTAINER_MAP_KEY /**< maximum value */ + ECMA_STRING_CONTAINER__MAX = ECMA_STRING_CONTAINER_SYMBOL /**< maximum value */ } ecma_string_container_t; /** diff --git a/jerry-core/ecma/base/ecma-helpers-string.c b/jerry-core/ecma/base/ecma-helpers-string.c index edc769689..3cd53da91 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.c +++ b/jerry-core/ecma/base/ecma-helpers-string.c @@ -240,41 +240,6 @@ ecma_prop_name_is_symbol (ecma_string_t *string_p) /**< ecma-string */ } /* ecma_prop_name_is_symbol */ #endif /* ENABLED (JERRY_ES2015) */ -#if ENABLED (JERRY_ES2015_BUILTIN_MAP) || ENABLED (JERRY_ES2015_BUILTIN_SET) -/** - * Allocate new ecma-string and fill it with reference to the map key descriptor - * - * @return pointer to ecma-string descriptor - */ -ecma_string_t * -ecma_new_map_key_string (ecma_value_t value) /**< non prop-name ecma-value */ -{ - JERRY_ASSERT (!ecma_is_value_prop_name (value)); - - ecma_extended_string_t *string_p = ecma_alloc_extended_string (); - string_p->header.refs_and_container = ECMA_STRING_REF_ONE | ECMA_STRING_CONTAINER_MAP_KEY; - string_p->u.value = ecma_copy_value_if_not_object (value); - string_p->header.u.hash = (lit_string_hash_t) (ecma_is_value_simple (value) ? value : 0); - - return (ecma_string_t *) string_p; -} /* ecma_new_map_key_string */ - -/** - * Check whether an ecma-string contains a map key string - * - * @return true - if the ecma-string contains a map key string - * false - otherwise - */ -bool -ecma_prop_name_is_map_key (ecma_string_t *string_p) /**< ecma-string */ -{ - JERRY_ASSERT (string_p != NULL); - - return (!ECMA_IS_DIRECT_STRING (string_p) - && ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAP_KEY); -} /* ecma_prop_name_is_map_key */ -#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) || ENABLED (JERRY_ES2015_BUILTIN_SET) */ - /** * Allocate new UTF8 ecma-string and fill it with characters from the given utf8 buffer * @@ -889,15 +854,6 @@ ecma_destroy_ecma_string (ecma_string_t *string_p) /**< ecma-string */ return; } #endif /* ENABLED (JERRY_ES2015) */ -#if ENABLED (JERRY_ES2015_BUILTIN_MAP) || ENABLED (JERRY_ES2015_BUILTIN_SET) - case ECMA_STRING_CONTAINER_MAP_KEY: - { - ecma_extended_string_t *key_p = (ecma_extended_string_t *) string_p; - ecma_free_value_if_not_object (key_p->u.value); - ecma_dealloc_extended_string (key_p); - return; - } -#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) || ENABLED (JERRY_ES2015_BUILTIN_SET) */ default: { JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC @@ -1786,14 +1742,6 @@ ecma_compare_ecma_strings (const ecma_string_t *string1_p, /**< ecma-string */ } #endif /* ENABLED (JERRY_ES2015) */ -#if ENABLED (JERRY_ES2015_BUILTIN_MAP) - if (string1_container == ECMA_STRING_CONTAINER_MAP_KEY) - { - return ecma_op_same_value_zero (((ecma_extended_string_t *) string1_p)->u.value, - ((ecma_extended_string_t *) string2_p)->u.value); - } -#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ - return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } /* ecma_compare_ecma_strings */ @@ -1840,14 +1788,6 @@ ecma_compare_ecma_non_direct_strings (const ecma_string_t *string1_p, /**< ecma- } #endif /* ENABLED (JERRY_ES2015) */ -#if ENABLED (JERRY_ES2015_BUILTIN_MAP) - if (string1_container == ECMA_STRING_CONTAINER_MAP_KEY) - { - return ecma_op_same_value_zero (((ecma_extended_string_t *) string1_p)->u.value, - ((ecma_extended_string_t *) string2_p)->u.value); - } -#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ - return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } /* ecma_compare_ecma_non_direct_strings */ diff --git a/jerry-core/ecma/operations/ecma-container-object.c b/jerry-core/ecma/operations/ecma-container-object.c index 339ec3f2c..ac5332e21 100644 --- a/jerry-core/ecma/operations/ecma-container-object.c +++ b/jerry-core/ecma/operations/ecma-container-object.c @@ -35,6 +35,325 @@ * @{ */ +/** + * Create a new internal buffer. + * + * Note: + * The first element of the collection tracks the size of the buffer. + * ECMA_VALUE_EMPTY values are not calculated into the size. + * + * @return pointer to the internal buffer + */ +static inline ecma_collection_t * +ecma_op_create_internal_buffer (void) +{ + ecma_collection_t *collection_p = ecma_new_collection (); + ecma_collection_push_back (collection_p, (ecma_value_t) 0); + + return collection_p; +} /* ecma_op_create_internal_buffer */ + +/** + * Append values to the internal buffer. + */ +static void +ecma_op_internal_buffer_append (ecma_collection_t *container_p, /**< internal container pointer */ + ecma_value_t key_arg, /**< key argument */ + ecma_value_t value_arg, /**< value argument */ + lit_magic_string_id_t lit_id) /**< class id */ +{ + JERRY_ASSERT (container_p != NULL); + + ecma_collection_push_back (container_p, ecma_copy_value_if_not_object (key_arg)); + + if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) + { + ecma_collection_push_back (container_p, ecma_copy_value_if_not_object (value_arg)); + } + + ECMA_CONTAINER_SET_SIZE (container_p, ECMA_CONTAINER_GET_SIZE (container_p) + 1); +} /* ecma_op_internal_buffer_append */ + +/** + * Update the value of a given entry. + */ +static inline void +ecma_op_internal_buffer_update (ecma_value_t *entry_p, /**< entry pointer */ + ecma_value_t value_arg, /**< value argument */ + lit_magic_string_id_t lit_id) /**< class id */ +{ + JERRY_ASSERT (entry_p != NULL); + + if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) + { + ecma_free_value_if_not_object (((ecma_container_pair_t *) entry_p)->value); + + ((ecma_container_pair_t *) entry_p)->value = ecma_copy_value_if_not_object (value_arg); + } +} /* ecma_op_internal_buffer_update */ + +/** + * Delete element from the internal buffer. + */ +static void +ecma_op_internal_buffer_delete (ecma_collection_t *container_p, /**< internal container pointer */ + ecma_container_pair_t *entry_p, /**< entry pointer */ + lit_magic_string_id_t lit_id) /**< class id */ +{ + JERRY_ASSERT (container_p != NULL); + JERRY_ASSERT (entry_p != NULL); + + ecma_free_value_if_not_object (entry_p->key); + entry_p->key = ECMA_VALUE_EMPTY; + + if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) + { + ecma_free_value_if_not_object (entry_p->value); + entry_p->value = ECMA_VALUE_EMPTY; + } + + ECMA_CONTAINER_SET_SIZE (container_p, ECMA_CONTAINER_GET_SIZE (container_p) - 1); +} /* ecma_op_internal_buffer_delete */ + +/** + * Find an entry in the collection. + * + * @return pointer to the appropriate entry. + */ +static ecma_value_t * +ecma_op_internal_buffer_find (ecma_collection_t *container_p, /**< internal container pointer */ + ecma_value_t key_arg, /**< key argument */ + lit_magic_string_id_t lit_id) /**< class id */ +{ + JERRY_ASSERT (container_p != NULL); + + uint8_t entry_size = ecma_op_container_entry_size (lit_id); + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + + for (uint32_t i = 0; i < entry_count; i += entry_size) + { + ecma_value_t *entry_p = start_p + i; + + if (ecma_op_same_value_zero (*entry_p, key_arg)) + { + return entry_p; + } + } + + return NULL; +} /* ecma_op_internal_buffer_find */ + +/** + * Get the value that belongs to the key. + * + * Note: in case of Set containers, the values are the same as the keys. + * + * @return ecma value + */ +static ecma_value_t +ecma_op_container_get_value (ecma_value_t *entry_p, /**< entry (key) pointer */ + lit_magic_string_id_t lit_id) /**< class id */ +{ + JERRY_ASSERT (entry_p != NULL); + + if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) + { + return ((ecma_container_pair_t *) entry_p)->value; + } + + return *entry_p; +} /* ecma_op_container_get_value */ + +/** + * Get the size (in ecma_value_t) of the stored entries. + * + * @return size of the entries. + */ +uint8_t +ecma_op_container_entry_size (lit_magic_string_id_t lit_id) /**< class id */ +{ + if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_MAP_UL) + { + return ECMA_CONTAINER_PAIR_SIZE; + } + + return ECMA_CONTAINER_VALUE_SIZE; +} /* ecma_op_container_entry_size */ + +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) +/** + * Release the entries in the WeakSet container. + */ +static void +ecma_op_container_free_weakset_entries (ecma_object_t *object_p, /**< object pointer */ + ecma_collection_t *container_p) /** internal buffer pointer */ +{ + JERRY_ASSERT (object_p != NULL); + JERRY_ASSERT (container_p != NULL); + + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + + for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE) + { + ecma_value_t *entry_p = start_p + i; + + if (ecma_is_value_empty (*entry_p)) + { + continue; + } + + ecma_op_container_unref_weak (ecma_get_object_from_value (*entry_p), ecma_make_object_value (object_p)); + ecma_op_container_remove_weak_entry (object_p, *entry_p); + + *entry_p = ECMA_VALUE_EMPTY; + } +} /* ecma_op_container_free_weakset_entries */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ + +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) +/** + * Release the entries in the WeakMap container. + */ +static void +ecma_op_container_free_weakmap_entries (ecma_object_t *object_p, /**< object pointer */ + ecma_collection_t *container_p) /**< internal buffer pointer */ +{ + JERRY_ASSERT (object_p != NULL); + JERRY_ASSERT (container_p != NULL); + + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + + for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) + { + ecma_container_pair_t *entry_p = (ecma_container_pair_t *)(start_p + i); + + if (ecma_is_value_empty (entry_p->key)) + { + continue; + } + + ecma_op_container_unref_weak (ecma_get_object_from_value (entry_p->key), ecma_make_object_value (object_p)); + ecma_op_container_remove_weak_entry (object_p, entry_p->key); + + ecma_free_value_if_not_object (entry_p->value); + + entry_p->key = ECMA_VALUE_EMPTY; + entry_p->value = ECMA_VALUE_EMPTY; + } +} /* ecma_op_container_free_weakmap_entries */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ + +#if ENABLED (JERRY_ES2015_BUILTIN_SET) +/** + * Release the entries in the Set container. + */ +static void +ecma_op_container_free_set_entries (ecma_collection_t *container_p) +{ + JERRY_ASSERT (container_p != NULL); + + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + + for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE) + { + ecma_value_t *entry_p = start_p + i; + + if (ecma_is_value_empty (*entry_p)) + { + continue; + } + + ecma_free_value_if_not_object (*entry_p); + *entry_p = ECMA_VALUE_EMPTY; + } +} /* ecma_op_container_free_set_entries */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ + +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) +/** + * Release the entries in the Map container. + */ +static void +ecma_op_container_free_map_entries (ecma_collection_t *container_p) +{ + JERRY_ASSERT (container_p != NULL); + + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); + + for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) + { + ecma_container_pair_t *entry_p = (ecma_container_pair_t *)(start_p + i); + + if (ecma_is_value_empty (entry_p->key)) + { + continue; + } + + ecma_free_value_if_not_object (entry_p->key); + ecma_free_value_if_not_object (entry_p->value); + + entry_p->key = ECMA_VALUE_EMPTY; + entry_p->value = ECMA_VALUE_EMPTY; + } +} /* ecma_op_container_free_map_entries */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ + +/** + * Release the internal buffer and the stored entries. + */ +void +ecma_op_container_free_entries (ecma_object_t *object_p) /**< collection object pointer */ +{ + JERRY_ASSERT (object_p != NULL); + + ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + + switch (map_object_p->u.class_prop.class_id) + { +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) + case LIT_MAGIC_STRING_WEAKSET_UL: + { + ecma_op_container_free_weakset_entries (object_p, container_p); + break; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) + case LIT_MAGIC_STRING_WEAKMAP_UL: + { + ecma_op_container_free_weakmap_entries (object_p, container_p); + break; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_SET) + case LIT_MAGIC_STRING_SET_UL: + { + ecma_op_container_free_set_entries (container_p); + break; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) + case LIT_MAGIC_STRING_MAP_UL: + { + ecma_op_container_free_map_entries (container_p); + break; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ + default: + { + break; + } + } + + ECMA_CONTAINER_SET_SIZE (container_p, 0); +} /* ecma_op_container_free_entries */ + /** * Handle calling [[Construct]] of built-in Map/Set like objects * @@ -52,29 +371,22 @@ ecma_op_container_create (const ecma_value_t *arguments_list_p, /**< arguments l || lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL); - ecma_object_t *internal_object_p = ecma_create_object (NULL, - sizeof (ecma_extended_object_t), - ECMA_OBJECT_TYPE_CLASS); - - ecma_extended_object_t *container_p = (ecma_extended_object_t *) internal_object_p; - container_p->u.class_prop.class_id = LIT_INTERNAL_MAGIC_STRING_CONTAINER; - container_p->u.class_prop.extra_info = ECMA_CONTAINER_FLAGS_EMPTY; - container_p->u.class_prop.u.length = 0; - - if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL) - { - container_p->u.class_prop.extra_info |= ECMA_CONTAINER_FLAGS_WEAK; - } + ecma_collection_t *container_p = ecma_op_create_internal_buffer (); ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (proto_id), sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *map_obj_p = (ecma_extended_object_t *) object_p; + map_obj_p->u.class_prop.extra_info = ECMA_CONTAINER_FLAGS_EMPTY; map_obj_p->u.class_prop.class_id = (uint16_t) lit_id; - ECMA_SET_INTERNAL_VALUE_POINTER (map_obj_p->u.class_prop.u.value, container_p); - ecma_deref_object (internal_object_p); + if (lit_id == LIT_MAGIC_STRING_WEAKMAP_UL || lit_id == LIT_MAGIC_STRING_WEAKSET_UL) + { + map_obj_p->u.class_prop.extra_info |= ECMA_CONTAINER_FLAGS_WEAK; + } + + ECMA_SET_INTERNAL_VALUE_POINTER (map_obj_p->u.class_prop.u.value, container_p); ecma_value_t set_value = ecma_make_object_value (object_p); ecma_value_t result = set_value; @@ -261,37 +573,6 @@ ecma_op_container_get_object (ecma_value_t this_arg, /**< this argument */ return NULL; } /* ecma_op_container_get_object */ -/** - * Creates a property key for the internal object from the given argument - * - * Note: - * This operation does not increase the reference counter of strings and symbols - * - * @return property key - */ -static ecma_string_t * -ecma_op_container_to_key (ecma_value_t key_arg) /**< key argument */ -{ - if (ecma_is_value_prop_name (key_arg)) - { - ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (key_arg); - ecma_ref_ecma_string (prop_name_p); - return prop_name_p; - } - - if (ecma_is_value_integer_number (key_arg)) - { - ecma_integer_value_t integer = ecma_get_integer_from_value (key_arg); - - if (JERRY_LIKELY (integer > 0 && integer <= ECMA_DIRECT_STRING_MAX_IMM)) - { - return (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_ECMA_INTEGER, (uintptr_t) integer); - } - } - - return ecma_new_map_key_string (key_arg); -} /* ecma_op_container_to_key */ - /** * Returns with the size of the Map/Set object. * @@ -308,9 +589,10 @@ ecma_op_container_size (ecma_value_t this_arg, /**< this argument */ return ECMA_VALUE_ERROR; } - ecma_extended_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_extended_object_t, - map_object_p->u.class_prop.u.value); - return ecma_make_uint32_value (container_p->u.class_prop.u.length); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + + return ecma_make_uint32_value (ECMA_CONTAINER_GET_SIZE (container_p)); } /* ecma_op_container_size */ /** @@ -338,26 +620,22 @@ ecma_op_container_get (ecma_value_t this_arg, /**< this argument */ } #endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ - ecma_extended_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_extended_object_t, - map_object_p->u.class_prop.u.value); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); - if (container_p->u.class_prop.u.length == 0) + if (ECMA_CONTAINER_GET_SIZE (container_p) == 0) { return ECMA_VALUE_UNDEFINED; } - ecma_string_t *prop_name_p = ecma_op_container_to_key (key_arg); + ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); - ecma_property_t *property_p = ecma_find_named_property ((ecma_object_t *) container_p, prop_name_p); - - ecma_deref_ecma_string (prop_name_p); - - if (property_p == NULL || ecma_is_value_empty (ECMA_PROPERTY_VALUE_PTR (property_p)->value)) + if (entry_p == NULL) { return ECMA_VALUE_UNDEFINED; } - return ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); + return ecma_copy_value (((ecma_container_pair_t *) entry_p)->value); } /* ecma_op_container_get */ /** @@ -378,30 +656,25 @@ ecma_op_container_has (ecma_value_t this_arg, /**< this argument */ return ECMA_VALUE_ERROR; } - ecma_extended_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_extended_object_t, - map_object_p->u.class_prop.u.value); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); #if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) - if ((container_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0 + if ((map_object_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0 && !ecma_is_value_object (key_arg)) { return ECMA_VALUE_FALSE; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ - if (container_p->u.class_prop.u.length == 0) + if (ECMA_CONTAINER_GET_SIZE (container_p) == 0) { return ECMA_VALUE_FALSE; } - ecma_string_t *prop_name_p = ecma_op_container_to_key (key_arg); + ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); - ecma_property_t *property_p = ecma_find_named_property ((ecma_object_t *) container_p, prop_name_p); - - ecma_deref_ecma_string (prop_name_p); - - return ecma_make_boolean_value (property_p != NULL - && !ecma_is_value_empty (ECMA_PROPERTY_VALUE_PTR (property_p)->value)); + return ecma_make_boolean_value (entry_p != NULL); } /* ecma_op_container_has */ /** @@ -467,52 +740,36 @@ ecma_op_container_set (ecma_value_t this_arg, /**< this argument */ return ECMA_VALUE_ERROR; } - ecma_extended_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_extended_object_t, - map_object_p->u.class_prop.u.value); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); #if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) - if ((container_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0 + if ((map_object_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0 && !ecma_is_value_object (key_arg)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Key must be an object")); } #endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ - ecma_string_t *prop_name_p = ecma_op_container_to_key (key_arg); + ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); - ecma_property_t *property_p = ecma_find_named_property ((ecma_object_t *) container_p, prop_name_p); - - if (property_p == NULL) + if (entry_p == NULL) { - ecma_property_value_t *value_p = ecma_create_named_data_property ((ecma_object_t *) container_p, - prop_name_p, - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, - NULL); - value_p->value = ecma_copy_value_if_not_object (value_arg); - container_p->u.class_prop.u.length++; + ecma_op_internal_buffer_append (container_p, key_arg, value_arg, lit_id); #if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) - if ((container_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0) + if ((map_object_p->u.class_prop.extra_info & ECMA_CONTAINER_FLAGS_WEAK) != 0) { ecma_object_t *key_p = ecma_get_object_from_value (key_arg); - ecma_op_container_set_weak (key_p, container_p); + ecma_op_container_set_weak (key_p, map_object_p); } #endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) || ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ - } else { - if (ecma_is_value_empty (ECMA_PROPERTY_VALUE_PTR (property_p)->value)) - { - container_p->u.class_prop.u.length++; - } - - ecma_named_data_property_assign_value ((ecma_object_t *) container_p, - ECMA_PROPERTY_VALUE_PTR (property_p), - value_arg); + ecma_op_internal_buffer_update (entry_p, value_arg, lit_id); } - ecma_deref_ecma_string (prop_name_p); ecma_ref_object ((ecma_object_t *) map_object_p); return this_arg; } /* ecma_op_container_set */ @@ -544,59 +801,28 @@ ecma_op_container_foreach (ecma_value_t this_arg, /**< this argument */ JERRY_ASSERT (ecma_is_value_object (predicate)); ecma_object_t *func_object_p = ecma_get_object_from_value (predicate); - - ecma_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, - map_object_p->u.class_prop.u.value); - - ecma_collection_t *props_p = ecma_op_object_get_property_names (container_p, ECMA_LIST_NO_OPTS); - - ecma_value_t *buffer_p = props_p->buffer_p; - ecma_value_t ret_value = ECMA_VALUE_UNDEFINED; - ecma_ref_object (container_p); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); - for (uint32_t i = 0; i < props_p->item_count; i++) + uint8_t entry_size = ecma_op_container_entry_size (lit_id); + + for (uint32_t i = 0; i < ECMA_CONTAINER_ENTRY_COUNT (container_p); i += entry_size) { - ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (buffer_p[i]); - ecma_property_t *property_p = ecma_find_named_property (container_p, prop_name_p); - JERRY_ASSERT (property_p != NULL); + ecma_value_t *entry_p = ECMA_CONTAINER_START (container_p) + i; - if (ecma_is_value_empty (ECMA_PROPERTY_VALUE_PTR (property_p)->value)) + if (ecma_is_value_empty (*entry_p)) { continue; } - ecma_value_t value = ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); - ecma_value_t key_arg; - - if (lit_id == LIT_MAGIC_STRING_SET_UL) - { - key_arg = value; - } - else if (ecma_prop_name_is_map_key (prop_name_p)) - { - key_arg = ((ecma_extended_string_t *) prop_name_p)->u.value; - } - else - { - if (ECMA_IS_DIRECT_STRING (prop_name_p) - && ECMA_GET_DIRECT_STRING_TYPE (prop_name_p) == ECMA_DIRECT_STRING_ECMA_INTEGER) - { - key_arg = ecma_make_uint32_value ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (prop_name_p)); - } - else - { - key_arg = buffer_p[i]; - } - } - - ecma_value_t call_args[] = { value, key_arg, this_arg }; + ecma_value_t key_arg = *entry_p; + ecma_value_t value_arg = ecma_op_container_get_value (entry_p, lit_id); + ecma_value_t call_args[] = { value_arg, key_arg, this_arg }; ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 3); - ecma_free_value (value); - if (ECMA_IS_VALUE_ERROR (call_value)) { ret_value = call_value; @@ -606,9 +832,6 @@ ecma_op_container_foreach (ecma_value_t this_arg, /**< this argument */ ecma_free_value (call_value); } - ecma_deref_object (container_p); - ecma_collection_free (props_p); - return ret_value; } /* ecma_op_container_foreach */ @@ -629,18 +852,7 @@ ecma_op_container_clear (ecma_value_t this_arg, /**< this argument */ return ECMA_VALUE_ERROR; } - ecma_object_t *internal_object_p = ecma_create_object (NULL, - sizeof (ecma_extended_object_t), - ECMA_OBJECT_TYPE_CLASS); - - ecma_extended_object_t *container_p = (ecma_extended_object_t *) internal_object_p; - container_p->u.class_prop.class_id = LIT_INTERNAL_MAGIC_STRING_CONTAINER; - container_p->u.class_prop.extra_info = ECMA_CONTAINER_FLAGS_EMPTY; - container_p->u.class_prop.u.length = 0; - - ECMA_SET_INTERNAL_VALUE_POINTER (map_object_p->u.class_prop.u.value, internal_object_p); - - ecma_deref_object (internal_object_p); + ecma_op_container_free_entries ((ecma_object_t *) map_object_p); return ECMA_VALUE_UNDEFINED; } /* ecma_op_container_clear */ @@ -663,25 +875,17 @@ ecma_op_container_delete (ecma_value_t this_arg, /**< this argument */ return ECMA_VALUE_ERROR; } - ecma_extended_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_extended_object_t, - map_object_p->u.class_prop.u.value); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); - ecma_string_t *prop_name_p = ecma_op_container_to_key (key_arg); + ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); - ecma_property_t *property_p = ecma_find_named_property ((ecma_object_t *) container_p, prop_name_p); - - ecma_deref_ecma_string (prop_name_p); - - if (property_p == NULL || ecma_is_value_empty (ECMA_PROPERTY_VALUE_PTR (property_p)->value)) + if (entry_p == NULL) { return ECMA_VALUE_FALSE; } - ecma_named_data_property_assign_value ((ecma_object_t *) container_p, - ECMA_PROPERTY_VALUE_PTR (property_p), - ECMA_VALUE_EMPTY); - container_p->u.class_prop.u.length--; - + ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, lit_id); return ECMA_VALUE_TRUE; } /* ecma_op_container_delete */ @@ -708,28 +912,21 @@ ecma_op_container_delete_weak (ecma_value_t this_arg, /**< this argument */ return ECMA_VALUE_FALSE; } - ecma_extended_object_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_extended_object_t, - map_object_p->u.class_prop.u.value); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); - ecma_string_t *prop_name_p = ecma_op_container_to_key (key_arg); + ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, lit_id); - ecma_property_t *property_p = ecma_find_named_property ((ecma_object_t *) container_p, prop_name_p); - - ecma_deref_ecma_string (prop_name_p); - - if (property_p == NULL) + if (entry_p == NULL) { return ECMA_VALUE_FALSE; } - ecma_delete_property ((ecma_object_t *) container_p, ECMA_PROPERTY_VALUE_PTR (property_p)); + ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, lit_id); - ecma_object_t *internal_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, - map_object_p->u.class_prop.u.value); ecma_object_t *key_object_p = ecma_get_object_from_value (key_arg); - ecma_op_container_unref_weak (key_object_p, ecma_make_object_value (internal_obj_p)); + ecma_op_container_unref_weak (key_object_p, ecma_make_object_value ((ecma_object_t *) map_object_p)); - container_p->u.class_prop.u.length--; return ECMA_VALUE_TRUE; } /* ecma_op_container_delete_weak */ @@ -764,18 +961,19 @@ ecma_op_container_unref_weak (ecma_object_t *object_p, /**< this argument */ * Helper function to remove a key/value pair from a weak container object */ void -ecma_op_container_remove_weak_entry (ecma_object_t *container_p, /**< internal container object */ +ecma_op_container_remove_weak_entry (ecma_object_t *object_p, /**< internal container object */ ecma_value_t key_arg) /**< key */ { - ecma_string_t *prop_name_p = ecma_new_map_key_string (key_arg); + ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; - ecma_property_t *property_p = ecma_find_named_property (container_p, prop_name_p); - JERRY_ASSERT (property_p != NULL); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); - ecma_deref_ecma_string (prop_name_p); + ecma_value_t *entry_p = ecma_op_internal_buffer_find (container_p, key_arg, map_object_p->u.class_prop.class_id); - ecma_delete_property (container_p, ECMA_PROPERTY_VALUE_PTR (property_p)); - ((ecma_extended_object_t *) container_p)->u.class_prop.u.length--; + JERRY_ASSERT (entry_p != NULL); + + ecma_op_internal_buffer_delete (container_p, (ecma_container_pair_t *) entry_p, map_object_p->u.class_prop.class_id); } /* ecma_op_container_remove_weak_entry */ #if ENABLED (JERRY_ES2015) @@ -814,6 +1012,60 @@ ecma_op_container_create_iterator (ecma_value_t this_arg, /**< this argument */ type); } /* ecma_op_container_create_iterator */ +/** + * Get the index of the iterator object. + * + * @return index of the iterator. + */ +static uint32_t +ecma_op_iterator_get_index (ecma_object_t *iter_obj_p) /**< iterator object pointer */ +{ + uint32_t index = ((ecma_extended_object_t *) iter_obj_p)->u.pseudo_array.u1.iterator_index; + + if (JERRY_UNLIKELY (index == ECMA_ITERATOR_INDEX_LIMIT)) + { + ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); + ecma_property_t *property_p = ecma_find_named_property (iter_obj_p, prop_name_p); + ecma_property_value_t *value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + + return (uint32_t) (ecma_get_number_from_value (value_p->value)); + } + + return index; +} /* ecma_op_iterator_get_index */ + +/** + * Set the index of the iterator object. + */ +static void +ecma_op_iterator_set_index (ecma_object_t *iter_obj_p, /**< iterator object pointer */ + uint32_t index) /* iterator index to set */ +{ + if (JERRY_UNLIKELY (index >= ECMA_ITERATOR_INDEX_LIMIT)) + { + /* After the ECMA_ITERATOR_INDEX_LIMIT limit is reached the [[%Iterator%NextIndex]] + property is stored as an internal property */ + ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); + ecma_property_t *property_p = ecma_find_named_property (iter_obj_p, prop_name_p); + ecma_property_value_t *value_p; + + if (property_p == NULL) + { + value_p = ecma_create_named_data_property (iter_obj_p, prop_name_p, ECMA_PROPERTY_FLAG_WRITABLE, &property_p); + value_p->value = ecma_make_uint32_value (index); + } + else + { + value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + value_p->value = ecma_make_uint32_value (index); + } + } + else + { + ((ecma_extended_object_t *) iter_obj_p)->u.pseudo_array.u1.iterator_index = (uint16_t) index; + } +} /* ecma_op_iterator_set_index */ + /** * The %{Set, Map}IteratorPrototype% object's 'next' routine * @@ -853,97 +1105,44 @@ ecma_op_container_iterator_next (ecma_value_t this_val, /**< this argument */ } ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) (ecma_get_object_from_value (iterated_value)); + lit_magic_string_id_t lit_id = map_object_p->u.class_prop.class_id; - ecma_object_t *internal_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, - map_object_p->u.class_prop.u.value); - ecma_collection_t *props_p = ecma_op_object_get_property_names (internal_obj_p, ECMA_LIST_NO_OPTS); + ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, + map_object_p->u.class_prop.u.value); + uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); + uint32_t index = ecma_op_iterator_get_index (obj_p); - uint32_t length = props_p->item_count; - uint32_t index = ext_obj_p->u.pseudo_array.u1.iterator_index; - - if (JERRY_UNLIKELY (index == ECMA_ITERATOR_INDEX_LIMIT)) - { - /* After the ECMA_ITERATOR_INDEX_LIMIT limit is reached the [[%Iterator%NextIndex]] - property is stored as an internal property */ - ecma_string_t *prop_name_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX); - - ecma_property_t *property_p = ecma_find_named_property (obj_p, prop_name_p); - ecma_property_value_t *value_p; - - if (property_p == NULL) - { - value_p = ecma_create_named_data_property (obj_p, prop_name_p, ECMA_PROPERTY_FLAG_WRITABLE, &property_p); - value_p->value = ecma_make_uint32_value (index); - } - else - { - value_p = ECMA_PROPERTY_VALUE_PTR (property_p); - index = (uint32_t) (ecma_get_number_from_value (value_p->value) + 1); - value_p->value = ecma_make_uint32_value (index); - } - } - else - { - ext_obj_p->u.pseudo_array.u1.iterator_index++; - } - - if (index >= length) + if (index == entry_count) { ext_obj_p->u.pseudo_array.u2.iterated_value = ECMA_VALUE_EMPTY; - ecma_collection_free (props_p); + return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); } + uint8_t entry_size = ecma_op_container_entry_size (lit_id); uint8_t iterator_kind = ext_obj_p->u.pseudo_array.extra_info; - - ecma_value_t *buffer_p = props_p->buffer_p; - + ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); ecma_value_t ret_value = ECMA_VALUE_UNDEFINED; - for (uint32_t i = 0; i < props_p->item_count; i++) + for (uint32_t i = index; i < entry_count; i += entry_size) { - if (index > 0) - { - index--; - continue; - } + ecma_value_t *entry_p = start_p + i; - ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (buffer_p[i]); - ecma_property_t *property_p = ecma_find_named_property (internal_obj_p, prop_name_p); - JERRY_ASSERT (property_p != NULL); - - if (ecma_is_value_empty (ECMA_PROPERTY_VALUE_PTR (property_p)->value)) + if (ecma_is_value_empty (*entry_p)) { - if (i == props_p->item_count - 1) + if (i == (entry_count - entry_size)) { ret_value = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); + break; } + continue; } - ecma_value_t value = ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); - ecma_value_t key_arg; + ecma_op_iterator_set_index (obj_p, i + entry_size); - if (iterator_type == ECMA_PSEUDO_SET_ITERATOR) - { - key_arg = value; - } - else if (ecma_prop_name_is_map_key (prop_name_p)) - { - key_arg = ((ecma_extended_string_t *) prop_name_p)->u.value; - } - else - { - if (ECMA_IS_DIRECT_STRING (prop_name_p) - && ECMA_GET_DIRECT_STRING_TYPE (prop_name_p) == ECMA_DIRECT_STRING_ECMA_INTEGER) - { - key_arg = ecma_make_uint32_value ((uint32_t) ECMA_GET_DIRECT_STRING_VALUE (prop_name_p)); - } - else - { - key_arg = buffer_p[i]; - } - } + ecma_value_t key_arg = *entry_p; + ecma_value_t value_arg = ecma_op_container_get_value (entry_p, lit_id); if (iterator_kind == ECMA_ITERATOR_KEYS) { @@ -951,25 +1150,22 @@ ecma_op_container_iterator_next (ecma_value_t this_val, /**< this argument */ } else if (iterator_kind == ECMA_ITERATOR_VALUES) { - ret_value = ecma_create_iter_result_object (value, ECMA_VALUE_FALSE); + ret_value = ecma_create_iter_result_object (value_arg, ECMA_VALUE_FALSE); } else { JERRY_ASSERT (iterator_kind == ECMA_ITERATOR_KEYS_VALUES); ecma_value_t entry_array_value; - entry_array_value = ecma_create_array_from_iter_element (value, key_arg); + entry_array_value = ecma_create_array_from_iter_element (value_arg, key_arg); ret_value = ecma_create_iter_result_object (entry_array_value, ECMA_VALUE_FALSE); ecma_free_value (entry_array_value); } - ecma_free_value (value); break; } - ecma_collection_free (props_p); - return ret_value; } /* ecma_op_container_iterator_next */ diff --git a/jerry-core/ecma/operations/ecma-container-object.h b/jerry-core/ecma/operations/ecma-container-object.h index 9c82f5042..ec30bb120 100644 --- a/jerry-core/ecma/operations/ecma-container-object.h +++ b/jerry-core/ecma/operations/ecma-container-object.h @@ -28,6 +28,7 @@ * @{ */ +uint8_t ecma_op_container_entry_size (lit_magic_string_id_t lit_id); ecma_value_t ecma_op_container_create (const ecma_value_t *arguments_list_p, ecma_length_t arguments_list_len, lit_magic_string_id_t lit_id, ecma_builtin_id_t proto_id); ecma_value_t ecma_op_container_size (ecma_value_t this_arg, lit_magic_string_id_t lit_id); @@ -42,6 +43,7 @@ ecma_value_t ecma_op_container_delete (ecma_value_t this_arg, ecma_value_t key_a ecma_value_t ecma_op_container_delete_weak (ecma_value_t this_arg, ecma_value_t key_arg, lit_magic_string_id_t lit_id); void ecma_op_container_unref_weak (ecma_object_t *object_p, ecma_value_t ref_holder); void ecma_op_container_remove_weak_entry (ecma_object_t *container_p, ecma_value_t key_arg); +void ecma_op_container_free_entries (ecma_object_t *object_p); ecma_value_t ecma_op_container_create_iterator (ecma_value_t this_arg, uint8_t type, lit_magic_string_id_t lit_id, ecma_builtin_id_t proto_id, ecma_pseudo_array_type_t iterator_type); ecma_value_t ecma_op_container_iterator_next (ecma_value_t this_val, ecma_pseudo_array_type_t iterator_type); diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 745171831..6ca3f91ed 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -72,7 +72,6 @@ typedef enum LIT_FIRST_INTERNAL_MAGIC_STRING = LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< first index of internal * magic strings */ LIT_INTERNAL_MAGIC_STRING_WEAK_REFS, /**< Weak references to the current object */ - LIT_INTERNAL_MAGIC_STRING_CONTAINER, /**< Literal ID for internal container objects */ LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT, /**< Internal object ID for internal properties */ LIT_MAGIC_STRING__COUNT /**< number of magic strings */ } lit_magic_string_id_t; diff --git a/tests/jerry/es2015/map-iterators.js b/tests/jerry/es2015/map-iterators.js index fc327f098..d65e34e9e 100644 --- a/tests/jerry/es2015/map-iterators.js +++ b/tests/jerry/es2015/map-iterators.js @@ -32,13 +32,15 @@ methods.forEach(function (method) { } }); -var m = new Map([{0: '0', 1: 0}, +var testArray = [{0: '0', 1: 0}, {0: '1', 1: 1}, {0: '2', 1: 2}, {0: '3', 1: 3}, {0: '4', 1: 4}, {0: '5', 1: 5}, - {0: '6', 1: 6}]); + {0: '6', 1: 6}]; + +var m = new Map(testArray); methods.forEach(function(method) { assert(m[method]().toString() === '[object Map Iterator]'); @@ -122,3 +124,61 @@ for (var i = 0; i < elementCount; i++) { } assert(m.size === 0); + +m = new Map(testArray); +var loopCount = 0; +var expected = [{0: '0', 1: 0}, + {0: '2', 1: 2}, + {0: '4', 1: 4}, + {0: '6', 1: 6}, + {0: '1', 1: 1}, + {0: '3', 1: 3}, + {0: '5', 1: 5}] + +m.forEach(function(value, key) { + if (loopCount === 0) { + for (i = 0; i < testArray.length; i++) { + if (i % 2) { + m.delete(testArray[i][0]); + m.set(testArray[i][0], testArray[i][1]); + } + } + } + + assert (key === expected[loopCount][0]); + assert (value === expected[loopCount][1]); + + loopCount++; +}); + +assert(loopCount === expected.length); + +loopCount = 0; +expected = [{0: '0', 1: 0}, + {0: '1', 1: 1}]; + +for (var [key, value] of m) { + if (loopCount === 0) { + m.clear(); + m.set('1', 1); + } + + assert(key === expected[loopCount][0]); + assert(value === expected[loopCount][1]); + + loopCount++; +} + +m = new Map(testArray); +loopCount = 0; + +for (var [key, value] of m) { + if (loopCount === 0) { + m.delete('' + testArray.length - 1); + } + + assert(key === '' + loopCount); + assert(value === loopCount); + + loopCount++; +} diff --git a/tests/jerry/es2015/set-iterators.js b/tests/jerry/es2015/set-iterators.js index 136688173..31423a359 100644 --- a/tests/jerry/es2015/set-iterators.js +++ b/tests/jerry/es2015/set-iterators.js @@ -106,3 +106,49 @@ for (var i = 0; i < elementCount; i++) { } assert(s.size === 0); + +s = new Set ([0, 1]); +var expected = [0, 1, 2, 4, 5, 6, 3]; +var loopCount = 0; + +s.forEach(function(element) { + if (loopCount === 0) { + for (var i = 0; i < expected.length ; i++) { + s.add(i); + } + s.delete(3); + s.add(3); + } + assert(element === expected[loopCount++]); +}); + +assert(loopCount === expected.length); + +s = new Set([0, 1, 2, 3, 4, 5, 6]); +expected = [0, 1]; +loopCount = 0; + +for (var value of s) { + if (loopCount === 0) { + s.clear(); + s.add(1); + } + + assert(value === expected[loopCount++]); +} + +s = new Set([0]) +expected = [0, 1]; +loopCount = 0; + +for (var value of s) { + if (loopCount === 0) { + s.add(2); + s.delete(2); + s.add(3); + s.delete(3); + s.add(1); + } + + assert(value === expected[loopCount++]); +} diff --git a/tests/jerry/es2015/weakmap.js b/tests/jerry/es2015/weakmap.js index 2eb702c95..d842872ef 100644 --- a/tests/jerry/es2015/weakmap.js +++ b/tests/jerry/es2015/weakmap.js @@ -115,7 +115,7 @@ m1.set(k1, "str"); m1.set(k1, "4"); m1.set(k1, null); m1.set(k1, 42); -print (m1.has (k1)); +assert (m1.has (k1)); k1 = {}; gc();