/* Copyright 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-literal-storage.h" #include "ecma-helpers.h" #include "jcontext.h" /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmalitstorage Literal storage * @{ */ /** * Free string list */ static void ecma_free_string_list (ecma_lit_storage_item_t *string_list_p) /**< string list */ { while (string_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (string_list_p->values[i] != JMEM_CP_NULL) { ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); JERRY_ASSERT (ECMA_STRING_IS_REF_EQUALS_TO_ONE (string_p)); ecma_deref_ecma_string (string_p); } } ecma_lit_storage_item_t *prev_item = string_list_p; string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp); jmem_pools_free (prev_item, sizeof (ecma_lit_storage_item_t)); } } /* ecma_free_string_list */ /** * Finalize literal storage */ void ecma_finalize_lit_storage (void) { ecma_free_string_list (JERRY_CONTEXT (string_list_first_p)); ecma_free_string_list (JERRY_CONTEXT (number_list_first_p)); } /* ecma_finalize_lit_storage */ /** * Find or create a literal string. * * @return ecma_string_t compressed pointer */ jmem_cpointer_t ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */ lit_utf8_size_t size) /**< size of the string */ { ecma_string_t *string_p = ecma_new_ecma_string_from_utf8 (chars_p, size); ecma_lit_storage_item_t *string_list_p = JERRY_CONTEXT (string_list_first_p); jmem_cpointer_t *empty_cpointer_p = NULL; while (string_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (string_list_p->values[i] == JMEM_CP_NULL) { if (empty_cpointer_p == NULL) { empty_cpointer_p = string_list_p->values + i; } } else { ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); if (ecma_compare_ecma_strings (string_p, value_p)) { /* Return with string if found in the list. */ ecma_deref_ecma_string (string_p); return string_list_p->values[i]; } } } string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp); } jmem_cpointer_t result; JMEM_CP_SET_NON_NULL_POINTER (result, string_p); if (empty_cpointer_p != NULL) { *empty_cpointer_p = result; return result; } ecma_lit_storage_item_t *new_item_p; new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); new_item_p->values[0] = result; for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { new_item_p->values[i] = JMEM_CP_NULL; } JMEM_CP_SET_POINTER (new_item_p->next_cp, JERRY_CONTEXT (string_list_first_p)); JERRY_CONTEXT (string_list_first_p) = new_item_p; return result; } /* ecma_find_or_create_literal_string */ /** * Find or create a literal number. * * @return ecma_string_t compressed pointer */ jmem_cpointer_t ecma_find_or_create_literal_number (ecma_number_t number_arg) /**< number to be searched */ { ecma_value_t num = ecma_make_number_value (number_arg); ecma_lit_storage_item_t *number_list_p = JERRY_CONTEXT (number_list_first_p); jmem_cpointer_t *empty_cpointer_p = NULL; while (number_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (number_list_p->values[i] == JMEM_CP_NULL) { if (empty_cpointer_p == NULL) { empty_cpointer_p = number_list_p->values + i; } } else { ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, number_list_p->values[i]); JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (value_p) == ECMA_STRING_LITERAL_NUMBER); if (ecma_is_value_integer_number (num)) { if (value_p->u.lit_number == num) { return number_list_p->values[i]; } } else { if (ecma_is_value_float_number (value_p->u.lit_number) && ecma_get_float_from_value (value_p->u.lit_number) == ecma_get_float_from_value (num)) { ecma_free_value (num); return number_list_p->values[i]; } } } } number_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, number_list_p->next_cp); } ecma_string_t *string_p = (ecma_string_t *) jmem_pools_alloc (sizeof (ecma_string_t)); string_p->refs_and_container = ECMA_STRING_REF_ONE | ECMA_STRING_LITERAL_NUMBER; string_p->u.lit_number = num; jmem_cpointer_t result; JMEM_CP_SET_NON_NULL_POINTER (result, string_p); if (empty_cpointer_p != NULL) { *empty_cpointer_p = result; return result; } ecma_lit_storage_item_t *new_item_p; new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t)); new_item_p->values[0] = result; for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { new_item_p->values[i] = JMEM_CP_NULL; } JMEM_CP_SET_POINTER (new_item_p->next_cp, JERRY_CONTEXT (number_list_first_p)); JERRY_CONTEXT (number_list_first_p) = new_item_p; return result; } /* ecma_find_or_create_literal_number */ /** * Log2 of snapshot literal alignment. */ #define JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG 2 /** * Snapshot literal alignment. */ #define JERRY_SNAPSHOT_LITERAL_ALIGNMENT (1u << JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG) #ifdef JERRY_ENABLE_SNAPSHOT_SAVE /** * Save literals to specified snapshot buffer. * * @return true, if save was performed successfully (i.e. buffer size is sufficient), * false - otherwise. */ bool ecma_save_literals_for_snapshot (uint8_t *buffer_p, /**< [out] output snapshot buffer */ size_t buffer_size, /**< size of the buffer */ size_t *in_out_buffer_offset_p, /**< [in,out] write position in the buffer */ lit_mem_to_snapshot_id_map_entry_t **out_map_p, /**< [out] map from literal identifiers * to the literal offsets * in snapshot */ uint32_t *out_map_len_p, /**< [out] number of literals */ uint32_t *out_lit_table_size_p) /**< [out] number of bytes, saved to snapshot buffer */ { /* Count literals and literal space. */ uint32_t string_count = 0; uint32_t number_count = 0; uint32_t lit_table_size = 2 * sizeof (uint32_t); ecma_lit_storage_item_t *string_list_p = JERRY_CONTEXT (string_list_first_p); while (string_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (string_list_p->values[i] != JMEM_CP_NULL) { ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); lit_table_size += (uint32_t) JERRY_ALIGNUP (sizeof (uint16_t) + ecma_string_get_size (string_p), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); string_count++; } } string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp); } ecma_lit_storage_item_t *number_list_p = JERRY_CONTEXT (number_list_first_p); while (number_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (number_list_p->values[i] != JMEM_CP_NULL) { lit_table_size += (uint32_t) sizeof (ecma_number_t); number_count++; } } number_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, number_list_p->next_cp); } /* Check whether enough space is available. */ if (*in_out_buffer_offset_p + lit_table_size > buffer_size) { return false; } /* Check whether the maximum literal table size is reached. */ if ((lit_table_size >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG) > UINT16_MAX) { return false; } uint32_t total_count = string_count + number_count; lit_mem_to_snapshot_id_map_entry_t *map_p; map_p = jmem_heap_alloc_block (total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); /* Set return values (no error is possible from here). */ buffer_p += *in_out_buffer_offset_p; *in_out_buffer_offset_p += lit_table_size; *out_map_p = map_p; *out_map_len_p = total_count; *out_lit_table_size_p = lit_table_size; /* Write data into the buffer. */ /* The zero value is reserved for NULL (no literal) * constant so the first literal must have offset one. */ uint32_t literal_offset = JERRY_SNAPSHOT_LITERAL_ALIGNMENT; ((uint32_t *) buffer_p)[0] = string_count; ((uint32_t *) buffer_p)[1] = number_count; buffer_p += 2 * sizeof (uint32_t); string_list_p = JERRY_CONTEXT (string_list_first_p); while (string_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (string_list_p->values[i] != JMEM_CP_NULL) { map_p->literal_id = string_list_p->values[i]; map_p->literal_offset = (jmem_cpointer_t) (literal_offset >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); map_p++; ecma_string_t *string_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]); ecma_length_t length = ecma_string_get_size (string_p); *((uint16_t *) buffer_p) = (uint16_t) length; ecma_string_to_utf8_bytes (string_p, buffer_p + sizeof (uint16_t), length); length = JERRY_ALIGNUP (sizeof (uint16_t) + length, JERRY_SNAPSHOT_LITERAL_ALIGNMENT); buffer_p += length; literal_offset += length; } } string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp); } number_list_p = JERRY_CONTEXT (number_list_first_p); while (number_list_p != NULL) { for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++) { if (number_list_p->values[i] != JMEM_CP_NULL) { map_p->literal_id = number_list_p->values[i]; map_p->literal_offset = (jmem_cpointer_t) (literal_offset >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); map_p++; ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, number_list_p->values[i]); JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (value_p) == ECMA_STRING_LITERAL_NUMBER); ecma_number_t num = ecma_get_number_from_value (value_p->u.lit_number); memcpy (buffer_p, &num, sizeof (ecma_number_t)); ecma_length_t length = JERRY_ALIGNUP (sizeof (ecma_number_t), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); buffer_p += length; literal_offset += length; } } number_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, number_list_p->next_cp); } return true; } /* ecma_save_literals_for_snapshot */ #endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ #ifdef JERRY_ENABLE_SNAPSHOT_EXEC /** * Helper function for ecma_load_literals_from_snapshot. * * Note: always inline because it is used only once. * * @return true, if load was performed successfully * false - otherwise (i.e. buffer length is incorrect). */ static inline bool __attr_always_inline___ ecma_load_literals_from_buffer (const uint8_t *buffer_p, /**< buffer with literal table in snapshot */ uint32_t lit_table_size, /**< size of literal table in snapshot */ lit_mem_to_snapshot_id_map_entry_t *map_p, /**< literal map */ uint32_t string_count, /**< number of strings */ uint32_t number_count) /**< number of numbers */ { /* The zero value is reserved for NULL (no literal) * constant so the first literal must have offset one. */ uint32_t literal_offset = JERRY_SNAPSHOT_LITERAL_ALIGNMENT; /* Load strings first. */ while (string_count > 0) { if (lit_table_size < literal_offset + sizeof (uint32_t)) { /* Buffer is not sufficent. */ return false; } lit_utf8_size_t length = *((uint16_t *) buffer_p); lit_utf8_size_t aligned_length = JERRY_ALIGNUP (sizeof (uint16_t) + length, JERRY_SNAPSHOT_LITERAL_ALIGNMENT); if (lit_table_size < literal_offset + aligned_length) { /* Buffer is not sufficent. */ return false; } map_p->literal_id = ecma_find_or_create_literal_string (buffer_p + sizeof (uint16_t), length); map_p->literal_offset = (jmem_cpointer_t) (literal_offset >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); map_p++; buffer_p += aligned_length; literal_offset += aligned_length; string_count--; } /* Load numbers. */ while (number_count > 0) { if (lit_table_size < literal_offset + sizeof (ecma_number_t)) { /* Buffer is not sufficent. */ return false; } ecma_number_t num; memcpy (&num, buffer_p, sizeof (ecma_number_t)); map_p->literal_id = ecma_find_or_create_literal_number (num); map_p->literal_offset = (jmem_cpointer_t) (literal_offset >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); map_p++; ecma_length_t length = JERRY_ALIGNUP (sizeof (ecma_number_t), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); buffer_p += length; literal_offset += length; number_count--; } return (lit_table_size == (literal_offset + 2 * sizeof (uint32_t) - JERRY_SNAPSHOT_LITERAL_ALIGNMENT)); } /* ecma_load_literals_from_buffer */ /** * Load literals from snapshot. * * @return true, if load was performed successfully (i.e. literals saved in the snapshot are consistent), * false - otherwise (i.e. snapshot is incorrect). */ bool ecma_load_literals_from_snapshot (const uint8_t *buffer_p, /**< buffer with literal table in snapshot */ uint32_t lit_table_size, /**< size of literal table in snapshot */ lit_mem_to_snapshot_id_map_entry_t **out_map_p, /**< [out] map from literal offsets * in snapshot to identifiers * of loaded literals in literal * storage */ uint32_t *out_map_len_p) /**< [out] literals number */ { *out_map_p = NULL; if (lit_table_size < 2 * sizeof (uint32_t)) { /* Buffer is not sufficent. */ return false; } uint32_t string_count = ((uint32_t *) buffer_p)[0]; uint32_t number_count = ((uint32_t *) buffer_p)[1]; buffer_p += 2 * sizeof (uint32_t); uint32_t total_count = string_count + number_count; lit_mem_to_snapshot_id_map_entry_t *map_p; *out_map_len_p = total_count; if (total_count == 0) { return true; } map_p = jmem_heap_alloc_block (total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); *out_map_p = map_p; if (ecma_load_literals_from_buffer (buffer_p, lit_table_size, map_p, string_count, number_count)) { return true; } jmem_heap_free_block (map_p, total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); *out_map_p = NULL; return false; } /* ecma_load_literals_from_snapshot */ #endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ /** * @} * @} */