/* Copyright 2014-2015 Samsung Electronics Co., Ltd. * * 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. */ /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmahelpers Helpers for operations with ECMA data types * @{ */ #include "deserializer.h" #include "ecma-alloc.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lcache.h" #include "jrt.h" #include "jrt-libc-includes.h" #include "vm.h" /** * Maximum length of strings' concatenation */ #define ECMA_STRING_MAX_CONCATENATION_LENGTH (CONFIG_ECMA_STRING_MAX_CONCATENATION_LENGTH) /** * Limit for magic string length */ #define ECMA_STRING_MAGIC_STRING_LENGTH_LIMIT 32 /** * The length should be representable with int32_t. */ JERRY_STATIC_ASSERT ((uint32_t) ((int32_t) ECMA_STRING_MAX_CONCATENATION_LENGTH) == ECMA_STRING_MAX_CONCATENATION_LENGTH); /** * Lengths of magic strings */ static ecma_length_t ecma_magic_string_lengths [ECMA_MAGIC_STRING__COUNT]; #ifndef JERRY_NDEBUG /** * Maximum length among lengths of magic strings */ static ecma_length_t ecma_magic_string_max_length; #endif /* !JERRY_NDEBUG */ static void ecma_init_ecma_string_from_lit_index (ecma_string_t *string_p, literal_index_t lit_index, bool is_stack_var); static void ecma_init_ecma_string_from_magic_string_id (ecma_string_t *string_p, ecma_magic_string_id_t magic_string_id, bool is_stack_var); /** * Allocate a collection of ecma-chars. * * @return pointer to the collection's header */ static ecma_collection_header_t* ecma_new_chars_collection (const ecma_char_t chars_buffer[], /**< ecma-chars */ ecma_length_t chars_number) /**< number of ecma-chars */ { JERRY_ASSERT (chars_buffer != NULL); JERRY_ASSERT (chars_number > 0); ecma_collection_header_t* collection_p = ecma_alloc_collection_header (); collection_p->unit_number = chars_number; uint16_t* next_chunk_cp_p = &collection_p->next_chunk_cp; ecma_char_t* cur_char_buf_iter_p = (ecma_char_t*) collection_p->data; ecma_char_t* cur_char_buf_end_p = cur_char_buf_iter_p + sizeof (collection_p->data) / sizeof (ecma_char_t); for (ecma_length_t char_index = 0; char_index < chars_number; char_index++) { if (unlikely (cur_char_buf_iter_p == cur_char_buf_end_p)) { ecma_collection_chunk_t *chunk_p = ecma_alloc_collection_chunk (); ECMA_SET_NON_NULL_POINTER (*next_chunk_cp_p, chunk_p); next_chunk_cp_p = &chunk_p->next_chunk_cp; cur_char_buf_iter_p = (ecma_char_t*) chunk_p->data; cur_char_buf_end_p = cur_char_buf_iter_p + sizeof (chunk_p->data) / sizeof (ecma_char_t); } JERRY_ASSERT (cur_char_buf_iter_p + 1 <= cur_char_buf_end_p); *cur_char_buf_iter_p++ = chars_buffer[char_index]; } *next_chunk_cp_p = ECMA_NULL_POINTER; return collection_p; } /* ecma_new_chars_collection */ /** * Compare two collection of ecma-chars. * * @return true - if collections are equal, * false - otherwise. */ static bool ecma_compare_chars_collection (const ecma_collection_header_t* header1_p, /**< first collection's header */ const ecma_collection_header_t* header2_p) /**< second collection's header */ { JERRY_ASSERT (header1_p != NULL && header2_p != NULL); if (header1_p->unit_number != header2_p->unit_number) { return false; } const ecma_length_t chars_number = header1_p->unit_number; const ecma_char_t* cur_char_buf1_iter_p = (ecma_char_t*) header1_p->data; const ecma_char_t* cur_char_buf1_end_p = cur_char_buf1_iter_p + sizeof (header1_p->data) / sizeof (ecma_char_t); const ecma_char_t* cur_char_buf2_iter_p = (ecma_char_t*) header2_p->data; const ecma_char_t* cur_char_buf2_end_p = cur_char_buf2_iter_p + sizeof (header2_p->data) / sizeof (ecma_char_t); uint16_t next_chunk1_cp = header1_p->next_chunk_cp; uint16_t next_chunk2_cp = header2_p->next_chunk_cp; for (ecma_length_t char_index = 0; char_index < chars_number; char_index++) { if (unlikely (cur_char_buf1_iter_p == cur_char_buf1_end_p)) { JERRY_ASSERT (cur_char_buf2_iter_p == cur_char_buf2_end_p); const ecma_collection_chunk_t *chunk1_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_chunk_t, next_chunk1_cp); const ecma_collection_chunk_t *chunk2_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_chunk_t, next_chunk2_cp); cur_char_buf1_iter_p = (ecma_char_t*) chunk1_p->data; cur_char_buf1_end_p = cur_char_buf1_iter_p + sizeof (chunk1_p->data) / sizeof (ecma_char_t); cur_char_buf2_iter_p = (ecma_char_t*) chunk2_p->data; cur_char_buf2_end_p = cur_char_buf2_iter_p + sizeof (chunk2_p->data) / sizeof (ecma_char_t); next_chunk1_cp = chunk1_p->next_chunk_cp; next_chunk2_cp = chunk2_p->next_chunk_cp; } JERRY_ASSERT (cur_char_buf1_iter_p + 1 <= cur_char_buf1_end_p); JERRY_ASSERT (cur_char_buf2_iter_p + 1 <= cur_char_buf2_end_p); if (*cur_char_buf1_iter_p++ != *cur_char_buf2_iter_p++) { return false; } } return true; } /* ecma_compare_chars_collection */ /** * Copy the collection of ecma-chars. * * @return pointer to collection copy */ static ecma_collection_header_t* ecma_copy_chars_collection (const ecma_collection_header_t* collection_p) /**< collection's header */ { JERRY_ASSERT (collection_p != NULL); ecma_collection_header_t *new_header_p = ecma_alloc_collection_header (); *new_header_p = *collection_p; uint16_t* next_chunk_cp_p = &new_header_p->next_chunk_cp; ecma_collection_chunk_t *chunk_p = ECMA_GET_POINTER (ecma_collection_chunk_t, collection_p->next_chunk_cp); while (chunk_p != NULL) { ecma_collection_chunk_t *new_chunk_p = ecma_alloc_collection_chunk (); *new_chunk_p = *chunk_p; ECMA_SET_NON_NULL_POINTER (*next_chunk_cp_p, new_chunk_p); next_chunk_cp_p = &new_chunk_p->next_chunk_cp; chunk_p = ECMA_GET_POINTER (ecma_collection_chunk_t, chunk_p->next_chunk_cp); } *next_chunk_cp_p = ECMA_NULL_POINTER; return new_header_p; } /* ecma_copy_chars_collection */ /** * Copy characters of the collection to buffer */ static void ecma_copy_chars_collection_to_buffer (const ecma_collection_header_t *collection_p, /**< collection header */ ecma_char_t chars_buffer[], /**< buffer for characters */ size_t buffer_size) /**< size of the buffer */ { JERRY_ASSERT (collection_p != NULL); ecma_char_t *out_chars_buf_iter_p = chars_buffer; const ecma_length_t chars_number = collection_p->unit_number; uint16_t next_chunk_cp = collection_p->next_chunk_cp; const ecma_char_t* cur_char_buf_iter_p = (ecma_char_t*) collection_p->data; const ecma_char_t* cur_char_buf_end_p = cur_char_buf_iter_p + sizeof (collection_p->data) / sizeof (ecma_char_t); for (ecma_length_t char_index = 0; char_index < chars_number; char_index++) { if (unlikely (cur_char_buf_iter_p == cur_char_buf_end_p)) { const ecma_collection_chunk_t *chunk_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_chunk_t, next_chunk_cp); cur_char_buf_iter_p = (ecma_char_t*) chunk_p->data; cur_char_buf_end_p = cur_char_buf_iter_p + sizeof (chunk_p->data) / sizeof (ecma_char_t); next_chunk_cp = chunk_p->next_chunk_cp; } JERRY_ASSERT (cur_char_buf_iter_p + 1 <= cur_char_buf_end_p); *out_chars_buf_iter_p++ = *cur_char_buf_iter_p++; } *out_chars_buf_iter_p = ECMA_CHAR_NULL; JERRY_ASSERT (out_chars_buf_iter_p - chars_buffer <= (ssize_t) buffer_size); } /* ecma_copy_chars_collection_to_buffer */ /** * Free the collection of ecma-chars. */ static void ecma_free_chars_collection (ecma_collection_header_t* collection_p) /**< collection's header */ { JERRY_ASSERT (collection_p != NULL); ecma_collection_chunk_t *chunk_p = ECMA_GET_POINTER (ecma_collection_chunk_t, collection_p->next_chunk_cp); while (chunk_p != NULL) { ecma_collection_chunk_t *next_chunk_p = ECMA_GET_POINTER (ecma_collection_chunk_t, chunk_p->next_chunk_cp); ecma_dealloc_collection_chunk (chunk_p); chunk_p = next_chunk_p; } ecma_dealloc_collection_header (collection_p); } /* ecma_free_chars_collection */ /** * Initialize data for string helpers */ void ecma_strings_init (void) { /* Initializing magic strings information */ #ifndef JERRY_NDEBUG ecma_magic_string_max_length = 0; #endif /* !JERRY_NDEBUG */ for (ecma_magic_string_id_t id = (ecma_magic_string_id_t) 0; id < ECMA_MAGIC_STRING__COUNT; id = (ecma_magic_string_id_t) (id + 1)) { ecma_magic_string_lengths [id] = ecma_zt_string_length (ecma_get_magic_string_zt (id)); #ifndef JERRY_NDEBUG ecma_magic_string_max_length = JERRY_MAX (ecma_magic_string_max_length, ecma_magic_string_lengths [id]); JERRY_ASSERT (ecma_magic_string_max_length <= ECMA_STRING_MAGIC_STRING_LENGTH_LIMIT); #endif /* !JERRY_NDEBUG */ } } /* ecma_strings_init */ /** * Initialize ecma-string descriptor with string described by index in literal table */ static void ecma_init_ecma_string_from_lit_index (ecma_string_t *string_p, /**< descriptor to initialize */ literal_index_t lit_index, /**< index in the literal table */ bool is_stack_var) /**< flag indicating whether the string descriptor is placed on stack (true) or in the heap (false) */ { #ifndef JERRY_NDEBUG JERRY_ASSERT (is_stack_var == (!mem_is_heap_pointer (string_p))); #endif /* !JERRY_NDEBUG */ const literal lit = deserialize_literal_by_id (lit_index); if (lit.type == LIT_MAGIC_STR) { ecma_init_ecma_string_from_magic_string_id (string_p, lit.data.magic_str_id, is_stack_var); return; } JERRY_ASSERT (lit.type == LIT_STR); string_p->refs = 1; string_p->is_stack_var = is_stack_var; string_p->container = ECMA_STRING_CONTAINER_LIT_TABLE; string_p->hash = lit.data.lp.hash; string_p->u.common_field = 0; string_p->u.lit_index = lit_index; } /* ecma_init_ecma_string_from_lit_index */ /** * Initialize ecma-string descriptor with specified magic string */ static void ecma_init_ecma_string_from_magic_string_id (ecma_string_t *string_p, /**< descriptor to initialize */ ecma_magic_string_id_t magic_string_id, /**< identifier of the magic string */ bool is_stack_var) /**< flag indicating whether the string descriptor is placed on stack (true) or in the heap (false) */ { #ifndef JERRY_NDEBUG JERRY_ASSERT (is_stack_var == (!mem_is_heap_pointer (string_p))); #endif /* !JERRY_NDEBUG */ string_p->refs = 1; string_p->is_stack_var = is_stack_var; string_p->container = ECMA_STRING_CONTAINER_MAGIC_STRING; string_p->hash = ecma_chars_buffer_calc_hash_last_chars (ecma_get_magic_string_zt (magic_string_id), ecma_magic_string_lengths [magic_string_id]); string_p->u.common_field = 0; string_p->u.magic_string_id = magic_string_id; } /* ecma_init_ecma_string_from_magic_string_id */ /** * Allocate new ecma-string and fill it with characters from specified buffer * * @return pointer to ecma-string descriptor */ ecma_string_t* ecma_new_ecma_string (const ecma_char_t *string_p) /**< zero-terminated string */ { JERRY_ASSERT (string_p != NULL); ecma_magic_string_id_t magic_string_id; if (ecma_is_zt_string_magic (string_p, &magic_string_id)) { return ecma_get_magic_string (magic_string_id); } ecma_length_t length = 0; const ecma_char_t *iter_p = string_p; while (*iter_p++) { length++; } JERRY_ASSERT (length > 0); ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_HEAP_CHUNKS; string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (string_p, length); string_desc_p->u.common_field = 0; ecma_collection_header_t *collection_p = ecma_new_chars_collection (string_p, length); ECMA_SET_NON_NULL_POINTER (string_desc_p->u.collection_cp, collection_p); return string_desc_p; } /* ecma_new_ecma_string */ /** * Allocate new ecma-string and fill it with ecma-number * * @return pointer to ecma-string descriptor */ ecma_string_t* ecma_new_ecma_string_from_uint32 (uint32_t uint32_number) /**< UInt32-represented ecma-number */ { ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_UINT32_IN_DESC; uint32_t last_two_digits = uint32_number % 100; uint32_t digit_pl = last_two_digits / 10; uint32_t digit_l = last_two_digits % 10; FIXME (/* Use digit to char conversion routine */); const ecma_char_t digits[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; const bool is_one_char_or_more = (uint32_number >= 10); const ecma_char_t last_chars [ECMA_STRING_HASH_LAST_CHARS_COUNT] = { is_one_char_or_more ? digits [digit_pl] : digits[digit_l], is_one_char_or_more ? digits [digit_l] : ECMA_CHAR_NULL }; /* Only last two chars are really used for hash calculation */ string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (last_chars, is_one_char_or_more ? 2 : 1); #ifndef JERRY_NDEBUG ecma_char_t char_buf [ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; ssize_t chars_copied = ecma_uint32_to_string (uint32_number, char_buf, ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); JERRY_ASSERT ((ecma_length_t) chars_copied == chars_copied); JERRY_ASSERT (string_desc_p->hash == ecma_chars_buffer_calc_hash_last_chars (char_buf, ecma_zt_string_length (char_buf))); #endif /* !JERRY_NDEBUG */ string_desc_p->u.common_field = 0; string_desc_p->u.uint32_number = uint32_number; return string_desc_p; } /* ecma_new_ecma_string_from_uint32 */ /** * Allocate new ecma-string and fill it with ecma-number * * @return pointer to ecma-string descriptor */ ecma_string_t* ecma_new_ecma_string_from_number (ecma_number_t num) /**< ecma-number */ { uint32_t uint32_num = ecma_number_to_uint32 (num); if (num == ecma_uint32_to_number (uint32_num)) { return ecma_new_ecma_string_from_uint32 (uint32_num); } ecma_char_t str_buf[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER + 1]; ecma_length_t length = ecma_number_to_zt_string (num, str_buf, sizeof (str_buf)); ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_HEAP_NUMBER; string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (str_buf, length); string_desc_p->u.common_field = 0; ecma_number_t *num_p = ecma_alloc_number (); *num_p = num; ECMA_SET_POINTER (string_desc_p->u.number_cp, num_p); return string_desc_p; } /* ecma_new_ecma_string_from_number */ /** * Initialize ecma-string descriptor placed on stack * with string described by index in literal table */ void ecma_new_ecma_string_on_stack_from_lit_index (ecma_string_t *string_p, /**< pointer to the ecma-string descriptor to initialize */ literal_index_t lit_index) /**< index in the literal table */ { ecma_init_ecma_string_from_lit_index (string_p, lit_index, true); } /* ecma_new_ecma_string_on_stack_from_lit_index */ /** * Allocate new ecma-string and fill it with reference to string literal * * @return pointer to ecma-string descriptor */ ecma_string_t* ecma_new_ecma_string_from_lit_index (literal_index_t lit_index) /**< index in the literal table */ { ecma_string_t* string_desc_p = ecma_alloc_string (); ecma_init_ecma_string_from_lit_index (string_desc_p, lit_index, false); return string_desc_p; } /* ecma_new_ecma_string_from_lit_index */ /** * Initialize ecma-string descriptor placed on stack with specified magic string */ void ecma_new_ecma_string_on_stack_from_magic_string_id (ecma_string_t *string_p, /**< pointer to the ecma-string descriptor to initialize */ ecma_magic_string_id_t id) /**< magic string id */ { ecma_init_ecma_string_from_magic_string_id (string_p, id, true); } /* ecma_new_ecma_string_on_stack_from_magic_string_id */ /** * Allocate new ecma-string and fill it with reference to ECMA magic string * * @return pointer to ecma-string descriptor */ ecma_string_t* ecma_new_ecma_string_from_magic_string_id (ecma_magic_string_id_t id) /**< identifier of magic string */ { JERRY_ASSERT (id < ECMA_MAGIC_STRING__COUNT); ecma_string_t* string_desc_p = ecma_alloc_string (); ecma_init_ecma_string_from_magic_string_id (string_desc_p, id, false); return string_desc_p; } /* ecma_new_ecma_string_from_magic_string_id */ /** * Concatenate ecma-strings * * @return concatenation of two ecma-strings */ ecma_string_t* ecma_concat_ecma_strings (ecma_string_t *string1_p, /**< first ecma-string */ ecma_string_t *string2_p) /**< second ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); uint32_t str1_len = (uint32_t) ecma_string_get_length (string1_p); uint32_t str2_len = (uint32_t) ecma_string_get_length (string2_p); if (str1_len == 0) { return ecma_copy_or_ref_ecma_string (string2_p); } else if (str2_len == 0) { return ecma_copy_or_ref_ecma_string (string1_p); } int64_t length = (int64_t) str1_len + (int64_t) str2_len; if (length > ECMA_STRING_MAX_CONCATENATION_LENGTH) { jerry_fatal (ERR_OUT_OF_MEMORY); } ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_CONCATENATION; string_desc_p->u.common_field = 0; string1_p = ecma_copy_or_ref_ecma_string (string1_p); string2_p = ecma_copy_or_ref_ecma_string (string2_p); ECMA_SET_NON_NULL_POINTER (string_desc_p->u.concatenation.string1_cp, string1_p); ECMA_SET_NON_NULL_POINTER (string_desc_p->u.concatenation.string2_cp, string2_p); if (str2_len >= ECMA_STRING_HASH_LAST_CHARS_COUNT) { string_desc_p->hash = string2_p->hash; } else { JERRY_STATIC_ASSERT (ECMA_STRING_HASH_LAST_CHARS_COUNT == 2); JERRY_ASSERT (str2_len == 1); ecma_char_t chars_buf[ECMA_STRING_HASH_LAST_CHARS_COUNT] = { ecma_string_get_char_at_pos (string1_p, str1_len - 1u), ecma_string_get_char_at_pos (string2_p, 0) }; string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (chars_buf, ECMA_STRING_HASH_LAST_CHARS_COUNT); } return string_desc_p; } /* ecma_concat_ecma_strings */ /** * Copy ecma-string * * @return pointer to copy of ecma-string with reference counter set to 1 */ static ecma_string_t* ecma_copy_ecma_string (ecma_string_t *string_desc_p) /**< string descriptor */ { JERRY_ASSERT (string_desc_p != NULL); JERRY_ASSERT (string_desc_p->refs > 0); ecma_string_t *new_str_p; switch ((ecma_string_container_t) string_desc_p->container) { case ECMA_STRING_CONTAINER_LIT_TABLE: case ECMA_STRING_CONTAINER_UINT32_IN_DESC: case ECMA_STRING_CONTAINER_MAGIC_STRING: { new_str_p = ecma_alloc_string (); *new_str_p = *string_desc_p; new_str_p->refs = 1; new_str_p->is_stack_var = false; break; } case ECMA_STRING_CONTAINER_CONCATENATION: { ecma_string_t *part1_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_desc_p->u.concatenation.string1_cp); ecma_string_t *part2_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_desc_p->u.concatenation.string2_cp); new_str_p = ecma_concat_ecma_strings (part1_p, part2_p); break; } case ECMA_STRING_CONTAINER_HEAP_NUMBER: { ecma_number_t *num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, string_desc_p->u.number_cp); new_str_p = ecma_new_ecma_string_from_number (*num_p); break; } case ECMA_STRING_CONTAINER_HEAP_CHUNKS: { new_str_p = ecma_alloc_string (); *new_str_p = *string_desc_p; const ecma_collection_header_t *chars_collection_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, string_desc_p->u.collection_cp); JERRY_ASSERT (chars_collection_p != NULL); ecma_collection_header_t *new_chars_collection_p = ecma_copy_chars_collection (chars_collection_p); ECMA_SET_NON_NULL_POINTER (new_str_p->u.collection_cp, new_chars_collection_p); break; } default: { JERRY_UNREACHABLE (); } } JERRY_ASSERT (ecma_compare_ecma_strings (string_desc_p, new_str_p)); return new_str_p; } /* ecma_copy_ecma_string */ /** * Increase reference counter of ecma-string. * * @return pointer to same ecma-string descriptor with increased reference counter * or the ecma-string's copy with reference counter set to 1 */ ecma_string_t* ecma_copy_or_ref_ecma_string (ecma_string_t *string_desc_p) /**< string descriptor */ { JERRY_ASSERT (string_desc_p != NULL); JERRY_ASSERT (string_desc_p->refs > 0); if (string_desc_p->is_stack_var) { return ecma_copy_ecma_string (string_desc_p); } else { string_desc_p->refs++; if (unlikely (string_desc_p->refs == 0)) { /* reference counter has overflowed */ string_desc_p->refs--; uint32_t current_refs = string_desc_p->refs; /* First trying to free unreachable objects that maybe refer to the string */ ecma_lcache_invalidate_all (); ecma_gc_run ((ecma_gc_gen_t) (ECMA_GC_GEN_COUNT - 1)); if (current_refs == string_desc_p->refs) { /* reference counter was not changed during GC, copying string */ return ecma_copy_ecma_string (string_desc_p); } string_desc_p->refs++; JERRY_ASSERT (string_desc_p->refs != 0); } } return string_desc_p; } /* ecma_copy_or_ref_ecma_string */ /** * Decrease reference counter and deallocate ecma-string if * after that the counter the counter becomes zero. */ void ecma_deref_ecma_string (ecma_string_t *string_p) /**< ecma-string */ { JERRY_ASSERT (string_p != NULL); JERRY_ASSERT (string_p->refs != 0); #ifndef JERRY_NDEBUG JERRY_ASSERT (string_p->is_stack_var == (!mem_is_heap_pointer (string_p))); #endif /* !JERRY_NDEBUG */ JERRY_ASSERT (!string_p->is_stack_var || string_p->refs == 1); string_p->refs--; if (string_p->refs != 0) { return; } switch ((ecma_string_container_t)string_p->container) { case ECMA_STRING_CONTAINER_HEAP_CHUNKS: { ecma_collection_header_t *chars_collection_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, string_p->u.collection_cp); ecma_free_chars_collection (chars_collection_p); break; } case ECMA_STRING_CONTAINER_HEAP_NUMBER: { ecma_number_t *num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, string_p->u.number_cp); ecma_dealloc_number (num_p); break; } case ECMA_STRING_CONTAINER_CONCATENATION: { ecma_string_t *string1_p, *string2_p; string1_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_p->u.concatenation.string1_cp); string2_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_p->u.concatenation.string2_cp); ecma_deref_ecma_string (string1_p); ecma_deref_ecma_string (string2_p); break; } case ECMA_STRING_CONTAINER_LIT_TABLE: case ECMA_STRING_CONTAINER_UINT32_IN_DESC: case ECMA_STRING_CONTAINER_MAGIC_STRING: { /* only the string descriptor itself should be freed */ } } if (!string_p->is_stack_var) { ecma_dealloc_string (string_p); } } /* ecma_deref_ecma_string */ /** * Assertion that specified ecma-string need not be freed */ void ecma_check_that_ecma_string_need_not_be_freed (const ecma_string_t *string_p) /**< ecma-string descriptor */ { #ifdef JERRY_NDEBUG (void) string_p; #else /* JERRY_NDEBUG */ /* Heap strings always need to be freed */ JERRY_ASSERT (string_p->is_stack_var); /* * No reference counter increment or decrement * should be performed with ecma-string placed * on stack */ JERRY_ASSERT (string_p->refs == 1); ecma_string_container_t container_type = (ecma_string_container_t) string_p->container; JERRY_ASSERT (container_type == ECMA_STRING_CONTAINER_LIT_TABLE || container_type == ECMA_STRING_CONTAINER_MAGIC_STRING || container_type == ECMA_STRING_CONTAINER_UINT32_IN_DESC); #endif /* !JERRY_NDEBUG */ } /* ecma_check_that_ecma_string_need_not_be_freed */ /** * Convert ecma-string to number */ ecma_number_t ecma_string_to_number (const ecma_string_t *str_p) /**< ecma-string */ { JERRY_ASSERT (str_p != NULL); switch ((ecma_string_container_t)str_p->container) { case ECMA_STRING_CONTAINER_UINT32_IN_DESC: { uint32_t uint32_number = str_p->u.uint32_number; return ecma_uint32_to_number (uint32_number); } case ECMA_STRING_CONTAINER_HEAP_NUMBER: { ecma_number_t *num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, str_p->u.number_cp); return *num_p; } case ECMA_STRING_CONTAINER_LIT_TABLE: case ECMA_STRING_CONTAINER_HEAP_CHUNKS: case ECMA_STRING_CONTAINER_CONCATENATION: case ECMA_STRING_CONTAINER_MAGIC_STRING: { const int32_t string_len = ecma_string_get_length (str_p); const size_t string_buf_size = (size_t) (string_len + 1) * sizeof (ecma_char_t); ecma_char_t *str_buffer_p = (ecma_char_t*) mem_heap_alloc_block (string_buf_size, MEM_HEAP_ALLOC_SHORT_TERM); if (str_buffer_p == NULL) { jerry_fatal (ERR_OUT_OF_MEMORY); } ssize_t bytes_copied = ecma_string_to_zt_string (str_p, str_buffer_p, (ssize_t) string_buf_size); JERRY_ASSERT (bytes_copied > 0); ecma_number_t num = ecma_zt_string_to_number (str_buffer_p); mem_heap_free_block (str_buffer_p); return num; } } JERRY_UNREACHABLE (); } /* ecma_string_to_number */ /** * Copy ecma-string's contents to a buffer. * * Buffer will contain length of string, in characters, followed by string's characters. * * @return number of bytes, actually copied to the buffer - if string's content was copied successfully; * otherwise (in case size of buffer is insuficcient) - negative number, which is calculated * as negation of buffer size, that is required to hold the string's content. */ ssize_t ecma_string_to_zt_string (const ecma_string_t *string_desc_p, /**< ecma-string descriptor */ ecma_char_t *buffer_p, /**< destination buffer */ ssize_t buffer_size) /**< size of buffer */ { JERRY_ASSERT (string_desc_p != NULL); JERRY_ASSERT (string_desc_p->refs > 0); JERRY_ASSERT (buffer_p != NULL); JERRY_ASSERT (buffer_size > 0); ssize_t required_buffer_size = ((ecma_string_get_length (string_desc_p) + 1) * ((ssize_t) sizeof (ecma_char_t))); if (required_buffer_size > buffer_size) { return -required_buffer_size; } switch ((ecma_string_container_t)string_desc_p->container) { case ECMA_STRING_CONTAINER_HEAP_CHUNKS: { const ecma_collection_header_t *chars_collection_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, string_desc_p->u.collection_cp); ecma_copy_chars_collection_to_buffer (chars_collection_p, buffer_p, (size_t) buffer_size); break; } case ECMA_STRING_CONTAINER_LIT_TABLE: { const literal lit = deserialize_literal_by_id (string_desc_p->u.lit_index); JERRY_ASSERT (lit.type == LIT_STR); const ecma_char_t *str_p = literal_to_zt (lit); JERRY_ASSERT (str_p != NULL); ecma_copy_zt_string_to_buffer (str_p, buffer_p, required_buffer_size); break; } case ECMA_STRING_CONTAINER_UINT32_IN_DESC: { uint32_t uint32_number = string_desc_p->u.uint32_number; ssize_t bytes_copied = ecma_uint32_to_string (uint32_number, buffer_p, required_buffer_size); JERRY_ASSERT (bytes_copied == required_buffer_size); break; } case ECMA_STRING_CONTAINER_HEAP_NUMBER: { ecma_number_t *num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, string_desc_p->u.number_cp); ecma_length_t length = ecma_number_to_zt_string (*num_p, buffer_p, buffer_size); JERRY_ASSERT (required_buffer_size == (length + 1) * ((ssize_t) sizeof (ecma_char_t))); break; } case ECMA_STRING_CONTAINER_CONCATENATION: { const ecma_string_t *string1_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_desc_p->u.concatenation.string1_cp); const ecma_string_t *string2_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_desc_p->u.concatenation.string2_cp); ecma_char_t *dest_p = buffer_p; ssize_t bytes_copied1, bytes_copied2; bytes_copied1 = ecma_string_to_zt_string (string1_p, dest_p, buffer_size); JERRY_ASSERT (bytes_copied1 > 0); /* one character, which is the null character at end of string, will be overwritten */ bytes_copied1 -= (ssize_t) sizeof (ecma_char_t); dest_p += ecma_string_get_length (string1_p); bytes_copied2 = ecma_string_to_zt_string (string2_p, dest_p, buffer_size - bytes_copied1); JERRY_ASSERT (bytes_copied2 > 0); JERRY_ASSERT (required_buffer_size == bytes_copied1 + bytes_copied2); break; } case ECMA_STRING_CONTAINER_MAGIC_STRING: { const ecma_magic_string_id_t id = string_desc_p->u.magic_string_id; const size_t length = ecma_magic_string_lengths [id]; size_t bytes_to_copy = (length + 1) * sizeof (ecma_char_t); memcpy (buffer_p, ecma_get_magic_string_zt (id), bytes_to_copy); JERRY_ASSERT (required_buffer_size == (ssize_t) bytes_to_copy); break; } } return required_buffer_size; } /* ecma_string_to_zt_string */ /** * Long path part of ecma-string to ecma-string comparison routine * * See also: * ecma_compare_ecma_strings * * @return true - if strings are equal; * false - otherwise. */ static bool __attr_noinline___ ecma_compare_ecma_strings_longpath (const ecma_string_t *string1_p, /* ecma-string */ const ecma_string_t *string2_p) /* ecma-string */ { if (string1_p->container == string2_p->container) { if (string1_p->container == ECMA_STRING_CONTAINER_LIT_TABLE) { JERRY_ASSERT (string1_p->u.lit_index != string2_p->u.lit_index); return false; } else if (string1_p->container == ECMA_STRING_CONTAINER_MAGIC_STRING) { JERRY_ASSERT (string1_p->u.magic_string_id != string2_p->u.magic_string_id); return false; } else if (string1_p->container == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { JERRY_ASSERT (string1_p->u.uint32_number != string2_p->u.uint32_number); return false; } } const int32_t string1_len = ecma_string_get_length (string1_p); const int32_t string2_len = ecma_string_get_length (string2_p); if (string1_len != string2_len) { return false; } const int32_t strings_len = string1_len; if (strings_len == 0) { return true; } if (string1_p->container == string2_p->container) { switch ((ecma_string_container_t) string1_p->container) { case ECMA_STRING_CONTAINER_HEAP_NUMBER: { ecma_number_t *num1_p, *num2_p; num1_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, string1_p->u.number_cp); num2_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, string2_p->u.number_cp); if (ecma_number_is_nan (*num1_p) && ecma_number_is_nan (*num2_p)) { return true; } return (*num1_p == *num2_p); } case ECMA_STRING_CONTAINER_HEAP_CHUNKS: { const ecma_collection_header_t *chars_collection1_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, string1_p->u.collection_cp); const ecma_collection_header_t *chars_collection2_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, string2_p->u.collection_cp); return ecma_compare_chars_collection (chars_collection1_p, chars_collection2_p); } case ECMA_STRING_CONTAINER_CONCATENATION: { /* long path */ break; } case ECMA_STRING_CONTAINER_LIT_TABLE: { JERRY_ASSERT (string1_p->u.lit_index != string2_p->u.lit_index); return false; } case ECMA_STRING_CONTAINER_MAGIC_STRING: { JERRY_ASSERT (string1_p->u.magic_string_id != string2_p->u.magic_string_id); return false; } case ECMA_STRING_CONTAINER_UINT32_IN_DESC: { JERRY_ASSERT (string1_p->u.uint32_number != string2_p->u.uint32_number); return false; } } } JERRY_ASSERT (strings_len != 0); const size_t string_buf_size = (size_t) (strings_len + 1) * sizeof (ecma_char_t); ecma_char_t *string1_buf = (ecma_char_t*) mem_heap_alloc_block (string_buf_size, MEM_HEAP_ALLOC_SHORT_TERM); ecma_char_t *string2_buf = (ecma_char_t*) mem_heap_alloc_block (string_buf_size, MEM_HEAP_ALLOC_SHORT_TERM); if (string1_buf == NULL || string2_buf == NULL) { jerry_fatal (ERR_OUT_OF_MEMORY); } ssize_t req_size; req_size = ecma_string_to_zt_string (string1_p, string1_buf, (ssize_t) string_buf_size); JERRY_ASSERT (req_size > 0); req_size = ecma_string_to_zt_string (string2_p, string2_buf, (ssize_t) string_buf_size); JERRY_ASSERT (req_size > 0); bool is_equal = (memcmp (string1_buf, string2_buf, string_buf_size) == 0); mem_heap_free_block (string1_buf); mem_heap_free_block (string2_buf); return is_equal; } /* ecma_compare_ecma_strings_longpath */ /** * Compare ecma-string to ecma-string if they're hashes are equal * * @return true - if strings are equal; * false - may be. */ bool ecma_compare_ecma_strings_equal_hashes (const ecma_string_t *string1_p, /* ecma-string */ const ecma_string_t *string2_p) /* ecma-string */ { JERRY_ASSERT (string1_p->hash == string2_p->hash); if (string1_p->container == string2_p->container && string1_p->u.common_field == string2_p->u.common_field) { return true; } else { return false; } } /* ecma_compare_ecma_strings_equal_hashes */ /** * Compare ecma-string to ecma-string * * @return true - if strings are equal; * false - otherwise. */ bool ecma_compare_ecma_strings (const ecma_string_t *string1_p, /* ecma-string */ const ecma_string_t *string2_p) /* ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); const bool is_equal_hashes = (string1_p->hash == string2_p->hash); if (!is_equal_hashes) { return false; } const bool is_equal_containers = (string1_p->container == string2_p->container); const bool is_equal_fields = (string1_p->u.common_field == string2_p->u.common_field); if (is_equal_containers && is_equal_fields) { return true; } else { return ecma_compare_ecma_strings_longpath (string1_p, string2_p); } } /* ecma_compare_ecma_strings */ /** * Relational compare of ecma-strings. * * First string is less than second string if: * - strings are not equal; * - first string is prefix of second or is lexicographically less than second. * * @return true - if first string is less than second string, * false - otherwise. */ bool ecma_compare_ecma_strings_relational (const ecma_string_t *string1_p, /**< ecma-string */ const ecma_string_t *string2_p) /**< ecma-string */ { if (ecma_compare_ecma_strings (string1_p, string2_p)) { return false; } const ecma_char_t *zt_string1_p, *zt_string2_p; bool is_zt_string1_on_heap = false, is_zt_string2_on_heap = false; ecma_char_t zt_string1_buffer [ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER + 1]; ecma_char_t zt_string2_buffer [ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER + 1]; if (string1_p->container == ECMA_STRING_CONTAINER_LIT_TABLE) { const literal lit = deserialize_literal_by_id (string1_p->u.lit_index); JERRY_ASSERT (lit.type == LIT_STR); zt_string1_p = literal_to_zt (lit); } else { ssize_t req_size = ecma_string_to_zt_string (string1_p, zt_string1_buffer, sizeof (zt_string1_buffer)); if (req_size < 0) { ecma_char_t *heap_buffer_p = (ecma_char_t*) mem_heap_alloc_block ((size_t) -req_size, MEM_HEAP_ALLOC_SHORT_TERM); if (heap_buffer_p == NULL) { jerry_fatal (ERR_OUT_OF_MEMORY); } ssize_t bytes_copied = ecma_string_to_zt_string (string1_p, heap_buffer_p, -req_size); JERRY_ASSERT (bytes_copied > 0); zt_string1_p = heap_buffer_p; is_zt_string1_on_heap = true; } else { zt_string1_p = zt_string1_buffer; } } if (string2_p->container == ECMA_STRING_CONTAINER_LIT_TABLE) { const literal lit = deserialize_literal_by_id (string2_p->u.lit_index); JERRY_ASSERT (lit.type == LIT_STR); zt_string2_p = literal_to_zt (lit); } else { ssize_t req_size = ecma_string_to_zt_string (string2_p, zt_string2_buffer, sizeof (zt_string2_buffer)); if (req_size < 0) { ecma_char_t *heap_buffer_p = (ecma_char_t*) mem_heap_alloc_block ((size_t) -req_size, MEM_HEAP_ALLOC_SHORT_TERM); if (heap_buffer_p == NULL) { jerry_fatal (ERR_OUT_OF_MEMORY); } ssize_t bytes_copied = ecma_string_to_zt_string (string2_p, heap_buffer_p, -req_size); JERRY_ASSERT (bytes_copied > 0); zt_string2_p = heap_buffer_p; is_zt_string2_on_heap = true; } else { zt_string2_p = zt_string2_buffer; } } bool is_first_less_than_second = ecma_compare_zt_strings_relational (zt_string1_p, zt_string2_p); if (is_zt_string1_on_heap) { mem_heap_free_block ((void*) zt_string1_p); } if (is_zt_string2_on_heap) { mem_heap_free_block ((void*) zt_string2_p); } return is_first_less_than_second; } /* ecma_compare_ecma_strings_relational */ /** * Get length of ecma-string * * @return number of characters in the string */ int32_t ecma_string_get_length (const ecma_string_t *string_p) /**< ecma-string */ { ecma_string_container_t container = (ecma_string_container_t) string_p->container; if (container == ECMA_STRING_CONTAINER_LIT_TABLE) { const literal lit = deserialize_literal_by_id (string_p->u.lit_index); return lit.data.lp.length; } else if (container == ECMA_STRING_CONTAINER_MAGIC_STRING) { return ecma_magic_string_lengths [string_p->u.magic_string_id]; } else if (container == ECMA_STRING_CONTAINER_UINT32_IN_DESC) { const uint32_t uint32_number = string_p->u.uint32_number; const int32_t max_uint32_len = 10; const uint32_t nums_with_ascending_length[10] = { 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u }; int32_t length = 1; while (length < max_uint32_len && uint32_number >= nums_with_ascending_length [length]) { length++; } return length; } else if (container == ECMA_STRING_CONTAINER_HEAP_NUMBER) { const ecma_number_t *num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, string_p->u.number_cp); ecma_char_t buffer[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER + 1]; return ecma_number_to_zt_string (*num_p, buffer, sizeof (buffer)); } else if (container == ECMA_STRING_CONTAINER_HEAP_CHUNKS) { const ecma_collection_header_t *collection_header_p = ECMA_GET_NON_NULL_POINTER (ecma_collection_header_t, string_p->u.collection_cp); return collection_header_p->unit_number; } else { JERRY_ASSERT (container == ECMA_STRING_CONTAINER_CONCATENATION); const ecma_string_t *string1_p, *string2_p; string1_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_p->u.concatenation.string1_cp); string2_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, string_p->u.concatenation.string2_cp); return ecma_string_get_length (string1_p) + ecma_string_get_length (string2_p); } } /* ecma_string_get_length */ /** * Get character from specified position in the ecma-string. * * @return character value */ ecma_char_t ecma_string_get_char_at_pos (const ecma_string_t *string_p, /**< ecma-string */ uint32_t index) /**< index of character */ { uint32_t length = (uint32_t) ecma_string_get_length (string_p); JERRY_ASSERT (index < (uint32_t) length); size_t buffer_size = sizeof (ecma_char_t) * (length + 1); ecma_char_t *zt_str_p = (ecma_char_t*) mem_heap_alloc_block (buffer_size, MEM_HEAP_ALLOC_SHORT_TERM); if (zt_str_p == NULL) { jerry_fatal (ERR_OUT_OF_MEMORY); } ecma_string_to_zt_string (string_p, zt_str_p, (ssize_t) buffer_size); ecma_char_t ch = zt_str_p [index]; mem_heap_free_block (zt_str_p); return ch; } /* ecma_string_get_char_at_pos */ /** * Compare zero-terminated string to zero-terminated string * * @return true - if strings are equal; * false - otherwise. */ bool ecma_compare_zt_strings (const ecma_char_t *string1_p, /**< zero-terminated string */ const ecma_char_t *string2_p) /**< zero-terminated string */ { const ecma_char_t *iter_1_p = string1_p; const ecma_char_t *iter_2_p = string2_p; while (*iter_1_p != ECMA_CHAR_NULL) { if (*iter_1_p++ != *iter_2_p++) { return false; } } return (*iter_2_p == ECMA_CHAR_NULL); } /* ecma_compare_zt_strings */ /** * Relational compare of zero-terminated strings * * First string is less than second string if: * - strings are not equal; * - first string is prefix of second or is lexicographically less than second. * * @return true - if first string is less than second string, * false - otherwise. */ bool ecma_compare_zt_strings_relational (const ecma_char_t *string1_p, /**< zero-terminated string */ const ecma_char_t *string2_p) /**< zero-terminated string */ { const ecma_char_t *iter_1_p = string1_p; const ecma_char_t *iter_2_p = string2_p; while (*iter_1_p != ECMA_CHAR_NULL && *iter_2_p != ECMA_CHAR_NULL) { #if CONFIG_ECMA_CHAR_ENCODING == CONFIG_ECMA_CHAR_ASCII const ecma_char_t chr_1 = *iter_1_p++; const ecma_char_t chr_2 = *iter_2_p++; if (chr_1 < chr_2) { return true; } else if (chr_1 > chr_2) { return false; } #elif CONFIG_ECMA_CHAR_ENCODING == CONFIG_ECMA_CHAR_UTF16 const ecma_char_t first_in_pair_range_begin = 0xD800; const ecma_char_t first_in_pair_range_end = 0xDBFF; const ecma_char_t second_in_pair_range_begin = 0xDC00; const ecma_char_t second_in_pair_range_end = 0xDFFF; const bool iter_1_at_first_in_pair = (*iter_1_p >= first_in_pair_range_begin && *iter_1_p <= first_in_pair_range_end); const bool iter_2_at_first_in_pair = (*iter_2_p >= first_in_pair_range_begin && *iter_2_p <= first_in_pair_range_end); const bool iter_1_at_second_in_pair = (*iter_1_p >= second_in_pair_range_begin && *iter_1_p <= second_in_pair_range_end); const bool iter_2_at_second_in_pair = (*iter_2_p >= second_in_pair_range_begin && *iter_2_p <= second_in_pair_range_end); JERRY_ASSERT (!iter_1_at_second_in_pair && !iter_2_at_second_in_pair); /* Pairs encode range U+010000 to U+10FFFF, while single chars encode U+0000 to U+7DFF and U+E000 to U+FFFF */ if (iter_1_at_first_in_pair && !iter_2_at_first_in_pair) { return false; } else if (!iter_1_at_first_in_pair && iter_2_at_first_in_pair) { return true; } else if (!iter_1_at_first_in_pair && !iter_2_at_first_in_pair) { const ecma_char_t chr_1 = *iter_1_p; const ecma_char_t chr_2 = *iter_2_p; if (chr_1 < chr_2) { return true; } else { return false; } iter_1_p++; iter_2_p++; } else { JERRY_ASSERT (iter_1_at_first_in_pair && iter_2_at_first_in_pair); uint32_t chr1, chr2; chr1 = *iter_1_p++ - first_in_pair_range_begin; chr1 <<= 10; JERRY_ASSERT (*iter_1_p >= second_in_pair_range_begin && *iter_1_p <= second_in_pair_range_end); chr1 += *iter_1_p++ - second_in_pair_range_begin; chr2 = *iter_2_p++ - first_in_pair_range_begin; chr2 <<= 10; JERRY_ASSERT (*iter_2_p >= second_in_pair_range_begin && *iter_2_p <= second_in_pair_range_end); chr2 += *iter_2_p++ - second_in_pair_range_begin; if (chr1 < chr2) { return true; } else if (chr1 > chr2) { return false; } } #endif /* CONFIG_ECMA_CHAR_ENCODING == CONFIG_ECMA_CHAR_UTF16 */ } return (*iter_1_p == ECMA_CHAR_NULL && *iter_2_p != ECMA_CHAR_NULL); } /* ecma_compare_zt_strings_relational */ /** * Copy zero-terminated string to buffer * * Warning: * the routine requires that buffer size is enough * * @return pointer to null character of copied string in destination buffer */ ecma_char_t* ecma_copy_zt_string_to_buffer (const ecma_char_t *string_p, /**< zero-terminated string */ ecma_char_t *buffer_p, /**< destination buffer */ ssize_t buffer_size) /**< size of buffer */ { const ecma_char_t *str_iter_p = string_p; ecma_char_t *buf_iter_p = buffer_p; ssize_t bytes_copied = 0; while (*str_iter_p != ECMA_CHAR_NULL) { bytes_copied += (ssize_t) sizeof (ecma_char_t); JERRY_ASSERT (bytes_copied <= buffer_size); *buf_iter_p++ = *str_iter_p++; } bytes_copied += (ssize_t) sizeof (ecma_char_t); JERRY_ASSERT (bytes_copied <= buffer_size); *buf_iter_p = ECMA_CHAR_NULL; return buf_iter_p; } /* ecma_copy_zt_string_to_buffer */ /** * Calculate zero-terminated string's length * * @return length of string */ ecma_length_t ecma_zt_string_length (const ecma_char_t *string_p) /**< zero-terminated string */ { const ecma_char_t *str_iter_p = string_p; ecma_length_t length = 0; while (*str_iter_p++) { length++; /* checking overflow */ JERRY_ASSERT (length != 0); } return length; } /* ecma_zt_string_length */ /** * Get specified magic string as zero-terminated string * * @return pointer to zero-terminated magic string */ const ecma_char_t* ecma_get_magic_string_zt (ecma_magic_string_id_t id) /**< magic string id */ { TODO(Support UTF-16); switch (id) { #define ECMA_MAGIC_STRING_DEF(id, ascii_zt_string) \ case id: return (ecma_char_t*) ascii_zt_string; #include "ecma-magic-strings.inc.h" #undef ECMA_MAGIC_STRING_DEF case ECMA_MAGIC_STRING__COUNT: break; } JERRY_UNREACHABLE(); } /* ecma_get_magic_string_zt */ /** * Get specified magic string * * @return ecma-string containing specified magic string */ ecma_string_t* ecma_get_magic_string (ecma_magic_string_id_t id) /**< magic string id */ { return ecma_new_ecma_string_from_magic_string_id (id); } /* ecma_get_magic_string */ /** * Check if passed zt-string equals to one of magic strings * and if equal magic string was found, return it's id in 'out_id_p' argument. * * @return true - if magic string equal to passed string was found, * false - otherwise. */ bool ecma_is_zt_string_magic (const ecma_char_t *zt_string_p, /**< zero-terminated string */ ecma_magic_string_id_t *out_id_p) /**< out: magic string's id */ { TODO (Improve performance of search); for (ecma_magic_string_id_t id = (ecma_magic_string_id_t) 0; id < ECMA_MAGIC_STRING__COUNT; id = (ecma_magic_string_id_t) (id + 1)) { if (ecma_compare_zt_strings (zt_string_p, ecma_get_magic_string_zt (id))) { *out_id_p = id; return true; } } *out_id_p = ECMA_MAGIC_STRING__COUNT; return false; } /* ecma_is_zt_string_magic */ /** * Long path part of ecma_is_string_magic * * Converts passed ecma-string to zt-string and * checks if it is equal to one of magic string * * @return true - if magic string equal to passed string was found, * false - otherwise. */ static bool ecma_is_string_magic_longpath (const ecma_string_t *string_p, /**< ecma-string */ ecma_magic_string_id_t *out_id_p) /**< out: magic string's id */ { ecma_char_t zt_string_buffer [ECMA_STRING_MAGIC_STRING_LENGTH_LIMIT + 1]; ssize_t copied = ecma_string_to_zt_string (string_p, zt_string_buffer, (ssize_t) sizeof (zt_string_buffer)); JERRY_ASSERT (copied > 0); return ecma_is_zt_string_magic (zt_string_buffer, out_id_p); } /* ecma_is_string_magic_longpath */ /** * Check if passed string equals to one of magic strings * and if equal magic string was found, return it's id in 'out_id_p' argument. * * @return true - if magic string equal to passed string was found, * false - otherwise. */ bool ecma_is_string_magic (const ecma_string_t *string_p, /**< ecma-string */ ecma_magic_string_id_t *out_id_p) /**< out: magic string's id */ { if (string_p->container == ECMA_STRING_CONTAINER_MAGIC_STRING) { JERRY_ASSERT (string_p->u.magic_string_id < ECMA_MAGIC_STRING__COUNT); *out_id_p = (ecma_magic_string_id_t) string_p->u.magic_string_id; return true; } else if (string_p->container == ECMA_STRING_CONTAINER_CONCATENATION && ecma_string_get_length (string_p) <= ECMA_STRING_MAGIC_STRING_LENGTH_LIMIT) { return ecma_is_string_magic_longpath (string_p, out_id_p); } else { /* * Any ecma-string constructor except ecma_concat_ecma_strings * should return ecma-string with ECMA_STRING_CONTAINER_MAGIC_STRING * container type if new ecma-string's content is equal to one of magic strings. */ JERRY_ASSERT (ecma_string_get_length (string_p) > ECMA_STRING_MAGIC_STRING_LENGTH_LIMIT || !ecma_is_string_magic_longpath (string_p, out_id_p)); return false; } } /* ecma_is_string_magic */ /** * Try to calculate hash of the ecma-string * * @return calculated hash */ ecma_string_hash_t ecma_string_hash (const ecma_string_t *string_p) /**< ecma-string to calculate hash for */ { return (string_p->hash); } /* ecma_string_try_hash */ /** * Calculate hash from last ECMA_STRING_HASH_LAST_CHARS_COUNT characters from the buffer. * * @return ecma-string's hash */ ecma_string_hash_t ecma_chars_buffer_calc_hash_last_chars (const ecma_char_t *chars, /**< characters buffer */ ecma_length_t length) /**< number of characters in the buffer */ { JERRY_ASSERT (chars != NULL); ecma_char_t char1 = (length > 0) ? chars[length - 1] : ECMA_CHAR_NULL; ecma_char_t char2 = (length > 1) ? chars[length - 2] : ECMA_CHAR_NULL; uint32_t t1 = (uint32_t) char1 + (uint32_t) char2; uint32_t t2 = t1 * 0x24418b66; uint32_t t3 = (t2 >> 16) ^ (t2 & 0xffffu); uint32_t t4 = (t3 >> 8) ^ (t3 & 0xffu); return (ecma_string_hash_t) t4; } /* ecma_chars_buffer_calc_hash_last_chars */ /** * @} * @} */