diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c index 3cd79e013..02ea6875d 100644 --- a/jerry-core/debugger/debugger.c +++ b/jerry-core/debugger/debugger.c @@ -15,6 +15,7 @@ #include "byte-code.h" #include "debugger.h" +#include "ecma-array-object.h" #include "ecma-builtin-helpers.h" #include "ecma-conversion.h" #include "ecma-eval.h" @@ -456,6 +457,17 @@ jerry_debugger_send_scope_variables (const uint8_t *recv_buffer_p) /**< pointer { JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); ecma_object_t *binding_obj_p = ecma_get_lex_env_binding_object (lex_env_p); + + if (JERRY_UNLIKELY (ecma_get_object_type (binding_obj_p) == ECMA_OBJECT_TYPE_ARRAY)) + { + ecma_extended_object_t *ext_binding_obj_p = (ecma_extended_object_t *) binding_obj_p; + + if (ext_binding_obj_p->u.array.is_fast_mode) + { + ecma_fast_array_convert_to_normal (binding_obj_p); + } + } + prop_iter_cp = binding_obj_p->u1.property_list_cp; } diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index fe64f2ef0..224b6594c 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -407,6 +407,31 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ break; } + case ECMA_OBJECT_TYPE_ARRAY: + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + if (ext_object_p->u.array.is_fast_mode) + { + if (object_p->u1.property_list_cp == JMEM_CP_NULL) + { + return; + } + + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + for (uint32_t i = 0; i < ext_object_p->u.array.length; i++) + { + if (ecma_is_value_object (values_p[i])) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (values_p[i])); + } + } + + return; + } + break; + } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) object_p; @@ -541,6 +566,37 @@ ecma_gc_free_native_pointer (ecma_property_t *property_p) /**< property */ #endif /* !JERRY_NDEBUG */ } /* ecma_gc_free_native_pointer */ +/** + * Free specified fast access mode array object. + */ +static void +ecma_free_fast_access_array (ecma_object_t *object_p) /**< fast access mode array object to free */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + JERRY_ASSERT (ext_object_p->u.array.is_fast_mode); + + const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (ext_object_p->u.array.length); + + if (object_p->u1.property_list_cp != JMEM_CP_NULL) + { + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + for (uint32_t i = 0; i < aligned_length; i++) + { + ecma_free_value_if_not_object (values_p[i]); + } + + jmem_heap_free_block (values_p, aligned_length * sizeof (ecma_value_t)); + } + + JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_objects_number) > 0); + JERRY_CONTEXT (ecma_gc_objects_number)--; + + ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); +} /* ecma_free_fast_access_array */ + /** * Free specified object. */ @@ -556,6 +612,17 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ if (obj_is_not_lex_env || ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { + if (obj_is_not_lex_env && ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + if (ext_object_p->u.array.is_fast_mode) + { + ecma_free_fast_access_array (object_p); + return; + } + } + jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; #if ENABLED (JERRY_PROPRETY_HASHMAP) diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 542ea3cd4..fce11c9d5 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -331,6 +331,8 @@ typedef enum #if ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) ECMA_LIST_SYMBOLS = (1 << 3), /**< list symbol properties only */ #endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */ + ECMA_LIST_CONVERT_FAST_ARRAYS = (1 << 4), /**< after listing the properties convert + * the fast access mode array back to normal array */ } ecma_list_properties_options_t; /** @@ -822,6 +824,9 @@ typedef struct { uint32_t length; /**< length property value */ ecma_property_t length_prop; /**< length property */ + bool is_fast_mode; /**< true - if the array is a fast access mode array + * false - otherwise */ + uint8_t hole_count; /**< Number of array holes in a fast access mode array */ } array; /** @@ -869,6 +874,19 @@ typedef struct ecma_built_in_props_t built_in; /**< built-in object part */ } ecma_extended_built_in_object_t; +/** + * Alignment for the fast access mode array length. + * The real length is aligned up for allocating the underlying buffer. + */ +#define ECMA_FAST_ARRAY_ALIGNMENT (8) + +/** + * Align the length of the fast mode array to get the allocated size of the underlying buffer + */ +#define ECMA_FAST_ARRAY_ALIGN_LENGTH(length) \ + (uint32_t) ((((length)) + ECMA_FAST_ARRAY_ALIGNMENT - 1) / ECMA_FAST_ARRAY_ALIGNMENT * ECMA_FAST_ARRAY_ALIGNMENT) + + /** * Compiled byte code data. */ diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index 9fe9ad40b..b20fd6c07 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -849,159 +849,6 @@ ecma_delete_property (ecma_object_t *object_p, /**< object */ } } /* ecma_delete_property */ -/** - * Low level delete of array items from new_length to old_length - * - * Note: new_length must be less than old_length - * - * @return the updated value of new_length - */ -uint32_t -ecma_delete_array_properties (ecma_object_t *object_p, /**< object */ - uint32_t new_length, /**< new length */ - uint32_t old_length) /**< old length */ -{ - JERRY_ASSERT (new_length < old_length); - - /* First the minimum value of new_length is updated. */ - jmem_cpointer_t current_prop_cp = object_p->u1.property_list_cp; - - if (current_prop_cp == JMEM_CP_NULL) - { - return new_length; - } - - ecma_property_header_t *current_prop_p; - -#if ENABLED (JERRY_PROPRETY_HASHMAP) - current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); - - if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) - { - current_prop_cp = current_prop_p->next_property_cp; - } -#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ - - while (current_prop_cp != JMEM_CP_NULL) - { - current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); - JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (current_prop_p)); - - ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) current_prop_p; - - for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) - { - if (ECMA_PROPERTY_IS_NAMED_PROPERTY (current_prop_p->types[i]) - && !ecma_is_property_configurable (current_prop_p->types[i])) - { - uint32_t index = ecma_string_get_property_index (current_prop_p->types[i], - prop_pair_p->names_cp[i]); - - if (index < old_length && index >= new_length) - { - JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); - - new_length = index + 1; - - if (new_length == old_length) - { - /* Early return. */ - return new_length; - } - } - } - } - - current_prop_cp = current_prop_p->next_property_cp; - } - - /* Second all properties between new_length and old_length are deleted. */ - current_prop_cp = object_p->u1.property_list_cp; - ecma_property_header_t *prev_prop_p = NULL; - -#if ENABLED (JERRY_PROPRETY_HASHMAP) - JERRY_ASSERT (current_prop_cp != JMEM_CP_NULL); - - ecma_property_hashmap_delete_status hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_NO_HASHMAP; - current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); - - if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) - { - prev_prop_p = current_prop_p; - current_prop_cp = current_prop_p->next_property_cp; - hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP; - } -#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ - - while (current_prop_cp != JMEM_CP_NULL) - { - current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); - - JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (current_prop_p)); - ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) current_prop_p; - - for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) - { - if (ECMA_PROPERTY_IS_NAMED_PROPERTY (current_prop_p->types[i]) - && ecma_is_property_configurable (current_prop_p->types[i])) - { - uint32_t index = ecma_string_get_property_index (current_prop_p->types[i], - prop_pair_p->names_cp[i]); - - if (index < old_length && index >= new_length) - { - JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); - -#if ENABLED (JERRY_PROPRETY_HASHMAP) - if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP) - { - hashmap_status = ecma_property_hashmap_delete (object_p, - prop_pair_p->names_cp[i], - current_prop_p->types + i); - } -#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ - - ecma_free_property (object_p, prop_pair_p->names_cp[i], current_prop_p->types + i); - current_prop_p->types[i] = ECMA_PROPERTY_TYPE_DELETED; - prop_pair_p->names_cp[i] = LIT_INTERNAL_MAGIC_STRING_DELETED; - } - } - } - - if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_DELETED - && current_prop_p->types[1] == ECMA_PROPERTY_TYPE_DELETED) - { - if (prev_prop_p == NULL) - { - object_p->u1.property_list_cp = current_prop_p->next_property_cp; - } - else - { - prev_prop_p->next_property_cp = current_prop_p->next_property_cp; - } - - jmem_cpointer_t next_prop_cp = current_prop_p->next_property_cp; - ecma_dealloc_property_pair ((ecma_property_pair_t *) current_prop_p); - current_prop_cp = next_prop_cp; - } - else - { - prev_prop_p = current_prop_p; - current_prop_cp = current_prop_p->next_property_cp; - } - } - -#if ENABLED (JERRY_PROPRETY_HASHMAP) - if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP) - { - ecma_property_hashmap_free (object_p); - ecma_property_hashmap_create (object_p); - } -#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ - - return new_length; -} /* ecma_delete_array_properties */ - /** * Check whether the object contains a property */ diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 039fe92b4..e9bd614c4 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -387,7 +387,6 @@ ecma_get_named_data_property (ecma_object_t *obj_p, ecma_string_t *name_p); void ecma_free_property (ecma_object_t *object_p, jmem_cpointer_t name_cp, ecma_property_t *property_p); void ecma_delete_property (ecma_object_t *object_p, ecma_property_value_t *prop_value_p); -uint32_t ecma_delete_array_properties (ecma_object_t *object_p, uint32_t new_length, uint32_t old_length); void ecma_named_data_property_assign_value (ecma_object_t *obj_p, ecma_property_value_t *prop_value_p, ecma_value_t value); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c index b22f36a59..06746cd90 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c @@ -282,24 +282,22 @@ ecma_builtin_helper_object_get_properties (ecma_object_t *obj_p, /**< object */ ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, opts); + if (props_p->item_count == 0) + { + ecma_free_values_collection (props_p, 0); + return new_array; + } + + JERRY_ASSERT (((ecma_extended_object_t *) new_array_p)->u.array.is_fast_mode); + + ecma_value_t *values_p = ecma_fast_array_extend (new_array_p, props_p->item_count); + ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p); while (ecma_value_p != NULL) { - ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index); - - ecma_value_t completion = ecma_builtin_helper_def_prop (new_array_p, - index_string_p, - *ecma_value_p, - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); - - JERRY_ASSERT (ecma_is_value_true (completion)); - - ecma_deref_ecma_string (index_string_p); - + values_p[index++] = ecma_copy_value_if_not_object (*ecma_value_p); ecma_value_p = ecma_collection_iterator_next (ecma_value_p); - - index++; } ecma_free_values_collection (props_p, 0); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object.c b/jerry-core/ecma/builtin-objects/ecma-builtin-object.c index 1eb03fea9..f6e0439df 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object.c @@ -321,7 +321,7 @@ ecma_builtin_object_object_get_own_property_symbols (ecma_object_t *obj_p) /**< static ecma_value_t ecma_builtin_object_object_seal (ecma_object_t *obj_p) /**< routine's argument */ { - ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_NO_OPTS); + ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_CONVERT_FAST_ARRAYS); ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p); @@ -380,7 +380,7 @@ ecma_builtin_object_object_seal (ecma_object_t *obj_p) /**< routine's argument * static ecma_value_t ecma_builtin_object_object_freeze (ecma_object_t *obj_p) /**< routine's argument */ { - ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_NO_OPTS); + ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_CONVERT_FAST_ARRAYS); ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p); @@ -597,7 +597,8 @@ ecma_builtin_object_object_define_properties (ecma_object_t *obj_p, /**< routine ecma_object_t *props_p = ecma_get_object_from_value (props); /* 3. */ - ecma_collection_header_t *prop_names_p = ecma_op_object_get_property_names (props_p, ECMA_LIST_ENUMERABLE); + ecma_collection_header_t *prop_names_p = ecma_op_object_get_property_names (props_p, ECMA_LIST_CONVERT_FAST_ARRAYS + | ECMA_LIST_ENUMERABLE); uint32_t property_number = prop_names_p->item_count; ecma_value_t ret_value = ECMA_VALUE_ERROR; @@ -820,7 +821,8 @@ ecma_builtin_object_object_assign (const ecma_value_t arguments_list_p[], /**< a /* 5.b.iii */ /* TODO: extends this collection if symbols will be supported */ - ecma_collection_header_t *props_p = ecma_op_object_get_property_names (from_obj_p, ECMA_LIST_ENUMERABLE); + ecma_collection_header_t *props_p = ecma_op_object_get_property_names (from_obj_p, ECMA_LIST_CONVERT_FAST_ARRAYS + | ECMA_LIST_ENUMERABLE); ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.c b/jerry-core/ecma/builtin-objects/ecma-builtins.c index ce76638d0..4db22e1c0 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.c @@ -432,6 +432,8 @@ ecma_instantiate_builtin (ecma_builtin_id_t obj_builtin_id) /**< built-in id */ ext_object_p->u.array.length = 0; ext_object_p->u.array.length_prop = ECMA_PROPERTY_FLAG_WRITABLE | ECMA_PROPERTY_TYPE_VIRTUAL; + ext_object_p->u.array.is_fast_mode = false; + ext_object_p->u.array.hole_count = 0; break; } #endif /* ENABLED (JERRY_BUILTIN_ARRAY) */ diff --git a/jerry-core/ecma/operations/ecma-array-object.c b/jerry-core/ecma/operations/ecma-array-object.c index 38f17c80f..e8d3f326f 100644 --- a/jerry-core/ecma/operations/ecma-array-object.c +++ b/jerry-core/ecma/operations/ecma-array-object.c @@ -20,6 +20,7 @@ #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" +#include "ecma-property-hashmap.h" #include "ecma-helpers.h" #include "ecma-number-arithmetic.h" #include "ecma-objects.h" @@ -33,6 +34,520 @@ * @{ */ +/** + * Allocate a new array object with the given length + * + * @return pointer to the constructed array object + */ +ecma_object_t * +ecma_op_new_array_object (ecma_length_t length) /**< length of the new array */ +{ +#if ENABLED (JERRY_BUILTIN_ARRAY) + ecma_object_t *array_prototype_object_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAY_PROTOTYPE); +#else /* !ENABLED (JERRY_BUILTIN_ARRAY) */ + ecma_object_t *array_prototype_object_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); +#endif /* (ENABLED (JERRY_BUILTIN_ARRAY)) */ + + ecma_object_t *object_p = ecma_create_object (array_prototype_object_p, + sizeof (ecma_extended_object_t), + ECMA_OBJECT_TYPE_ARRAY); + + /* + * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_ARRAY type. + * + * See also: ecma_object_get_class_name + */ + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + ext_obj_p->u.array.length = length; + ext_obj_p->u.array.length_prop = ECMA_PROPERTY_FLAG_WRITABLE | ECMA_PROPERTY_TYPE_VIRTUAL; + ext_obj_p->u.array.is_fast_mode = false; + ext_obj_p->u.array.hole_count = 0; + + return object_p; +} /* ecma_op_new_array_object */ + +/** + * Allocate a new fast access mode array object with the given length + * + * @return NULL - if the allocation of the underlying buffer failed + * pointer to the constructed fast access mode array object otherwise + */ +ecma_object_t * +ecma_op_new_fast_array_object (ecma_length_t length) /**< length of the new fast access mode array */ +{ + ecma_object_t *object_p = ecma_op_new_array_object (length); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + ext_obj_p->u.array.is_fast_mode = true; + + JERRY_ASSERT (object_p->u1.property_list_cp == JMEM_CP_NULL); + + if (length == 0) + { + return object_p; + } + + const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (length); + + ecma_value_t *values_p; + + values_p = (ecma_value_t *) jmem_heap_alloc_block_null_on_error (aligned_length * sizeof (ecma_value_t)); + + if (JERRY_UNLIKELY (values_p == NULL)) + { + return NULL; + } + + for (uint32_t i = 0; i < aligned_length; i++) + { + values_p[i] = ECMA_VALUE_ARRAY_HOLE; + } + + ECMA_SET_NON_NULL_POINTER (object_p->u1.property_list_cp, values_p); + + return object_p; +} /* ecma_op_new_fast_array_object */ + +/** + * Property name type flag for array indices. + */ +#define ECMA_FAST_ARRAY_UINT32_DIRECT_STRING_PROP_TYPE 0x80 + +/** + * Converts a fast access mode array back to a normal property list based array + */ +void +ecma_fast_array_convert_to_normal (ecma_object_t *object_p) /**< fast access mode array object */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + + if (object_p->u1.property_list_cp == JMEM_CP_NULL) + { + ext_obj_p->u.array.is_fast_mode = false; + return; + } + + uint32_t length = ext_obj_p->u.array.length; + const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (length); + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + /* Check whether the buffer contains only array holes */ + if (JERRY_UNLIKELY (ext_obj_p->u.array.length == ext_obj_p->u.array.hole_count)) + { + ext_obj_p->u.array.is_fast_mode = false; + jmem_heap_free_block (values_p, aligned_length * sizeof (ecma_value_t)); + object_p->u1.property_list_cp = JMEM_CP_NULL; + return; + } + + ecma_ref_object (object_p); + + ecma_property_pair_t *property_pair_p = NULL; + jmem_cpointer_t next_property_pair_cp = JMEM_CP_NULL; + + uint32_t prop_index = 1; + int32_t index = (int32_t) (length - 1); + + while (index >= 0) + { + if (ecma_is_value_array_hole (values_p[index])) + { + index--; + continue; + } + + if (prop_index == 1) + { + property_pair_p = ecma_alloc_property_pair (); + property_pair_p->header.next_property_cp = next_property_pair_cp; + property_pair_p->names_cp[0] = LIT_INTERNAL_MAGIC_STRING_DELETED; + property_pair_p->header.types[0] = ECMA_PROPERTY_TYPE_DELETED; + ECMA_SET_NON_NULL_POINTER (next_property_pair_cp, property_pair_p); + } + + JERRY_ASSERT (index <= ECMA_DIRECT_STRING_MAX_IMM); + + property_pair_p->names_cp[prop_index] = (jmem_cpointer_t) index; + property_pair_p->header.types[prop_index] = (ecma_property_t) (ECMA_PROPERTY_TYPE_NAMEDDATA + | ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE + | ECMA_FAST_ARRAY_UINT32_DIRECT_STRING_PROP_TYPE); + + property_pair_p->values[prop_index].value = values_p[index]; + + index--; + prop_index = !prop_index; + } + + ext_obj_p->u.array.is_fast_mode = false; + jmem_heap_free_block (values_p, aligned_length * sizeof (ecma_value_t)); + ECMA_SET_POINTER (object_p->u1.property_list_cp, property_pair_p); + + ecma_deref_object (object_p); +} /* ecma_fast_array_convert_to_normal */ + +/** + * Maximum number of array holes in a fast mode access array. + * If the number of holes exceeds this limit, the array is converted back + * to normal property list based array. + */ +#define ECMA_FAST_ARRAY_MAX_HOLE_COUNT 32 + +#if ENABLED (JERRY_SYSTEM_ALLOCATOR) +/** + * Maximum length of the array length to allocate fast mode access for it + * e.g. new Array(5000) is constructed as fast mode access array, + * but new Array(50000000) is consturcted as normal property list based array + */ +#define ECMA_FAST_ARRAY_MAX_INITIAL_LENGTH (1 << 13) +#else +/** + * Maximum length of the array length to allocate fast mode access for it + * e.g. new Array(5000) is constructed as fast mode access array, + * but new Array(50000000) is consturcted as normal property list based array + */ +#define ECMA_FAST_ARRAY_MAX_INITIAL_LENGTH (1 << 17) +#endif + +/** + * [[Put]] operation for a fast access mode array + * + * @return false - If the property name is not array index, or the requested index to be set + * would result too much array hole in the underlying buffer. The these cases + * the array is converted back to normal property list based array. + * true - If the indexed property can be set with/without resizing the underlying buffer. + */ +bool +ecma_fast_array_set_property (ecma_object_t *object_p, /**< fast access mode array object */ + ecma_string_t *property_name_p, /**< property name */ + ecma_value_t value) /**< value to be set */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + + uint32_t index = ecma_string_get_array_index (property_name_p); + + if (JERRY_UNLIKELY (index == ECMA_STRING_NOT_ARRAY_INDEX)) + { + ecma_fast_array_convert_to_normal (object_p); + return false; + } + + ecma_value_t *values_p; + + if (JERRY_LIKELY (index < ext_obj_p->u.array.length)) + { + JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); + + values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + if (ecma_is_value_array_hole (values_p[index])) + { + ext_obj_p->u.array.hole_count--; + } + else + { + ecma_free_value_if_not_object (values_p[index]); + } + + values_p[index] = ecma_copy_value_if_not_object (value); + + return true; + } + + uint32_t new_holes = index - ext_obj_p->u.array.length; + uint32_t old_length = ext_obj_p->u.array.length; + uint32_t new_length = index + 1; + + const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (old_length); + + if (JERRY_LIKELY (index < aligned_length)) + { + JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); + + values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + JERRY_ASSERT (ecma_is_value_array_hole (values_p[index])); + ext_obj_p->u.array.length = new_length; + } + else + { + if ((new_holes + ext_obj_p->u.array.hole_count) > ECMA_FAST_ARRAY_MAX_HOLE_COUNT) + { + ecma_fast_array_convert_to_normal (object_p); + + return false; + } + + values_p = ecma_fast_array_extend (object_p, new_length); + } + + values_p[index] = ecma_copy_value_if_not_object (value); + ext_obj_p->u.array.hole_count = (uint8_t) (ext_obj_p->u.array.hole_count + new_holes); + + return true; +} /* ecma_fast_array_set_property */ + + +/** + * Extend the underlying buffer of a fast mode access array for the given new length + * + * @return pointer to the extended underlying buffer + */ +ecma_value_t * +ecma_fast_array_extend (ecma_object_t *object_p, /**< fast access mode array object */ + uint32_t new_length) /**< new length of the fast access mode array */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + uint32_t old_length = ext_obj_p->u.array.length; + + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + JERRY_ASSERT (old_length < new_length); + + ecma_ref_object (object_p); + + ecma_value_t *new_values_p; + const uint32_t old_length_aligned = ECMA_FAST_ARRAY_ALIGN_LENGTH (old_length); + const uint32_t new_length_aligned = ECMA_FAST_ARRAY_ALIGN_LENGTH (new_length); + + if (object_p->u1.property_list_cp == JMEM_CP_NULL) + { + new_values_p = jmem_heap_alloc_block (new_length_aligned * sizeof (ecma_value_t)); + } + else + { + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + new_values_p = (ecma_value_t *) jmem_heap_realloc_block (values_p, + old_length_aligned * sizeof (ecma_value_t), + new_length_aligned * sizeof (ecma_value_t)); + } + + for (uint32_t i = old_length; i < new_length_aligned; i++) + { + new_values_p[i] = ECMA_VALUE_ARRAY_HOLE; + } + + ext_obj_p->u.array.length = new_length; + + ECMA_SET_POINTER (object_p->u1.property_list_cp, new_values_p); + + ecma_deref_object (object_p); + return new_values_p; +} /* ecma_fast_array_extend */ + +/** + * Delete the array object's property referenced by its value pointer. + * + * Note: specified property must be owned by specified object. + */ +void +ecma_array_object_delete_property (ecma_object_t *object_p, /**< object */ + ecma_string_t *property_name_p, /**< property name */ + ecma_property_value_t *prop_value_p) /**< property value reference */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + + if (!ext_obj_p->u.array.is_fast_mode) + { + ecma_delete_property (object_p, prop_value_p); + return; + } + + JERRY_ASSERT (object_p->u1.property_list_cp != JMEM_CP_NULL); + + uint32_t index = ecma_string_get_array_index (property_name_p); + + JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); + JERRY_ASSERT (index < ext_obj_p->u.array.length); + + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + if (ecma_is_value_array_hole (values_p[index])) + { + return; + } + + ecma_free_value_if_not_object (values_p[index]); + + values_p[index] = ECMA_VALUE_ARRAY_HOLE; + ext_obj_p->u.array.hole_count++; +} /* ecma_array_object_delete_property */ + +/** + * Low level delete of fast access mode array items + * + * @return the updated value of new_length + */ +static uint32_t +ecma_delete_fast_array_properties (ecma_object_t *object_p, /**< fast access mode array */ + uint32_t new_length) /**< new length of the fast access mode array */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + + ecma_ref_object (object_p); + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + uint32_t old_length = ext_obj_p->u.array.length; + const uint32_t old_aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (old_length); + JERRY_ASSERT (new_length < old_length); + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + + if (new_length == 0) + { + for (uint32_t i = 0; i < old_length; i++) + { + ecma_free_value_if_not_object (values_p[i]); + } + + jmem_heap_free_block (values_p, old_aligned_length * sizeof (ecma_value_t)); + object_p->u1.property_list_cp = JMEM_CP_NULL; + ext_obj_p->u.array.length = new_length; + ecma_deref_object (object_p); + return new_length; + } + + for (uint32_t i = new_length; i < old_length - 1; i++) + { + if (ecma_is_value_array_hole (values_p[i])) + { + ext_obj_p->u.array.hole_count--; + } + else + { + ecma_free_value_if_not_object (values_p[i]); + } + } + + const uint32_t new_aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (new_length); + + ecma_value_t *new_values_p; + new_values_p = (ecma_value_t *) jmem_heap_realloc_block (values_p, + old_aligned_length * sizeof (ecma_value_t), + new_aligned_length * sizeof (ecma_value_t)); + + ext_obj_p->u.array.length = new_length; + + ECMA_SET_POINTER (object_p->u1.property_list_cp, new_values_p); + ecma_deref_object (object_p); + + return new_length; +} /* ecma_delete_fast_array_properties */ + +/** + * Update the length of a fast access mode array to a new length + * + * Note: if the new length would result too much array hole in the underlying arraybuffer + * the array is converted back to normal property list based array + */ +static void +ecma_fast_array_set_length (ecma_object_t *object_p, /**< fast access mode array object */ + uint32_t new_length) /**< new length of the fast access mode array object*/ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + uint32_t old_length = ext_obj_p->u.array.length; + + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + JERRY_ASSERT (new_length >= old_length); + + if (new_length == old_length) + { + return; + } + + uint32_t new_holes = new_length - old_length - 1; + + if ((new_holes + ext_obj_p->u.array.hole_count) > ECMA_FAST_ARRAY_MAX_HOLE_COUNT) + { + ecma_fast_array_convert_to_normal (object_p); + return; + } + + ecma_fast_array_extend (object_p, new_length); + ext_obj_p->u.array.hole_count = (uint8_t) (ext_obj_p->u.array.hole_count + new_holes); + + return; +} /* ecma_fast_array_set_length */ + +/** + * Get collection of property names of a fast access mode array object + * + * Note: Since the fast array object only contains indexed, enumberable, writable, configurable properties + * we can return a collection of non-array hole array indices + * + * @return collection of strings - property names + */ +ecma_collection_header_t * +ecma_fast_array_get_property_names (ecma_object_t *object_p, /**< fast access mode array object */ + uint32_t opts) /**< any combination of ecma_list_properties_options_t values */ +{ + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + JERRY_ASSERT (ext_obj_p->u.array.is_fast_mode); + + ecma_collection_header_t *ret_p = ecma_new_values_collection (); + +#if ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) + if (opts & ECMA_LIST_SYMBOLS) + { + return ret_p; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */ + + uint32_t length = ext_obj_p->u.array.length; + + bool append_length = (!(opts & ECMA_LIST_ENUMERABLE) && !(opts & ECMA_LIST_ARRAY_INDICES)); + + if (length == 0) + { + if (append_length) + { + ecma_append_to_values_collection (ret_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH), 0); + } + + return ret_p; + } + + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + for (uint32_t i = 0; i < length; i++) + { + if (ecma_is_value_array_hole (values_p[i])) + { + continue; + } + + ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (i); + + ecma_append_to_values_collection (ret_p, ecma_make_string_value (index_str_p), ECMA_COLLECTION_NO_COPY); + } + + if (append_length) + { + ecma_append_to_values_collection (ret_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH), 0); + } + + if (opts & ECMA_LIST_CONVERT_FAST_ARRAYS) + { + ecma_fast_array_convert_to_normal (object_p); + } + + return ret_p; +} /* ecma_fast_array_get_property_names */ + /** * Array object creation operation. * @@ -77,6 +592,20 @@ ecma_op_create_array_object (const ecma_value_t *arguments_list_p, /**< list of array_items_p = NULL; array_items_count = 0; } + + if (length > ECMA_FAST_ARRAY_MAX_INITIAL_LENGTH) + { + return ecma_make_object_value (ecma_op_new_array_object (length)); + } + + ecma_object_t *object_p = ecma_op_new_fast_array_object (length); + + if (object_p == NULL) + { + object_p = ecma_op_new_array_object (length); + } + + return ecma_make_object_value (object_p); } else { @@ -85,43 +614,29 @@ ecma_op_create_array_object (const ecma_value_t *arguments_list_p, /**< list of array_items_count = arguments_list_len; } -#if ENABLED (JERRY_BUILTIN_ARRAY) - ecma_object_t *array_prototype_object_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAY_PROTOTYPE); -#else /* ENABLED (JERRY_BUILTIN_ARRAY) */ - ecma_object_t *array_prototype_object_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); -#endif /* !(ENABLED (JERRY_BUILTIN_ARRAY)) */ + ecma_object_t *object_p = ecma_op_new_fast_array_object (length); - ecma_object_t *object_p = ecma_create_object (array_prototype_object_p, - sizeof (ecma_extended_object_t), - ECMA_OBJECT_TYPE_ARRAY); + /* At this point we were not able to allocate a length * sizeof (ecma_value_t) amount of memory, + so we can terminate the engine since converting it into normal property list based array + would consume more memory. */ + if (object_p == NULL) + { + jerry_fatal (ERR_OUT_OF_MEMORY); + } - /* - * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_ARRAY type. - * - * See also: ecma_object_get_class_name - */ + if (length == 0) + { + return ecma_make_object_value (object_p); + } - ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; - ext_obj_p->u.array.length = length; - ext_obj_p->u.array.length_prop = ECMA_PROPERTY_FLAG_WRITABLE | ECMA_PROPERTY_TYPE_VIRTUAL; + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); for (uint32_t index = 0; index < array_items_count; index++) { - if (ecma_is_value_array_hole (array_items_p[index])) - { - continue; - } - - ecma_string_t *item_name_string_p = ecma_new_ecma_string_from_uint32 (index); - - ecma_builtin_helper_def_prop (object_p, - item_name_string_p, - array_items_p[index], - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); - - ecma_deref_ecma_string (item_name_string_p); + JERRY_ASSERT (!ecma_is_value_array_hole (array_items_p[index])); + values_p[index] = ecma_copy_value_if_not_object (array_items_p[index]); } return ecma_make_object_value (object_p); @@ -160,6 +675,167 @@ ecma_op_create_array_object_by_constructor (const ecma_value_t *arguments_list_p } /* ecma_op_create_array_object_by_constructor */ #endif /* ENABLED (JERRY_ES2015_CLASS) */ +/** + * Low level delete of array items from new_length to old_length + * + * Note: new_length must be less than old_length + * + * @return the updated value of new_length + */ +static uint32_t +ecma_delete_array_properties (ecma_object_t *object_p, /**< object */ + uint32_t new_length, /**< new length */ + uint32_t old_length) /**< old length */ +{ + JERRY_ASSERT (new_length < old_length); + JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + + if (ext_obj_p->u.array.is_fast_mode) + { + return ecma_delete_fast_array_properties (object_p, new_length); + } + + /* First the minimum value of new_length is updated. */ + jmem_cpointer_t current_prop_cp = object_p->u1.property_list_cp; + + if (current_prop_cp == JMEM_CP_NULL) + { + return new_length; + } + + ecma_property_header_t *current_prop_p; + +#if ENABLED (JERRY_PROPRETY_HASHMAP) + current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); + + if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) + { + current_prop_cp = current_prop_p->next_property_cp; + } +#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ + + while (current_prop_cp != JMEM_CP_NULL) + { + current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (current_prop_p)); + + ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) current_prop_p; + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + if (ECMA_PROPERTY_IS_NAMED_PROPERTY (current_prop_p->types[i]) + && !ecma_is_property_configurable (current_prop_p->types[i])) + { + uint32_t index = ecma_string_get_property_index (current_prop_p->types[i], + prop_pair_p->names_cp[i]); + + if (index < old_length && index >= new_length) + { + JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); + + new_length = index + 1; + + if (new_length == old_length) + { + /* Early return. */ + return new_length; + } + } + } + } + + current_prop_cp = current_prop_p->next_property_cp; + } + + /* Second all properties between new_length and old_length are deleted. */ + current_prop_cp = object_p->u1.property_list_cp; + ecma_property_header_t *prev_prop_p = NULL; + +#if ENABLED (JERRY_PROPRETY_HASHMAP) + JERRY_ASSERT (current_prop_cp != JMEM_CP_NULL); + + ecma_property_hashmap_delete_status hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_NO_HASHMAP; + current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); + + if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) + { + prev_prop_p = current_prop_p; + current_prop_cp = current_prop_p->next_property_cp; + hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP; + } +#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ + + while (current_prop_cp != JMEM_CP_NULL) + { + current_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, current_prop_cp); + + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (current_prop_p)); + ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) current_prop_p; + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + if (ECMA_PROPERTY_IS_NAMED_PROPERTY (current_prop_p->types[i]) + && ecma_is_property_configurable (current_prop_p->types[i])) + { + uint32_t index = ecma_string_get_property_index (current_prop_p->types[i], + prop_pair_p->names_cp[i]); + + if (index < old_length && index >= new_length) + { + JERRY_ASSERT (index != ECMA_STRING_NOT_ARRAY_INDEX); + +#if ENABLED (JERRY_PROPRETY_HASHMAP) + if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP) + { + hashmap_status = ecma_property_hashmap_delete (object_p, + prop_pair_p->names_cp[i], + current_prop_p->types + i); + } +#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ + + ecma_free_property (object_p, prop_pair_p->names_cp[i], current_prop_p->types + i); + current_prop_p->types[i] = ECMA_PROPERTY_TYPE_DELETED; + prop_pair_p->names_cp[i] = LIT_INTERNAL_MAGIC_STRING_DELETED; + } + } + } + + if (current_prop_p->types[0] == ECMA_PROPERTY_TYPE_DELETED + && current_prop_p->types[1] == ECMA_PROPERTY_TYPE_DELETED) + { + if (prev_prop_p == NULL) + { + object_p->u1.property_list_cp = current_prop_p->next_property_cp; + } + else + { + prev_prop_p->next_property_cp = current_prop_p->next_property_cp; + } + + jmem_cpointer_t next_prop_cp = current_prop_p->next_property_cp; + ecma_dealloc_property_pair ((ecma_property_pair_t *) current_prop_p); + current_prop_cp = next_prop_cp; + } + else + { + prev_prop_p = current_prop_p; + current_prop_cp = current_prop_p->next_property_cp; + } + } + +#if ENABLED (JERRY_PROPRETY_HASHMAP) + if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP) + { + ecma_property_hashmap_free (object_p); + ecma_property_hashmap_create (object_p); + } +#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */ + + return new_length; +} /* ecma_delete_array_properties */ + /** * Update the length of an array to a new length * @@ -244,6 +920,10 @@ ecma_op_array_object_set_length (ecma_object_t *object_p, /**< the array object { current_len_uint32 = ecma_delete_array_properties (object_p, new_len_uint32, old_len_uint32); } + else if (ext_object_p->u.array.is_fast_mode) + { + ecma_fast_array_set_length (object_p, new_len_uint32); + } ext_object_p->u.array.length = current_len_uint32; @@ -325,6 +1005,15 @@ ecma_op_array_object_define_own_property (ecma_object_t *object_p, /**< the arra return result; } + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + /* Note for further optimization: for enumerable, configurable, writable data properties + it's not necessary to convert it back to normal property list based array */ + if (JERRY_UNLIKELY (ext_object_p->u.array.is_fast_mode)) + { + ecma_fast_array_convert_to_normal (object_p); + } + uint32_t index = ecma_string_get_array_index (property_name_p); if (index == ECMA_STRING_NOT_ARRAY_INDEX) @@ -332,8 +1021,6 @@ ecma_op_array_object_define_own_property (ecma_object_t *object_p, /**< the arra return ecma_op_general_object_define_own_property (object_p, property_name_p, property_desc_p); } - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - bool update_length = (index >= ext_object_p->u.array.length); if (update_length && !ecma_is_property_writable (ext_object_p->u.array.length_prop)) diff --git a/jerry-core/ecma/operations/ecma-array-object.h b/jerry-core/ecma/operations/ecma-array-object.h index a901c5d0e..17865bccc 100644 --- a/jerry-core/ecma/operations/ecma-array-object.h +++ b/jerry-core/ecma/operations/ecma-array-object.h @@ -39,6 +39,28 @@ typedef enum * in the property descriptor */ } ecma_array_object_set_length_flags_t; +ecma_object_t * +ecma_op_new_array_object (ecma_length_t length); + +ecma_object_t * +ecma_op_new_fast_array_object (ecma_length_t length); + +ecma_value_t * +ecma_fast_array_extend (ecma_object_t *object_p, uint32_t new_lengt); + +bool +ecma_fast_array_set_property (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t value); + +void +ecma_array_object_delete_property (ecma_object_t *object_p, ecma_string_t *property_name_p, + ecma_property_value_t *prop_value_p); + +ecma_collection_header_t * +ecma_fast_array_get_property_names (ecma_object_t *object_p, uint32_t opts); + +void +ecma_fast_array_convert_to_normal (ecma_object_t *object_p); + ecma_value_t ecma_op_create_array_object (const ecma_value_t *arguments_list_p, ecma_length_t arguments_list_len, bool is_treat_single_arg_as_length); diff --git a/jerry-core/ecma/operations/ecma-objects-general.c b/jerry-core/ecma/operations/ecma-objects-general.c index fb23e5025..84257e905 100644 --- a/jerry-core/ecma/operations/ecma-objects-general.c +++ b/jerry-core/ecma/operations/ecma-objects-general.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "ecma-array-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" #include "ecma-function-object.h" @@ -159,8 +160,15 @@ ecma_op_general_object_delete (ecma_object_t *obj_p, /**< the object */ /* 3. */ if (ecma_is_property_configurable (property)) { - /* a. */ - ecma_delete_property (obj_p, property_ref.value_p); + if (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY) + { + ecma_array_object_delete_property (obj_p, property_name_p, property_ref.value_p); + } + else + { + /* a. */ + ecma_delete_property (obj_p, property_ref.value_p); + } /* b. */ return ECMA_VALUE_TRUE; diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 662546414..28cbf0a9d 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -122,10 +122,10 @@ ecma_op_object_get_own_property (ecma_object_t *object_p, /**< the object */ } case ECMA_OBJECT_TYPE_ARRAY: { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + if (ecma_string_is_length (property_name_p)) { - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - if (options & ECMA_PROPERTY_GET_VALUE) { property_ref_p->virtual_value = ecma_make_uint32_value (ext_object_p->u.array.length); @@ -133,6 +133,35 @@ ecma_op_object_get_own_property (ecma_object_t *object_p, /**< the object */ return ext_object_p->u.array.length_prop; } + + if (ext_object_p->u.array.is_fast_mode) + { + uint32_t index = ecma_string_get_array_index (property_name_p); + + if (index != ECMA_STRING_NOT_ARRAY_INDEX) + { + if (JERRY_LIKELY (index < ext_object_p->u.array.length)) + { + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + if (ecma_is_value_array_hole (values_p[index])) + { + return ECMA_PROPERTY_TYPE_NOT_FOUND; + } + + if (options & ECMA_PROPERTY_GET_VALUE) + { + property_ref_p->virtual_value = ecma_fast_copy_value (values_p[index]); + } + + return (ecma_property_t) (ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | ECMA_PROPERTY_TYPE_VIRTUAL); + } + } + + return ECMA_PROPERTY_TYPE_NOT_FOUND; + } + + break; } #if ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) @@ -445,12 +474,30 @@ ecma_op_object_find_own (ecma_value_t base_value, /**< base value */ } case ECMA_OBJECT_TYPE_ARRAY: { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + if (ecma_string_is_length (property_name_p)) { - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - return ecma_make_uint32_value (ext_object_p->u.array.length); } + + if (JERRY_LIKELY (ext_object_p->u.array.is_fast_mode)) + { + uint32_t index = ecma_string_get_array_index (property_name_p); + + if (JERRY_LIKELY (index != ECMA_STRING_NOT_ARRAY_INDEX)) + { + if (JERRY_LIKELY (index < ext_object_p->u.array.length)) + { + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + return (ecma_is_value_array_hole (values_p[index]) ? ECMA_VALUE_NOT_FOUND + : ecma_fast_copy_value (values_p[index])); + } + } + return ECMA_VALUE_NOT_FOUND; + } + break; } case ECMA_OBJECT_TYPE_PSEUDO_ARRAY: @@ -934,10 +981,10 @@ ecma_op_object_put (ecma_object_t *object_p, /**< the object */ { case ECMA_OBJECT_TYPE_ARRAY: { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + if (ecma_string_is_length (property_name_p)) { - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - if (ecma_is_property_writable (ext_object_p->u.array.length_prop)) { return ecma_op_array_object_set_length (object_p, value, 0); @@ -945,6 +992,22 @@ ecma_op_object_put (ecma_object_t *object_p, /**< the object */ return ecma_reject (is_throw); } + + if (JERRY_LIKELY (ext_object_p->u.array.is_fast_mode)) + { + if (JERRY_UNLIKELY (!ecma_get_object_extensible (object_p))) + { + return ecma_reject (is_throw); + } + + if (ecma_fast_array_set_property (object_p, property_name_p, value)) + { + return ECMA_VALUE_TRUE; + } + } + + JERRY_ASSERT (!(ext_object_p->u.array.is_fast_mode)); + break; } case ECMA_OBJECT_TYPE_PSEUDO_ARRAY: @@ -1594,6 +1657,16 @@ ecma_op_object_get_property_names (ecma_object_t *obj_p, /**< object */ JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); + if (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; + + if (ext_object_p->u.array.is_fast_mode) + { + return ecma_fast_array_get_property_names (obj_p, opts); + } + } + ecma_collection_header_t *ret_p = ecma_new_values_collection (); ecma_collection_header_t *skipped_non_enumerable_p = ecma_new_values_collection (); diff --git a/jerry-core/vm/vm-utils.c b/jerry-core/vm/vm-utils.c index 3acaa12e9..4327e002e 100644 --- a/jerry-core/vm/vm-utils.c +++ b/jerry-core/vm/vm-utils.c @@ -70,6 +70,7 @@ vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimite vm_frame_ctx_t *context_p = JERRY_CONTEXT (vm_top_context_p); ecma_object_t *array_p = ecma_get_object_from_value (result_array); + JERRY_ASSERT (((ecma_extended_object_t *) array_p)->u.array.is_fast_mode); uint32_t index = 0; while (context_p != NULL) @@ -98,15 +99,10 @@ vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimite ecma_deref_ecma_string (line_str_p); ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index); - ecma_property_value_t *prop_value_p; - prop_value_p = ecma_create_named_data_property (array_p, - index_str_p, - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, - NULL); + ecma_fast_array_set_property (array_p, index_str_p, ecma_make_string_value (str_p)); + ecma_deref_ecma_string (str_p); ecma_deref_ecma_string (index_str_p); - prop_value_p->value = ecma_make_string_value (str_p); - context_p = context_p->prev_context_p; index++; @@ -116,13 +112,6 @@ vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimite } } - if (index > 0) - { - JERRY_ASSERT (ecma_get_object_type (array_p) == ECMA_OBJECT_TYPE_ARRAY); - - ((ecma_extended_object_t *) array_p)->u.array.length = index; - } - return result_array; #else /* !ENABLED (JERRY_LINE_INFO) */ JERRY_UNUSED (max_depth); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index f8195c6ec..85bd6afa7 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -54,6 +54,7 @@ vm_op_get_value (ecma_value_t object, /**< base object */ { if (ecma_is_value_object (object)) { + ecma_object_t *object_p = ecma_get_object_from_value (object); ecma_string_t *property_name_p = NULL; if (ecma_is_value_integer_number (property)) @@ -62,6 +63,22 @@ vm_op_get_value (ecma_value_t object, /**< base object */ if (int_value >= 0 && int_value <= ECMA_DIRECT_STRING_MAX_IMM) { + if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_ARRAY) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + if (JERRY_LIKELY (ext_object_p->u.array.is_fast_mode + && (uint32_t) int_value < ext_object_p->u.array.length)) + { + ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); + + if (JERRY_LIKELY (!ecma_is_value_array_hole (values_p[int_value]))) + { + return ecma_fast_copy_value (values_p[int_value]); + } + } + } + property_name_p = (ecma_string_t *) ECMA_CREATE_DIRECT_STRING (ECMA_DIRECT_STRING_UINT, (uintptr_t) int_value); } @@ -81,7 +98,6 @@ vm_op_get_value (ecma_value_t object, /**< base object */ if (property_name_p != NULL) { #if ENABLED (JERRY_LCACHE) - ecma_object_t *object_p = ecma_get_object_from_value (object); ecma_property_t *property_p = ecma_lcache_lookup (object_p, property_name_p); if (property_p != NULL && @@ -92,7 +108,7 @@ vm_op_get_value (ecma_value_t object, /**< base object */ #endif /* ENABLED (JERRY_LCACHE) */ /* There is no need to free the name. */ - return ecma_op_object_get (ecma_get_object_from_value (object), property_name_p); + return ecma_op_object_get (object_p, property_name_p); } } @@ -1309,14 +1325,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_PUSH_ARRAY: { - result = ecma_op_create_array_object (NULL, 0, false); - - if (ECMA_IS_VALUE_ERROR (result)) - { - goto error; - } - - *stack_top_p++ = result; + // Note: this operation cannot throw an exception + *stack_top_p++ = ecma_make_object_value (ecma_op_new_fast_array_object (0)); continue; } #if ENABLED (JERRY_ES2015_CLASS) @@ -1646,45 +1656,31 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_APPEND_ARRAY: { ecma_object_t *array_obj_p; - uint32_t length_num; uint32_t values_length = *byte_code_p++; stack_top_p -= values_length; array_obj_p = ecma_get_object_from_value (stack_top_p[-1]); ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; + uint32_t old_length = ext_array_obj_p->u.array.length; - length_num = ext_array_obj_p->u.array.length; + JERRY_ASSERT (ext_array_obj_p->u.array.is_fast_mode); + + ecma_value_t *values_p = ecma_fast_array_extend (array_obj_p, old_length + values_length); for (uint32_t i = 0; i < values_length; i++) { - if (!ecma_is_value_array_hole (stack_top_p[i])) + values_p[old_length + i] = stack_top_p[i]; + + if (JERRY_UNLIKELY (ecma_is_value_array_hole (stack_top_p[i]))) { - ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (length_num); - - ecma_property_value_t *prop_value_p; - - prop_value_p = ecma_create_named_data_property (array_obj_p, - index_str_p, - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, - NULL); - - JERRY_ASSERT (ecma_is_value_undefined (prop_value_p->value)); - prop_value_p->value = stack_top_p[i]; - - /* The reference is moved so no need to free stack_top_p[i] except for objects. */ - if (ecma_is_value_object (stack_top_p[i])) - { - ecma_free_value (stack_top_p[i]); - } - - ecma_deref_ecma_string (index_str_p); + ext_array_obj_p->u.array.hole_count++; + } + else if (ecma_is_value_object (stack_top_p[i])) + { + ecma_deref_object (ecma_get_object_from_value (stack_top_p[i])); } - - length_num++; } - - ext_array_obj_p->u.array.length = length_num; continue; } case VM_OC_PUSH_UNDEFINED_BASE: diff --git a/tests/debugger/do_variables.expected b/tests/debugger/do_variables.expected index 5b476375c..b450d2058 100644 --- a/tests/debugger/do_variables.expected +++ b/tests/debugger/do_variables.expected @@ -111,9 +111,9 @@ level | type (jerry-debugger) variables 0 name | type | value y | String | abc -2 | Number | 3 -1 | Number | 2 0 | Number | 1 +1 | Number | 2 +2 | Number | 3 (jerry-debugger) variables 1 name | type | value h | undefined | undefined