jerryscript/jerry-core/ecma/base/ecma-property-hashmap.c
Zoltan Herczeg 5c852ab6fb Optimize LCache operation.
The cache stores only real properties now, because storing NULLs has
little benefit according to tests. Since only real properties are
stored now, there is no need to create real references to objects
and property names, which reduces the keeping of dead objects after
garbage collection.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
2016-06-02 03:56:36 -07:00

514 lines
16 KiB
C

/* Copyright 2014-2016 Samsung Electronics Co., Ltd.
* Copyright 2016 University of Szeged.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecma-globals.h"
#include "ecma-helpers.h"
#include "ecma-property-hashmap.h"
#include "jrt-libc-includes.h"
/** \addtogroup ecma ECMA
* @{
*
* \addtogroup ecmapropertyhashmap Property hashmap
* @{
*/
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
/**
* Compute the total size of the property hashmap.
*/
#define ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE(max_property_count) \
(sizeof (ecma_property_hashmap_t) + (max_property_count * sizeof (jmem_cpointer_t)) + (max_property_count >> 3))
/**
* Number of items in the stepping table.
*/
#define ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS 8
/**
* Stepping values for searching items in the hashmap.
*/
static const uint32_t ecma_property_hashmap_steps[ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS] =
{
3, 5, 7, 11, 13, 17, 19, 23
};
/**
* Get the value of a bit in a bitmap.
*/
#define ECMA_PROPERTY_HASHMAP_GET_BIT(byte_p, index) \
((byte_p)[(index) >> 3] & (1 << ((index) & 0x7)))
/**
* Clear the value of a bit in a bitmap.
*/
#define ECMA_PROPERTY_HASHMAP_CLEAR_BIT(byte_p, index) \
((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] & ~(1 << ((index) & 0x7))))
/**
* Set the value of a bit in a bitmap.
*/
#define ECMA_PROPERTY_HASHMAP_SET_BIT(byte_p, index) \
((byte_p)[(index) >> 3] = (uint8_t) ((byte_p)[(index) >> 3] | (1 << ((index) & 0x7))))
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
/**
* Create a new property hashmap for the object.
* The object must not have a property hashmap.
*/
void
ecma_property_hashmap_create (ecma_object_t *object_p) /**< object */
{
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
JERRY_ASSERT (ecma_get_property_list (object_p) != NULL);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (ecma_get_property_list (object_p)));
uint32_t named_property_count = 0;
ecma_property_header_t *prop_iter_p = ecma_get_property_list (object_p);
while (prop_iter_p != NULL)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
{
ecma_property_types_t type = ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + i);
if (type == ECMA_PROPERTY_TYPE_NAMEDDATA || type == ECMA_PROPERTY_TYPE_NAMEDACCESSOR)
{
named_property_count++;
}
}
prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t,
prop_iter_p->next_property_cp);
}
/* The max_property_count must be power of 2. */
uint32_t max_property_count = ECMA_PROPERTY_HASMAP_MINIMUM_SIZE;
/* At least 1/3 items must be NULL. */
while (max_property_count < (named_property_count + (named_property_count >> 1)))
{
max_property_count <<= 1;
}
size_t total_size = ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (max_property_count);
ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) jmem_heap_alloc_block (total_size);
memset (hashmap_p, 0, total_size);
hashmap_p->header.types[0].type_and_flags = ECMA_PROPERTY_TYPE_HASHMAP;
hashmap_p->header.next_property_cp = object_p->property_list_or_bound_object_cp;
hashmap_p->max_property_count = max_property_count;
hashmap_p->null_count = max_property_count - named_property_count;
jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
uint8_t *bits_p = (uint8_t *) (pair_list_p + max_property_count);
uint32_t mask = max_property_count - 1;
uint8_t shift_counter = 0;
if (max_property_count <= (1u << LIT_STRING_HASH_BITS))
{
hashmap_p->header.types[1].type_and_flags = 0;
}
else
{
while (max_property_count > (1u << LIT_STRING_HASH_BITS))
{
shift_counter++;
max_property_count >>= 1;
}
}
hashmap_p->header.types[1].type_and_flags = shift_counter;
prop_iter_p = ecma_get_property_list (object_p);
ECMA_SET_POINTER (object_p->property_list_or_bound_object_cp, hashmap_p);
while (prop_iter_p != NULL)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
{
ecma_property_types_t type = ECMA_PROPERTY_GET_TYPE (prop_iter_p->types + i);
if (!(type == ECMA_PROPERTY_TYPE_NAMEDDATA || type == ECMA_PROPERTY_TYPE_NAMEDACCESSOR))
{
continue;
}
ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p;
ecma_string_t *name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
property_pair_p->names_cp[i]);
uint32_t entry_index = name_p->hash;
uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
if (mask < (1u << LIT_STRING_HASH_BITS))
{
entry_index &= mask;
}
else
{
entry_index <<= shift_counter;
JERRY_ASSERT (entry_index <= mask);
}
#ifndef JERRY_NDEBUG
/* Because max_property_count (power of 2) and step (a prime
* number) are relative primes, all entries of the hasmap are
* visited exactly once before the start entry index is reached
* again. Furthermore because at least one NULL is present in
* the hashmap, the while loop must be terminated before the
* the starting index is reached again. */
uint32_t start_entry_index = entry_index;
#endif /* !JERRY_NDEBUG */
while (pair_list_p[entry_index] != ECMA_NULL_POINTER)
{
entry_index = (entry_index + step) & mask;
#ifndef JERRY_NDEBUG
JERRY_ASSERT (entry_index != start_entry_index);
#endif /* !JERRY_NDEBUG */
}
ECMA_SET_POINTER (pair_list_p[entry_index], property_pair_p);
if (i != 0)
{
ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index);
}
}
prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t,
prop_iter_p->next_property_cp);
}
#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
(void) object_p;
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
} /* ecma_property_hashmap_create */
/**
* Free the hashmap of the object.
* The object must have a property hashmap.
*/
void
ecma_property_hashmap_free (ecma_object_t *object_p) /**< object */
{
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
/* Property hash must be exists and must be the first property. */
ecma_property_header_t *property_p = ecma_get_property_list (object_p);
JERRY_ASSERT (property_p != NULL
&& ECMA_PROPERTY_GET_TYPE (property_p->types + 0) == ECMA_PROPERTY_TYPE_HASHMAP);
ecma_property_hashmap_t *hashmap_p = (ecma_property_hashmap_t *) property_p;
object_p->property_list_or_bound_object_cp = property_p->next_property_cp;
jmem_heap_free_block (hashmap_p,
ECMA_PROPERTY_HASHMAP_GET_TOTAL_SIZE (hashmap_p->max_property_count));
#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
(void) object_p;
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
} /* ecma_property_hashmap_free */
/**
* Insert named property into the hashmap.
*/
void
ecma_property_hashmap_insert (ecma_object_t *object_p, /**< object */
ecma_string_t *name_p, /**< name of the property */
ecma_property_pair_t *property_pair_p, /**< property pair */
int property_index) /**< property index in the pair (0 or 1) */
{
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t,
object_p->property_list_or_bound_object_cp);
JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (hashmap_p->header.types + 0) == ECMA_PROPERTY_TYPE_HASHMAP);
/* The NULLs are reduced below 1/8 of the hashmap. */
if (hashmap_p->null_count < (hashmap_p->max_property_count >> 3))
{
ecma_property_hashmap_free (object_p);
ecma_property_hashmap_create (object_p);
return;
}
JERRY_ASSERT (property_index < ECMA_PROPERTY_PAIR_ITEM_COUNT);
uint32_t entry_index = name_p->hash;
uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
uint32_t mask = hashmap_p->max_property_count - 1;
if (mask < (1u << LIT_STRING_HASH_BITS))
{
entry_index &= mask;
}
else
{
entry_index <<= hashmap_p->header.types[1].type_and_flags;
}
#ifndef JERRY_NDEBUG
/* See the comment for this variable in ecma_property_hashmap_create. */
uint32_t start_entry_index = entry_index;
#endif /* !JERRY_NDEBUG */
jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
while (pair_list_p[entry_index] != ECMA_NULL_POINTER)
{
entry_index = (entry_index + step) & mask;
#ifndef JERRY_NDEBUG
JERRY_ASSERT (entry_index != start_entry_index);
#endif /* !JERRY_NDEBUG */
}
ECMA_SET_POINTER (pair_list_p[entry_index], property_pair_p);
uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count);
bits_p += (entry_index >> 3);
mask = (uint32_t) (1 << (entry_index & 0x7));
if (!(*bits_p & mask))
{
/* Deleted entries also has ECMA_NULL_POINTER
* value, but they are not NULL values. */
hashmap_p->null_count--;
JERRY_ASSERT (hashmap_p->null_count > 0);
}
if (property_index == 0)
{
*bits_p = (uint8_t) ((*bits_p) & ~mask);
}
else
{
*bits_p = (uint8_t) ((*bits_p) | mask);
}
#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
(void) object_p;
(void) name_p;
(void) property_pair_p;
(void) property_index;
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
} /* ecma_property_hashmap_insert */
/**
* Delete named property from the hashmap.
*/
void
ecma_property_hashmap_delete (ecma_object_t *object_p, /**< object */
ecma_string_t *name_p, /**< name of the property */
ecma_property_t *property_p) /**< property */
{
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
ecma_property_hashmap_t *hashmap_p = ECMA_GET_NON_NULL_POINTER (ecma_property_hashmap_t,
object_p->property_list_or_bound_object_cp);
JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (hashmap_p->header.types + 0) == ECMA_PROPERTY_TYPE_HASHMAP);
uint32_t entry_index = name_p->hash;
uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
uint32_t mask = hashmap_p->max_property_count - 1;
jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count);
if (mask < (1u << LIT_STRING_HASH_BITS))
{
entry_index &= mask;
}
else
{
entry_index <<= hashmap_p->header.types[1].type_and_flags;
JERRY_ASSERT (entry_index <= mask);
}
#ifndef JERRY_NDEBUG
/* See the comment for this variable in ecma_property_hashmap_create. */
uint32_t start_entry_index = entry_index;
#endif /* !JERRY_NDEBUG */
while (true)
{
if (pair_list_p[entry_index] != ECMA_NULL_POINTER)
{
size_t offset = 0;
if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
{
offset = 1;
}
ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t,
pair_list_p[entry_index]);
if ((property_pair_p->header.types + offset) == property_p)
{
JERRY_ASSERT (ecma_compare_ecma_strings (ECMA_GET_NON_NULL_POINTER (ecma_string_t,
property_pair_p->names_cp[offset]),
name_p));
pair_list_p[entry_index] = ECMA_NULL_POINTER;
ECMA_PROPERTY_HASHMAP_SET_BIT (bits_p, entry_index);
return;
}
}
else
{
/* Must be a deleted entry. */
JERRY_ASSERT (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index));
}
entry_index = (entry_index + step) & mask;
#ifndef JERRY_NDEBUG
JERRY_ASSERT (entry_index != start_entry_index);
#endif /* !JERRY_NDEBUG */
}
#else /* CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
(void) object_p;
(void) name_p;
(void) property_p;
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
} /* ecma_property_hashmap_delete */
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
/**
* Find a named property.
*
* @return pointer to the property if found or NULL otherwise
*/
ecma_property_t *
ecma_property_hashmap_find (ecma_property_hashmap_t *hashmap_p, /**< hashmap */
ecma_string_t *name_p, /**< property name */
ecma_string_t **property_real_name_p) /**< [out] property real name */
{
#ifndef JERRY_NDEBUG
/* A sanity check in debug mode: a named property must be present
* in both the property hashmap and in the property chain, or missing
* from both data collection. The following code checks the property
* chain, and sets the property_found variable. */
bool property_found = false;
ecma_property_header_t *prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t,
hashmap_p->header.next_property_cp);
while (prop_iter_p != NULL && !property_found)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
{
if (prop_pair_p->names_cp[i] != ECMA_NULL_POINTER)
{
ecma_string_t *property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
prop_pair_p->names_cp[i]);
if (ecma_compare_ecma_strings (name_p, property_name_p))
{
property_found = true;
break;
}
}
}
prop_iter_p = ECMA_GET_POINTER (ecma_property_header_t,
prop_iter_p->next_property_cp);
}
#endif /* !JERRY_NDEBUG */
uint32_t entry_index = name_p->hash;
uint32_t step = ecma_property_hashmap_steps[entry_index & (ECMA_PROPERTY_HASHMAP_NUMBER_OF_STEPS - 1)];
uint32_t mask = hashmap_p->max_property_count - 1;
jmem_cpointer_t *pair_list_p = (jmem_cpointer_t *) (hashmap_p + 1);
uint8_t *bits_p = (uint8_t *) (pair_list_p + hashmap_p->max_property_count);
if (mask < (1u << LIT_STRING_HASH_BITS))
{
entry_index &= mask;
}
else
{
entry_index <<= hashmap_p->header.types[1].type_and_flags;
JERRY_ASSERT (entry_index <= mask);
}
#ifndef JERRY_NDEBUG
/* See the comment for this variable in ecma_property_hashmap_create. */
uint32_t start_entry_index = entry_index;
#endif /* !JERRY_NDEBUG */
while (true)
{
if (pair_list_p[entry_index] != ECMA_NULL_POINTER)
{
size_t offset = 0;
if (ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
{
offset = 1;
}
ecma_property_pair_t *property_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_property_pair_t,
pair_list_p[entry_index]);
ecma_string_t *property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
property_pair_p->names_cp[offset]);
if (ecma_compare_ecma_strings (name_p, property_name_p))
{
#ifndef JERRY_NDEBUG
JERRY_ASSERT (property_found);
#endif /* !JERRY_NDEBUG */
*property_real_name_p = property_name_p;
return property_pair_p->header.types + offset;
}
}
else
{
if (!ECMA_PROPERTY_HASHMAP_GET_BIT (bits_p, entry_index))
{
#ifndef JERRY_NDEBUG
JERRY_ASSERT (!property_found);
#endif /* !JERRY_NDEBUG */
return NULL;
}
/* Otherwise it is a deleted entry. */
}
entry_index = (entry_index + step) & mask;
#ifndef JERRY_NDEBUG
JERRY_ASSERT (entry_index != start_entry_index);
#endif /* !JERRY_NDEBUG */
}
} /* ecma_property_hashmap_find */
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
/**
* @}
* @}
*/