Rework Map object (#2760)
This patch reworks the core of the builtin Map object. Advantages: - Provide sublinear access time for the elements via Lcache and property hashmap - This implementation is suitable for the builtin Set object as well Also add the missing 'forEach' routine for the builtin object as well. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
committed by
László Langó
parent
bc9efb07a5
commit
d2931c6e40
@@ -246,50 +246,6 @@ ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended
|
||||
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
|
||||
/**
|
||||
* Mark objects referenced by Map built-in.
|
||||
*/
|
||||
static void
|
||||
ecma_gc_mark_map_object (ecma_extended_object_t *ext_object_p) /**< extended object */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = (ecma_map_object_t *) ext_object_p;
|
||||
|
||||
jmem_cpointer_t first_chunk_cp = map_object_p->first_chunk_cp;
|
||||
|
||||
if (JERRY_UNLIKELY (first_chunk_cp == ECMA_NULL_POINTER))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ecma_value_t *item_p = ECMA_GET_NON_NULL_POINTER (ecma_map_object_chunk_t, first_chunk_cp)->items;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ecma_value_t item = *item_p++;
|
||||
|
||||
if (!ecma_is_value_pointer (item))
|
||||
{
|
||||
if (ecma_is_value_object (item))
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (item));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item_p = (ecma_value_t *) ecma_get_pointer_from_value (item);
|
||||
|
||||
if (item_p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* ecma_gc_mark_map_object */
|
||||
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
|
||||
/**
|
||||
* Mark objects as visited starting from specified object as root
|
||||
*/
|
||||
@@ -340,13 +296,6 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
case LIT_MAGIC_STRING_MAP_UL:
|
||||
{
|
||||
ecma_gc_mark_map_object (ext_object_p);
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
default:
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -968,8 +968,7 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
ecma_extended_object_t header; /**< header part */
|
||||
jmem_cpointer_t first_chunk_cp; /**< first chunk of item list */
|
||||
jmem_cpointer_t last_chunk_cp; /**< last chunk of item list */
|
||||
uint32_t size; /**< size of the map object */
|
||||
} ecma_map_object_t;
|
||||
|
||||
/**
|
||||
@@ -1273,6 +1272,7 @@ typedef enum
|
||||
ECMA_DIRECT_STRING_PTR = 0, /**< string is a string pointer, only used by property names */
|
||||
ECMA_DIRECT_STRING_MAGIC = 1, /**< string is a magic string */
|
||||
ECMA_DIRECT_STRING_UINT = 2, /**< string is an unsigned int */
|
||||
ECMA_DIRECT_STRING_ECMA_INTEGER = 3, /**< string is an ecma-integer */
|
||||
} ecma_direct_string_type_t;
|
||||
|
||||
/**
|
||||
@@ -1351,6 +1351,8 @@ 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_LITERAL_NUMBER, /**< a literal number which is used solely by the literal storage
|
||||
* so no string processing function supports this type except
|
||||
* the ecma_deref_ecma_string function. */
|
||||
@@ -1422,6 +1424,7 @@ typedef struct
|
||||
ecma_value_t lit_number; /**< number (see ECMA_STRING_LITERAL_NUMBER) */
|
||||
uint32_t common_uint32_field; /**< for zeroing and comparison in some cases */
|
||||
ecma_value_t symbol_descriptor; /**< symbol descriptor string-value */
|
||||
ecma_value_t value; /**< original key value corresponds to the map key string */
|
||||
} u;
|
||||
} ecma_string_t;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "ecma-alloc.h"
|
||||
#include "ecma-conversion.h"
|
||||
#include "ecma-gc.h"
|
||||
#include "ecma-globals.h"
|
||||
#include "ecma-helpers.h"
|
||||
@@ -236,6 +237,41 @@ ecma_prop_name_is_symbol (ecma_string_t *string_p) /**< ecma-string */
|
||||
} /* ecma_prop_name_is_symbol */
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
/**
|
||||
* 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_string_t *string_p = ecma_alloc_string ();
|
||||
string_p->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->hash = ecma_is_value_simple (value) ? (uint16_t) value : 0;
|
||||
|
||||
return 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) */
|
||||
|
||||
/**
|
||||
* Allocate new ecma-string and fill it with characters from the utf8 string
|
||||
*
|
||||
@@ -984,6 +1020,13 @@ ecma_deref_ecma_string (ecma_string_t *string_p) /**< ecma-string */
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
case ECMA_STRING_CONTAINER_MAP_KEY:
|
||||
{
|
||||
ecma_free_value_if_not_object (string_p->u.value);
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_UINT32_IN_DESC
|
||||
@@ -1865,6 +1908,13 @@ ecma_compare_ecma_non_direct_strings (const ecma_string_t *string1_p, /**< ecma-
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
if (string1_container == ECMA_STRING_CONTAINER_MAP_KEY)
|
||||
{
|
||||
return ecma_op_same_value_zero (string1_p->u.value, string2_p->u.value);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
|
||||
if (string1_container >= ECMA_STRING_CONTAINER_UINT32_IN_DESC)
|
||||
{
|
||||
return string1_p->u.common_uint32_field == string2_p->u.common_uint32_field;
|
||||
|
||||
@@ -222,6 +222,10 @@ lit_magic_string_id_t ecma_get_typeof_lit_id (ecma_value_t value);
|
||||
ecma_string_t *ecma_new_symbol_from_descriptor_string (ecma_value_t string_desc);
|
||||
bool ecma_prop_name_is_symbol (ecma_string_t *string_p);
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
ecma_string_t *ecma_new_map_key_string (ecma_value_t value);
|
||||
bool ecma_prop_name_is_map_key (ecma_string_t *string_p);
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
ecma_string_t *ecma_new_ecma_string_from_utf8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size);
|
||||
ecma_string_t *ecma_new_ecma_string_from_utf8_converted_to_cesu8 (const lit_utf8_byte_t *string_p,
|
||||
lit_utf8_size_t string_size);
|
||||
|
||||
@@ -65,6 +65,24 @@ ecma_builtin_map_prototype_object_delete (ecma_value_t this_arg, /**< this argum
|
||||
return ecma_op_map_delete (this_arg, key_arg);
|
||||
} /* ecma_builtin_map_prototype_object_delete */
|
||||
|
||||
/**
|
||||
* The Map.prototype object's 'forEach' routine
|
||||
*
|
||||
* See also:
|
||||
* ECMA-262 v6, 23.1.3.5
|
||||
*
|
||||
* @return ecma value
|
||||
* Returned value must be freed with ecma_free_value.
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_builtin_map_prototype_object_foreach (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t predicate, /**< callback function */
|
||||
ecma_value_t predicate_this_arg) /**< this argument for
|
||||
* invoke predicate */
|
||||
{
|
||||
return ecma_op_map_foreach (this_arg, predicate, predicate_this_arg);
|
||||
} /* ecma_builtin_map_prototype_object_foreach */
|
||||
|
||||
/**
|
||||
* The Map.prototype object's 'get' routine
|
||||
*
|
||||
|
||||
@@ -40,6 +40,7 @@ STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG,
|
||||
* (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */
|
||||
ROUTINE (LIT_MAGIC_STRING_CLEAR, ecma_builtin_map_prototype_object_clear, 0, 0)
|
||||
ROUTINE (LIT_MAGIC_STRING_DELETE, ecma_builtin_map_prototype_object_delete, 1, 1)
|
||||
ROUTINE (LIT_MAGIC_STRING_FOR_EACH_UL, ecma_builtin_map_prototype_object_foreach, 2, 1)
|
||||
ROUTINE (LIT_MAGIC_STRING_GET, ecma_builtin_map_prototype_object_get, 1, 1)
|
||||
ROUTINE (LIT_MAGIC_STRING_HAS, ecma_builtin_map_prototype_object_has, 1, 1)
|
||||
ROUTINE (LIT_MAGIC_STRING_SET, ecma_builtin_map_prototype_object_set, 2, 2)
|
||||
|
||||
@@ -156,6 +156,54 @@ ecma_op_same_value (ecma_value_t x, /**< ecma value */
|
||||
}
|
||||
} /* ecma_op_same_value */
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
/**
|
||||
* SameValueZero operation.
|
||||
*
|
||||
* See also:
|
||||
* ECMA-262 v6, 7.2.10
|
||||
*
|
||||
* @return true - if the value are same according to ECMA-defined SameValueZero algorithm,
|
||||
* false - otherwise
|
||||
*/
|
||||
bool
|
||||
ecma_op_same_value_zero (ecma_value_t x, /**< ecma value */
|
||||
ecma_value_t y) /**< ecma value */
|
||||
{
|
||||
if (ecma_is_value_number (x) && ecma_is_value_number (y))
|
||||
{
|
||||
ecma_number_t x_num = ecma_get_number_from_value (x);
|
||||
ecma_number_t y_num = ecma_get_number_from_value (y);
|
||||
|
||||
bool is_x_nan = ecma_number_is_nan (x_num);
|
||||
bool is_y_nan = ecma_number_is_nan (y_num);
|
||||
|
||||
if (is_x_nan || is_y_nan)
|
||||
{
|
||||
/*
|
||||
* If both are NaN
|
||||
* return true;
|
||||
* else
|
||||
* one of the numbers is NaN, and another - is not
|
||||
* return false;
|
||||
*/
|
||||
return (is_x_nan && is_y_nan);
|
||||
}
|
||||
|
||||
if (ecma_number_is_zero (x_num)
|
||||
&& ecma_number_is_zero (y_num)
|
||||
&& ecma_number_is_negative (x_num) != ecma_number_is_negative (y_num))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return (x_num == y_num);
|
||||
}
|
||||
|
||||
return ecma_op_same_value (x, y);
|
||||
} /* ecma_op_same_value_zero */
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
|
||||
/**
|
||||
* ToPrimitive operation.
|
||||
*
|
||||
|
||||
@@ -39,6 +39,9 @@ typedef enum
|
||||
|
||||
ecma_value_t ecma_op_check_object_coercible (ecma_value_t value);
|
||||
bool ecma_op_same_value (ecma_value_t x, ecma_value_t y);
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
bool ecma_op_same_value_zero (ecma_value_t x, ecma_value_t y);
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */
|
||||
ecma_value_t ecma_op_to_primitive (ecma_value_t value, ecma_preferred_type_hint_t preferred_type);
|
||||
bool ecma_op_to_boolean (ecma_value_t value);
|
||||
ecma_value_t ecma_op_to_number (ecma_value_t value);
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ecma-alloc.h"
|
||||
#include "ecma-builtins.h"
|
||||
#include "ecma-exceptions.h"
|
||||
#include "ecma-function-object.h"
|
||||
#include "ecma-gc.h"
|
||||
#include "ecma-helpers.h"
|
||||
#include "ecma-map-object.h"
|
||||
#include "ecma-property-hashmap.h"
|
||||
#include "ecma-objects.h"
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_MAP)
|
||||
@@ -29,8 +32,23 @@
|
||||
* @{
|
||||
*/
|
||||
|
||||
JERRY_STATIC_ASSERT (ECMA_MAP_OBJECT_ITEM_COUNT == 3,
|
||||
ecma_map_object_item_count_must_be_3);
|
||||
/**
|
||||
* Creates an empty object for the map object's internal slot.
|
||||
*
|
||||
* Note: The created object is not registered to the GC.
|
||||
*
|
||||
* @return ecma value of the created object
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_op_map_create_internal_object (void)
|
||||
{
|
||||
ecma_object_t *internal_object_p = ecma_alloc_object ();
|
||||
internal_object_p->type_flags_refs = (ECMA_OBJECT_TYPE_GENERAL | ECMA_OBJECT_FLAG_EXTENSIBLE | ECMA_OBJECT_REF_ONE);
|
||||
internal_object_p->property_list_or_bound_object_cp = JMEM_CP_NULL;
|
||||
internal_object_p->prototype_or_outer_reference_cp = JMEM_CP_NULL;
|
||||
|
||||
return ecma_make_object_value (internal_object_p);
|
||||
} /* ecma_op_map_create_internal_object */
|
||||
|
||||
/**
|
||||
* Handle calling [[Construct]] of built-in map like objects
|
||||
@@ -43,17 +61,14 @@ ecma_op_map_create (const ecma_value_t *arguments_list_p, /**< arguments list */
|
||||
{
|
||||
JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL);
|
||||
|
||||
ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_MAP_PROTOTYPE);
|
||||
ecma_object_t *object_p = ecma_create_object (prototype_obj_p,
|
||||
ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_MAP_PROTOTYPE),
|
||||
sizeof (ecma_map_object_t),
|
||||
ECMA_OBJECT_TYPE_CLASS);
|
||||
|
||||
ecma_map_object_t *map_object_p = (ecma_map_object_t *) object_p;
|
||||
map_object_p->header.u.class_prop.class_id = LIT_MAGIC_STRING_MAP_UL;
|
||||
map_object_p->header.u.class_prop.extra_info = 0;
|
||||
map_object_p->header.u.class_prop.u.length = 0;
|
||||
map_object_p->first_chunk_cp = ECMA_NULL_POINTER;
|
||||
map_object_p->last_chunk_cp = ECMA_NULL_POINTER;
|
||||
ecma_map_object_t *map_obj_p = (ecma_map_object_t *) object_p;
|
||||
map_obj_p->header.u.class_prop.class_id = LIT_MAGIC_STRING_MAP_UL;
|
||||
map_obj_p->header.u.class_prop.u.value = ecma_op_map_create_internal_object ();
|
||||
map_obj_p->size = 0;
|
||||
|
||||
return ecma_make_object_value (object_p);
|
||||
} /* ecma_op_map_create */
|
||||
@@ -86,6 +101,63 @@ ecma_op_map_get_object (ecma_value_t this_arg) /**< this argument */
|
||||
return NULL;
|
||||
} /* ecma_op_map_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_map_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_object (key_arg))
|
||||
{
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (key_arg);
|
||||
ecma_string_t *key_string_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_MAP_KEY);
|
||||
ecma_property_t *property_p = ecma_find_named_property (obj_p, key_string_p);
|
||||
ecma_string_t *object_key_string;
|
||||
|
||||
if (property_p == NULL)
|
||||
{
|
||||
object_key_string = ecma_new_map_key_string (key_arg);
|
||||
ecma_property_value_t *value_p = ecma_create_named_data_property (obj_p,
|
||||
key_string_p,
|
||||
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
|
||||
NULL);
|
||||
value_p->value = ecma_make_string_value (object_key_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
object_key_string = ecma_get_string_from_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value);
|
||||
|
||||
}
|
||||
|
||||
ecma_ref_ecma_string (object_key_string);
|
||||
return object_key_string;
|
||||
}
|
||||
|
||||
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_map_to_key */
|
||||
|
||||
/**
|
||||
* Returns with the size of the map object.
|
||||
*
|
||||
@@ -95,106 +167,15 @@ ecma_value_t
|
||||
ecma_op_map_size (ecma_value_t this_arg) /**< this argument */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
return ecma_make_uint32_value (map_object_p->header.u.class_prop.u.length);
|
||||
return ecma_make_uint32_value (map_object_p->size);
|
||||
} /* ecma_op_map_size */
|
||||
|
||||
/**
|
||||
* Linear search for the value in the map storage
|
||||
*
|
||||
* @return pointer to value if key is found
|
||||
* NULL otherwise
|
||||
*/
|
||||
static ecma_value_t *
|
||||
ecma_builtin_map_search (jmem_cpointer_t first_chunk_cp, /**< first chunk */
|
||||
ecma_value_t key) /**< key to search */
|
||||
{
|
||||
if (JERRY_UNLIKELY (first_chunk_cp == ECMA_NULL_POINTER))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ecma_map_object_chunk_t *chunk_p = ECMA_GET_NON_NULL_POINTER (ecma_map_object_chunk_t,
|
||||
first_chunk_cp);
|
||||
|
||||
bool is_direct = true;
|
||||
const ecma_string_t *key_str_p = NULL;
|
||||
ecma_number_t key_float = 0;
|
||||
|
||||
if (ecma_is_value_non_direct_string (key))
|
||||
{
|
||||
key_str_p = ecma_get_string_from_value (key);
|
||||
is_direct = false;
|
||||
}
|
||||
else if (ecma_is_value_float_number (key))
|
||||
{
|
||||
key_float = ecma_get_float_from_value (key);
|
||||
is_direct = false;
|
||||
}
|
||||
|
||||
ecma_value_t *item_p = chunk_p->items;
|
||||
ecma_value_t last_key = ECMA_VALUE_ARRAY_HOLE;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ecma_value_t item = *item_p++;
|
||||
|
||||
if (JERRY_UNLIKELY (item == ECMA_VALUE_ARRAY_HOLE))
|
||||
{
|
||||
JERRY_ASSERT (last_key == ECMA_VALUE_ARRAY_HOLE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JERRY_UNLIKELY (ecma_is_value_pointer (item)))
|
||||
{
|
||||
item_p = (ecma_value_t *) ecma_get_pointer_from_value (item);
|
||||
|
||||
if (item_p == NULL)
|
||||
{
|
||||
JERRY_ASSERT (last_key == ECMA_VALUE_ARRAY_HOLE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JERRY_ASSERT (!ecma_is_value_pointer (*item_p));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (last_key == ECMA_VALUE_ARRAY_HOLE)
|
||||
{
|
||||
last_key = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JERRY_LIKELY (is_direct))
|
||||
{
|
||||
if (key == last_key)
|
||||
{
|
||||
return item_p - 1;
|
||||
}
|
||||
}
|
||||
else if (key_str_p != NULL)
|
||||
{
|
||||
if (ecma_is_value_non_direct_string (last_key)
|
||||
&& ecma_compare_ecma_non_direct_strings (key_str_p, ecma_get_string_from_value (last_key)))
|
||||
{
|
||||
return item_p - 1;
|
||||
}
|
||||
}
|
||||
else if (ecma_is_value_float_number (last_key)
|
||||
&& ecma_get_float_from_value (last_key) == key_float)
|
||||
{
|
||||
return item_p - 1;
|
||||
}
|
||||
|
||||
last_key = ECMA_VALUE_ARRAY_HOLE;
|
||||
}
|
||||
}
|
||||
} /* ecma_builtin_map_search */
|
||||
|
||||
/**
|
||||
* The generic map prototype object's 'get' routine
|
||||
*
|
||||
@@ -206,19 +187,31 @@ ecma_op_map_get (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t key_arg) /**< key argument */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
ecma_value_t *value_p = ecma_builtin_map_search (map_object_p->first_chunk_cp, key_arg);
|
||||
|
||||
if (value_p == NULL)
|
||||
if (map_object_p->size == 0)
|
||||
{
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
return ecma_copy_value (*value_p);
|
||||
ecma_object_t *internal_obj_p = ecma_get_object_from_value (map_object_p->header.u.class_prop.u.value);
|
||||
|
||||
ecma_string_t *prop_name_p = ecma_op_map_to_key (key_arg);
|
||||
|
||||
ecma_property_t *property_p = ecma_find_named_property (internal_obj_p, prop_name_p);
|
||||
|
||||
ecma_deref_ecma_string (prop_name_p);
|
||||
|
||||
if (property_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
return ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value);
|
||||
} /* ecma_op_map_get */
|
||||
|
||||
/**
|
||||
@@ -232,12 +225,26 @@ ecma_op_map_has (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t key_arg) /**< key argument */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
return ecma_make_boolean_value (ecma_builtin_map_search (map_object_p->first_chunk_cp, key_arg) != NULL);
|
||||
if (map_object_p->size == 0)
|
||||
{
|
||||
return ECMA_VALUE_FALSE;
|
||||
}
|
||||
|
||||
ecma_object_t *internal_obj_p = ecma_get_object_from_value (map_object_p->header.u.class_prop.u.value);
|
||||
|
||||
ecma_string_t *prop_name_p = ecma_op_map_to_key (key_arg);
|
||||
|
||||
ecma_property_t *property_p = ecma_find_named_property (internal_obj_p, prop_name_p);
|
||||
|
||||
ecma_deref_ecma_string (prop_name_p);
|
||||
|
||||
return ecma_make_boolean_value (property_p != NULL);
|
||||
} /* ecma_op_map_has */
|
||||
|
||||
/**
|
||||
@@ -252,79 +259,34 @@ ecma_op_map_set (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t value_arg) /**< value argument */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
ecma_value_t *value_p = ecma_builtin_map_search (map_object_p->first_chunk_cp, key_arg);
|
||||
ecma_object_t *internal_obj_p = ecma_get_object_from_value (map_object_p->header.u.class_prop.u.value);
|
||||
|
||||
if (value_p == NULL)
|
||||
ecma_string_t *prop_name_p = ecma_op_map_to_key (key_arg);
|
||||
|
||||
ecma_property_t *property_p = ecma_find_named_property (internal_obj_p, prop_name_p);
|
||||
|
||||
if (property_p == NULL)
|
||||
{
|
||||
ecma_value_t *key_p = NULL;
|
||||
ecma_map_object_chunk_t *last_chunk_p = ECMA_GET_POINTER (ecma_map_object_chunk_t,
|
||||
map_object_p->last_chunk_cp);
|
||||
|
||||
if (last_chunk_p != NULL)
|
||||
{
|
||||
if (last_chunk_p->items[2] == ECMA_VALUE_ARRAY_HOLE)
|
||||
{
|
||||
key_p = last_chunk_p->items + 2;
|
||||
|
||||
if (last_chunk_p->items[1] == ECMA_VALUE_ARRAY_HOLE)
|
||||
{
|
||||
key_p = last_chunk_p->items + 1;
|
||||
value_p = last_chunk_p->items + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key_p == NULL || value_p == NULL)
|
||||
{
|
||||
size_t size = sizeof (ecma_map_object_chunk_t);
|
||||
ecma_map_object_chunk_t *new_chunk_p = (ecma_map_object_chunk_t *) jmem_heap_alloc_block (size);
|
||||
|
||||
new_chunk_p->items[ECMA_MAP_OBJECT_ITEM_COUNT] = ecma_make_pointer_value (NULL);
|
||||
|
||||
for (int i = 0; i < ECMA_MAP_OBJECT_ITEM_COUNT; i++)
|
||||
{
|
||||
new_chunk_p->items[i] = ECMA_VALUE_ARRAY_HOLE;
|
||||
}
|
||||
|
||||
ECMA_SET_NON_NULL_POINTER (map_object_p->last_chunk_cp, new_chunk_p);
|
||||
|
||||
if (last_chunk_p == NULL)
|
||||
{
|
||||
map_object_p->first_chunk_cp = map_object_p->last_chunk_cp;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_chunk_p->items[ECMA_MAP_OBJECT_ITEM_COUNT] = ecma_make_pointer_value (new_chunk_p);
|
||||
}
|
||||
|
||||
if (key_p == NULL)
|
||||
{
|
||||
JERRY_ASSERT (value_p == NULL);
|
||||
key_p = new_chunk_p->items + 0;
|
||||
value_p = new_chunk_p->items + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
value_p = new_chunk_p->items + 0;
|
||||
}
|
||||
}
|
||||
|
||||
*key_p = ecma_copy_value_if_not_object (key_arg);
|
||||
map_object_p->header.u.class_prop.u.length++;
|
||||
ecma_property_value_t *value_p = ecma_create_named_data_property (internal_obj_p,
|
||||
prop_name_p,
|
||||
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
|
||||
NULL);
|
||||
value_p->value = ecma_copy_value_if_not_object (value_arg);
|
||||
map_object_p->size++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ecma_free_value_if_not_object (*value_p);
|
||||
ecma_named_data_property_assign_value (internal_obj_p, ECMA_PROPERTY_VALUE_PTR (property_p), value_arg);
|
||||
}
|
||||
|
||||
*value_p = ecma_copy_value_if_not_object (value_arg);
|
||||
|
||||
ecma_ref_object (&map_object_p->header.object);
|
||||
ecma_deref_ecma_string (prop_name_p);
|
||||
ecma_ref_object ((ecma_object_t *) &map_object_p->header);
|
||||
return this_arg;
|
||||
} /* ecma_op_map_set */
|
||||
|
||||
@@ -334,43 +296,121 @@ ecma_op_map_set (ecma_value_t this_arg, /**< this argument */
|
||||
void
|
||||
ecma_op_map_clear_map (ecma_map_object_t *map_object_p) /**< map object */
|
||||
{
|
||||
JERRY_ASSERT (ecma_get_object_type (&map_object_p->header.object) == ECMA_OBJECT_TYPE_CLASS
|
||||
&& (map_object_p->header.u.class_prop.class_id == LIT_MAGIC_STRING_MAP_UL));
|
||||
ecma_object_t *object_p = ecma_get_object_from_value (map_object_p->header.u.class_prop.u.value);
|
||||
|
||||
jmem_cpointer_t first_chunk_cp = map_object_p->first_chunk_cp;
|
||||
JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE);
|
||||
|
||||
if (JERRY_UNLIKELY (first_chunk_cp == ECMA_NULL_POINTER))
|
||||
ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p);
|
||||
|
||||
if (prop_iter_p != NULL && prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
|
||||
{
|
||||
return;
|
||||
ecma_property_hashmap_free (object_p);
|
||||
prop_iter_p = ecma_get_property_list (object_p);
|
||||
}
|
||||
|
||||
ecma_map_object_chunk_t *chunk_p = ECMA_GET_NON_NULL_POINTER (ecma_map_object_chunk_t,
|
||||
first_chunk_cp);
|
||||
|
||||
do
|
||||
while (prop_iter_p != NULL)
|
||||
{
|
||||
ecma_value_t *current_p = chunk_p->items;
|
||||
ecma_value_t *last_p = current_p + ECMA_MAP_OBJECT_ITEM_COUNT;
|
||||
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
|
||||
|
||||
do
|
||||
/* 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_free_value_if_not_object (*current_p++);
|
||||
ecma_property_t *property_p = (ecma_property_t *) (prop_iter_p->types + i);
|
||||
jmem_cpointer_t name_cp = prop_pair_p->names_cp[i];
|
||||
|
||||
if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED)
|
||||
{
|
||||
ecma_free_property (object_p, name_cp, property_p);
|
||||
}
|
||||
}
|
||||
while (current_p < last_p);
|
||||
|
||||
ecma_value_t next = *current_p;
|
||||
prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t,
|
||||
prop_iter_p->next_property_cp);
|
||||
|
||||
jmem_heap_free_block (chunk_p, sizeof (ecma_map_object_chunk_t));
|
||||
|
||||
chunk_p = (ecma_map_object_chunk_t *) ecma_get_pointer_from_value (next);
|
||||
ecma_dealloc_property_pair (prop_pair_p);
|
||||
}
|
||||
while (chunk_p != NULL);
|
||||
|
||||
map_object_p->header.u.class_prop.u.length = 0;
|
||||
map_object_p->first_chunk_cp = ECMA_NULL_POINTER;
|
||||
map_object_p->last_chunk_cp = ECMA_NULL_POINTER;
|
||||
ecma_dealloc_object (object_p);
|
||||
} /* ecma_op_map_clear_map */
|
||||
|
||||
/**
|
||||
* The generic map prototype object's 'forEach' routine
|
||||
*
|
||||
* @return ecma value
|
||||
* Returned value must be freed with ecma_free_value.
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_op_map_foreach (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t predicate, /**< callback function */
|
||||
ecma_value_t predicate_this_arg) /**< this argument for
|
||||
* invoke predicate */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
if (!ecma_op_is_callable (predicate))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Callback function is not callable."));
|
||||
}
|
||||
|
||||
JERRY_ASSERT (ecma_is_value_object (predicate));
|
||||
ecma_object_t *func_object_p = ecma_get_object_from_value (predicate);
|
||||
|
||||
ecma_object_t *internal_obj_p = ecma_get_object_from_value (map_object_p->header.u.class_prop.u.value);
|
||||
|
||||
ecma_collection_header_t *props_p = ecma_op_object_get_property_names (internal_obj_p, ECMA_LIST_NO_OPTS);
|
||||
|
||||
ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p);
|
||||
|
||||
ecma_value_t ret_value = ECMA_VALUE_UNDEFINED;
|
||||
|
||||
while (ecma_value_p != NULL)
|
||||
{
|
||||
ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (*ecma_value_p);
|
||||
ecma_property_t *property_p = ecma_find_named_property (internal_obj_p, prop_name_p);
|
||||
JERRY_ASSERT (property_p != NULL);
|
||||
|
||||
ecma_value_t value = ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value);
|
||||
ecma_value_t key_arg;
|
||||
|
||||
if (ecma_prop_name_is_map_key (prop_name_p))
|
||||
{
|
||||
key_arg = prop_name_p->u.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_arg = *ecma_value_p;
|
||||
}
|
||||
|
||||
ecma_value_t call_args[] = { value, key_arg };
|
||||
|
||||
ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 2);
|
||||
|
||||
ecma_free_value (value);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (call_value))
|
||||
{
|
||||
ret_value = call_value;
|
||||
break;
|
||||
}
|
||||
|
||||
ecma_value_p = ecma_collection_iterator_next (ecma_value_p);
|
||||
}
|
||||
|
||||
ecma_free_values_collection (props_p, 0);
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_op_map_foreach */
|
||||
|
||||
/**
|
||||
* The Map prototype object's 'clear' routine
|
||||
*
|
||||
@@ -380,70 +420,21 @@ ecma_op_map_clear_map (ecma_map_object_t *map_object_p) /**< map object */
|
||||
ecma_value_t
|
||||
ecma_op_map_clear (ecma_value_t this_arg) /**< this argument */
|
||||
{
|
||||
/* WeakMap does not have a clear method. */
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
ecma_op_map_clear_map (map_object_p);
|
||||
|
||||
map_object_p->header.u.class_prop.u.value = ecma_op_map_create_internal_object ();
|
||||
map_object_p->size = 0;
|
||||
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
} /* ecma_op_map_clear */
|
||||
|
||||
/**
|
||||
* Deletes the current chunk if it is filled with ECMA_VALUE_ARRAY_HOLE.
|
||||
*
|
||||
* @return next chunk if the chunk is deleted, NULL otherwise
|
||||
*/
|
||||
static ecma_map_object_chunk_t *
|
||||
ecma_op_map_delete_chunk (ecma_map_object_t *map_object_p, /**< map object */
|
||||
ecma_map_object_chunk_t *chunk_p, /**< current chunk */
|
||||
ecma_map_object_chunk_t *prev_chunk_p) /**< previous chunk */
|
||||
{
|
||||
for (int i = 0; i < ECMA_MAP_OBJECT_ITEM_COUNT; i++)
|
||||
{
|
||||
JERRY_ASSERT (!ecma_is_value_pointer (chunk_p->items[i]));
|
||||
|
||||
if (chunk_p->items[i] != ECMA_VALUE_ARRAY_HOLE)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ecma_value_t next_chunk = chunk_p->items[ECMA_MAP_OBJECT_ITEM_COUNT];
|
||||
ecma_map_object_chunk_t *next_chunk_p = (ecma_map_object_chunk_t *) ecma_get_pointer_from_value (next_chunk);
|
||||
|
||||
jmem_heap_free_block (chunk_p, sizeof (ecma_map_object_chunk_t));
|
||||
|
||||
if (prev_chunk_p != NULL)
|
||||
{
|
||||
prev_chunk_p->items[ECMA_MAP_OBJECT_ITEM_COUNT] = ecma_make_pointer_value (next_chunk_p);
|
||||
|
||||
if (next_chunk_p == NULL)
|
||||
{
|
||||
JERRY_ASSERT (map_object_p->first_chunk_cp != map_object_p->last_chunk_cp);
|
||||
JERRY_ASSERT (ECMA_GET_NON_NULL_POINTER (ecma_map_object_chunk_t, map_object_p->last_chunk_cp) == chunk_p);
|
||||
|
||||
ECMA_SET_POINTER (map_object_p->last_chunk_cp, prev_chunk_p);
|
||||
}
|
||||
return next_chunk_p;
|
||||
}
|
||||
|
||||
if (next_chunk_p == NULL)
|
||||
{
|
||||
JERRY_ASSERT (map_object_p->first_chunk_cp == map_object_p->last_chunk_cp);
|
||||
JERRY_ASSERT (ECMA_GET_NON_NULL_POINTER (ecma_map_object_chunk_t, map_object_p->last_chunk_cp) == chunk_p);
|
||||
|
||||
map_object_p->first_chunk_cp = ECMA_NULL_POINTER;
|
||||
map_object_p->last_chunk_cp = ECMA_NULL_POINTER;
|
||||
return next_chunk_p;
|
||||
}
|
||||
|
||||
ECMA_SET_POINTER (map_object_p->first_chunk_cp, next_chunk_p);
|
||||
return next_chunk_p;
|
||||
} /* ecma_op_map_delete_chunk */
|
||||
|
||||
/**
|
||||
* The generic map prototype object's 'delete' routine
|
||||
*
|
||||
@@ -455,123 +446,27 @@ ecma_op_map_delete (ecma_value_t this_arg, /**< this argument */
|
||||
ecma_value_t key_arg) /**< key argument */
|
||||
{
|
||||
ecma_map_object_t *map_object_p = ecma_op_map_get_object (this_arg);
|
||||
|
||||
if (map_object_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
if (JERRY_UNLIKELY (map_object_p->first_chunk_cp == ECMA_NULL_POINTER))
|
||||
ecma_object_t *internal_obj_p = ecma_get_object_from_value (map_object_p->header.u.class_prop.u.value);
|
||||
|
||||
ecma_string_t *prop_name_p = ecma_op_map_to_key (key_arg);
|
||||
|
||||
ecma_property_t *property_p = ecma_find_named_property (internal_obj_p, prop_name_p);
|
||||
|
||||
ecma_deref_ecma_string (prop_name_p);
|
||||
|
||||
if (property_p == NULL)
|
||||
{
|
||||
return ECMA_VALUE_FALSE;
|
||||
}
|
||||
|
||||
ecma_map_object_chunk_t *chunk_p = ECMA_GET_NON_NULL_POINTER (ecma_map_object_chunk_t,
|
||||
map_object_p->first_chunk_cp);
|
||||
|
||||
bool is_direct = true;
|
||||
const ecma_string_t *key_str_p = NULL;
|
||||
ecma_number_t key_float = 0;
|
||||
|
||||
if (ecma_is_value_non_direct_string (key_arg))
|
||||
{
|
||||
key_str_p = ecma_get_string_from_value (key_arg);
|
||||
is_direct = false;
|
||||
}
|
||||
else if (ecma_is_value_float_number (key_arg))
|
||||
{
|
||||
key_float = ecma_get_float_from_value (key_arg);
|
||||
is_direct = false;
|
||||
}
|
||||
|
||||
ecma_map_object_chunk_t *prev_chunk_p = NULL;
|
||||
ecma_value_t *item_p = chunk_p->items;
|
||||
bool is_key = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ecma_value_t item = *item_p++;
|
||||
|
||||
if (JERRY_UNLIKELY (item == ECMA_VALUE_ARRAY_HOLE))
|
||||
{
|
||||
JERRY_ASSERT (is_key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JERRY_UNLIKELY (ecma_is_value_pointer (item)))
|
||||
{
|
||||
prev_chunk_p = chunk_p;
|
||||
chunk_p = (ecma_map_object_chunk_t *) ecma_get_pointer_from_value (item);
|
||||
|
||||
if (chunk_p == NULL)
|
||||
{
|
||||
JERRY_ASSERT (is_key);
|
||||
return ECMA_VALUE_FALSE;
|
||||
}
|
||||
|
||||
item_p = chunk_p->items;
|
||||
|
||||
JERRY_ASSERT (!ecma_is_value_pointer (*item_p));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_key)
|
||||
{
|
||||
if (JERRY_LIKELY (is_direct))
|
||||
{
|
||||
if (key_arg == item)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (key_str_p != NULL)
|
||||
{
|
||||
if (ecma_is_value_non_direct_string (item)
|
||||
&& ecma_compare_ecma_non_direct_strings (key_str_p, ecma_get_string_from_value (item)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ecma_is_value_float_number (item)
|
||||
&& ecma_get_float_from_value (item) == key_float)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
is_key = !is_key;
|
||||
}
|
||||
|
||||
map_object_p->header.u.class_prop.u.length--;
|
||||
|
||||
item_p -= 1;
|
||||
ecma_free_value_if_not_object (item_p[0]);
|
||||
item_p[0] = ECMA_VALUE_ARRAY_HOLE;
|
||||
|
||||
if ((item_p - chunk_p->items) < ECMA_MAP_OBJECT_ITEM_COUNT - 1)
|
||||
{
|
||||
JERRY_ASSERT (!ecma_is_value_pointer (item_p[1]));
|
||||
|
||||
ecma_free_value_if_not_object (item_p[1]);
|
||||
item_p[1] = ECMA_VALUE_ARRAY_HOLE;
|
||||
|
||||
ecma_op_map_delete_chunk (map_object_p, chunk_p, prev_chunk_p);
|
||||
return ECMA_VALUE_TRUE;
|
||||
}
|
||||
|
||||
ecma_map_object_chunk_t *next_chunk_p = ecma_op_map_delete_chunk (map_object_p, chunk_p, prev_chunk_p);
|
||||
|
||||
if (next_chunk_p == NULL)
|
||||
{
|
||||
prev_chunk_p = chunk_p;
|
||||
|
||||
ecma_value_t next_chunk = chunk_p->items[ECMA_MAP_OBJECT_ITEM_COUNT];
|
||||
next_chunk_p = (ecma_map_object_chunk_t *) ecma_get_pointer_from_value (next_chunk);
|
||||
}
|
||||
|
||||
ecma_free_value_if_not_object (next_chunk_p->items[0]);
|
||||
next_chunk_p->items[0] = ECMA_VALUE_ARRAY_HOLE;
|
||||
|
||||
ecma_op_map_delete_chunk (map_object_p, next_chunk_p, prev_chunk_p);
|
||||
ecma_delete_property (internal_obj_p, ECMA_PROPERTY_VALUE_PTR (property_p));
|
||||
map_object_p->size--;
|
||||
|
||||
return ECMA_VALUE_TRUE;
|
||||
} /* ecma_op_map_delete */
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
ecma_value_t ecma_op_map_create (const ecma_value_t *arguments_list_p, ecma_length_t arguments_list_len);
|
||||
ecma_value_t ecma_op_map_size (ecma_value_t this_arg);
|
||||
ecma_value_t ecma_op_map_get (ecma_value_t this_arg, ecma_value_t key_arg);
|
||||
ecma_value_t ecma_op_map_foreach (ecma_value_t this_arg, ecma_value_t predicate, ecma_value_t predicate_this_arg);
|
||||
ecma_value_t ecma_op_map_has (ecma_value_t this_arg, ecma_value_t key_arg);
|
||||
ecma_value_t ecma_op_map_set (ecma_value_t this_arg, ecma_value_t key_arg, ecma_value_t value_arg);
|
||||
void ecma_op_map_clear_map (ecma_map_object_t *map_object_p);
|
||||
|
||||
@@ -47,6 +47,7 @@ typedef enum
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_VALUE, /**< [[Values]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REMAINING_ELEMENT, /**< [[RemainingElement]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_ITERATOR_NEXT_INDEX, /**< [[%Iterator%NextIndex]] property */
|
||||
LIT_INTERNAL_MAGIC_STRING_MAP_KEY, /**< Property key used when an object is a key in a map object */
|
||||
/* List of well known symbols */
|
||||
LIT_GLOBAL_SYMBOL_HAS_INSTANCE, /**< @@hasInstance well known symbol */
|
||||
LIT_GLOBAL_SYMBOL_IS_CONCAT_SPREADABLE, /**< @@isConcatSpreadable well known symbol */
|
||||
|
||||
@@ -329,6 +329,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMPILE, "compile")
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ENTRIES, "entries")
|
||||
#endif
|
||||
#if ENABLED (JERRY_BUILTIN_ARRAY) \
|
||||
|| ENABLED (JERRY_ES2015_BUILTIN_MAP) \
|
||||
|| ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY)
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FOR_EACH_UL, "forEach")
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var map = new Map();
|
||||
|
||||
/* Test when the map object keys are strings */
|
||||
for (var i = 0; i < 15; i++) {
|
||||
map.set ("foo" + i, i);
|
||||
assert (map.size === i + 1);
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
|
||||
map.forEach (function (value, key) {
|
||||
assert (typeof key === "string");
|
||||
assert (key === "foo" + counter);
|
||||
assert (typeof value === "number");
|
||||
assert (value === counter);
|
||||
counter++;
|
||||
});
|
||||
|
||||
map.clear ();
|
||||
assert (map.size === 0);
|
||||
counter = 0;
|
||||
|
||||
/* Test when the map object keys are numbers */
|
||||
for (var i = 0; i < 15; i++) {
|
||||
map.set (i + 0.5, "foo" + i);
|
||||
assert (map.size === i + 1);
|
||||
}
|
||||
|
||||
map.forEach (function (value, key) {
|
||||
assert (typeof key === "number");
|
||||
assert (key === counter + 0.5);
|
||||
assert (typeof value === "string");
|
||||
assert (value === "foo" + counter);
|
||||
counter++;
|
||||
});
|
||||
|
||||
map.clear ();
|
||||
assert (map.size === 0);
|
||||
counter = 0;
|
||||
|
||||
var objectList = [];
|
||||
|
||||
/* Test when the map object keys are objects */
|
||||
for (var i = 0; i < 15; i++) {
|
||||
objectList[i] = { index : i };
|
||||
map.set (objectList[i], "foo" + i);
|
||||
assert (map.size === i + 1);
|
||||
}
|
||||
|
||||
map.forEach (function (value, key) {
|
||||
assert (typeof key === "object");
|
||||
assert (key === objectList[counter]);
|
||||
assert (key.index === objectList[counter].index);
|
||||
assert (typeof value === "string");
|
||||
assert (value === "foo" + counter);
|
||||
counter++;
|
||||
});
|
||||
|
||||
/* Test when the this argument is not a map object */
|
||||
try {
|
||||
Map.prototype.forEach.call ({});
|
||||
assert (false);
|
||||
} catch (e) {
|
||||
assert (e instanceof TypeError);
|
||||
}
|
||||
|
||||
/* Test when the 1st argument is not callable */
|
||||
try {
|
||||
map.forEach (5);
|
||||
assert (false);
|
||||
} catch (e) {
|
||||
assert (e instanceof TypeError);
|
||||
}
|
||||
|
||||
/* Test when the the callback function throws error */
|
||||
try {
|
||||
map.forEach (function (value, key) {
|
||||
throw new ReferenceError ("foo");
|
||||
});
|
||||
assert (false);
|
||||
} catch (e) {
|
||||
assert (e instanceof ReferenceError);
|
||||
assert (e.message === "foo");
|
||||
}
|
||||
|
||||
/* Test when the 2nd optional argument is present */
|
||||
var object = {
|
||||
_secret : 42
|
||||
}
|
||||
|
||||
map.forEach (function (value, key) {
|
||||
assert (this._secret === 42);
|
||||
}, object);
|
||||
@@ -73,6 +73,43 @@ assert(m.get(1) === 8);
|
||||
assert(m.get(2) === 8);
|
||||
assert(m.get(3) === 8);
|
||||
|
||||
m.set(NaN, "not a number");
|
||||
assert(m.size === 4);
|
||||
assert(m.get(NaN) === "not a number");
|
||||
assert(m.get(Number("foo")) === "not a number")
|
||||
|
||||
m.set(0, "PosZero");
|
||||
assert(m.size === 5);
|
||||
m.set(-0, "NegZero");
|
||||
assert(m.size === 5);
|
||||
assert(m.get (0) === "NegZero");
|
||||
assert(m.get (-0) === "NegZero");
|
||||
|
||||
var symbolFoo = Symbol ("foo");
|
||||
m.set (symbolFoo, "SymbolFoo");
|
||||
assert(m.size === 6);
|
||||
assert(m.get(symbolFoo) === "SymbolFoo");
|
||||
|
||||
var object = {};
|
||||
m.set (object, "object");
|
||||
assert(m.size === 7);
|
||||
assert(m.get(object) === "object");
|
||||
assert(m.get({}) === undefined);
|
||||
|
||||
var myObj = { o : 4 };
|
||||
m.set("foo", myObj);
|
||||
assert(m.size === 8);
|
||||
assert(m.get ("foo") === myObj);
|
||||
assert(m.get ("foo").o === 4);
|
||||
|
||||
m.clear();
|
||||
assert(m.size === 0);
|
||||
|
||||
m.set("foo", myObj);
|
||||
assert(m.size === 1);
|
||||
assert(m.get ("foo") === myObj);
|
||||
assert(m.get ("foo").o === 4);
|
||||
|
||||
var mapNameDesc = Object.getOwnPropertyDescriptor (Map, 'name');
|
||||
assert(mapNameDesc.value === "Map");
|
||||
assert(mapNameDesc.writable === false);
|
||||
|
||||
Reference in New Issue
Block a user