diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index 20de934fb..b20965dda 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -93,6 +93,7 @@ project (JerryCore CXX C ASM) # Include directories set(INCLUDE_CORE ${CMAKE_SOURCE_DIR}/jerry-core + ${CMAKE_SOURCE_DIR}/jerry-core/lit ${CMAKE_SOURCE_DIR}/jerry-core/rcs ${CMAKE_SOURCE_DIR}/jerry-core/mem ${CMAKE_SOURCE_DIR}/jerry-core/vm @@ -110,6 +111,7 @@ project (JerryCore CXX C ASM) # Sources # Jerry core file(GLOB SOURCE_CORE_API *.cpp) + file(GLOB SOURCE_CORE_LIT lit/*.cpp) file(GLOB SOURCE_CORE_RCS rcs/*.cpp) file(GLOB SOURCE_CORE_MEM mem/*.cpp) file(GLOB SOURCE_CORE_VM vm/*.cpp) @@ -123,6 +125,7 @@ project (JerryCore CXX C ASM) set(SOURCE_CORE jerry.cpp ${SOURCE_CORE_API} + ${SOURCE_CORE_LIT} ${SOURCE_CORE_RCS} ${SOURCE_CORE_MEM} ${SOURCE_CORE_VM} diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 2823f1f6d..f88e21fa8 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -26,6 +26,7 @@ #include "config.h" #include "jrt.h" #include "mem-allocator.h" +#include "rcs-recordset.h" /** \addtogroup compressedpointer Compressed pointer * @{ @@ -757,11 +758,20 @@ typedef enum } ecma_string_container_t; FIXME (Move to library that should define the type (literal.h /* ? */)) +/** + * Literal and compressed pointer to literal + */ +typedef rcs_record_t *literal_t; +typedef rcs_cpointer_t lit_cpointer_t; + /** * Index in literal table + * + * FIXME: Remove after switching to literal storage */ typedef uint32_t literal_index_t; + /** * Identifiers of ECMA and implementation-defined magic string constants */ diff --git a/jerry-core/jrt/jrt.h b/jerry-core/jrt/jrt.h index 5cc7a8e20..45fa13aff 100644 --- a/jerry-core/jrt/jrt.h +++ b/jerry-core/jrt/jrt.h @@ -214,4 +214,16 @@ extern void __noreturn jerry_fatal (jerry_fatal_code_t code); #define JERRY_MIN(v1, v2) ((v1 < v2) ? v1 : v2) #define JERRY_MAX(v1, v2) ((v1 < v2) ? v2 : v1) +/** + * Placement new operator (constructs an object on a pre-allocated buffer) + * + * Our version of the libc library doesn't support calling the constructors and destructors of the static variables. + * It is proposed to use placement new operator. Generally it is available via #include , + * To fix the unavailability of the header in some configurations placement new operator is implemented here. + */ +inline void* operator new (size_t, void* where) +{ + return where; +} /* operator new */ + #endif /* !JERRY_GLOBALS_H */ diff --git a/jerry-core/lit/lit-literal-storage.cpp b/jerry-core/lit/lit-literal-storage.cpp new file mode 100644 index 000000000..1165a0d44 --- /dev/null +++ b/jerry-core/lit/lit-literal-storage.cpp @@ -0,0 +1,460 @@ +/* Copyright 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. + */ + +#include "lit-literal-storage.h" +#include "ecma-helpers.h" + +/** + * Literal storage + * As our libc library doesn't call constructors of static variables, lit_storage is initialized + * in lit_init function by placement new operator. + */ +lit_literal_storage_t lit_storage; + +/** + * Get pointer to the previous record inside the literal storage + * + * @return pointer to the previous record of NULL if this is the first record ('prev' field in the header) + */ +rcs_record_t * +lit_charset_record_t::get_prev () const +{ + rcs_record_iterator_t it ((rcs_recordset_t *)&lit_storage, (rcs_record_t *)this); + it.skip (RCS_DYN_STORAGE_LENGTH_UNIT); + + cpointer_t cpointer; + cpointer.packed_value = it.read (); + + return cpointer_t::decompress (cpointer); +} /* lit_charset_record_t::get_prev */ + +/** + * Set the pointer to the previous record inside the literal storage (sets 'prev' field in the header) + */ +void +lit_charset_record_t::set_prev (rcs_record_t *prev_rec_p) /**< pointer to the record to set as previous */ +{ + rcs_record_iterator_t it ((rcs_recordset_t *)&lit_storage, (rcs_record_t *)this); + it.skip (RCS_DYN_STORAGE_LENGTH_UNIT); + + it.write (cpointer_t::compress (prev_rec_p).packed_value); +} /* lit_charset_record_t::set_prev */ + +/** + * Set the charset of the record + */ +void +lit_charset_record_t::set_charset (const ecma_char_t *str, /**< buffer containing characters to set */ + size_t size) /**< size of the buffer in bytes */ +{ + JERRY_ASSERT (header_size () + size == get_size () - get_alignment_bytes_count ()); + + rcs_record_iterator_t it ((rcs_recordset_t *)&lit_storage, (rcs_record_t *)this); + it.skip (header_size ()); + + for (size_t i = 0; i < get_length (); ++i) + { + it.write (str[i]); + it.skip (); + } +} /* lit_charset_record_t::set_charset */ + +/** + * Get the characters which are stored to the record + * + * @return number of code units written to the buffer + */ +ecma_length_t +lit_charset_record_t::get_charset (ecma_char_t *buff, /**< output buffer */ + size_t size) /**< size of the output buffer in bytes */ +{ + JERRY_ASSERT (buff && size >= sizeof (ecma_char_t)); + + rcs_record_iterator_t it ((rcs_recordset_t *)&lit_storage, (rcs_record_t *)this); + it.skip (header_size ()); + ecma_length_t len = get_length (); + size_t i; + + for (i = 0; i < len && size > sizeof (ecma_char_t); ++i) + { + buff[i] = it.read (); + it.skip (); + size -= sizeof (ecma_char_t); + } + + return (ecma_length_t) i; +} /* lit_charset_record_t::get_charset */ + +/** + * Compares characters from the record to the string + * + * @return 0 if strings are equal + * -1 if str2 is greater + * 1 if str2 is less + */ +int +lit_charset_record_t::compare_zt (const ecma_char_t *str_to_compare_with, /**< buffer with string to compare */ + size_t length) /**< length of the string in buffer str2 */ +{ + size_t i; + + if (get_length () == 0) + { + if (str_to_compare_with != NULL) + { + return -1; + } + else + { + return 0; + } + } + + if (str_to_compare_with == NULL) + { + return 1; + } + + rcs_record_iterator_t it_this (&lit_storage, this); + + it_this.skip (header_size ()); + + for (i = 0; i < get_length () && i < length; i++) + { + ecma_char_t chr = it_this.read (); + + if (chr > str_to_compare_with[i]) + { + return 1; + } + else if (chr < str_to_compare_with[i]) + { + return -1; + } + + it_this.skip (); + } + + if (i < length) + { + return -1; + } + + return 0; +} /* lit_charset_record_t::compare_zt */ + +/** + * Compares two lit_charset_record_t records for equality + * + * @return true if strings inside records are equal + * false otherwise + */ +bool +lit_charset_record_t::equal (lit_charset_record_t *rec) /**< charset record to compare with */ +{ + if (get_length () != rec->get_length ()) + { + return false; + } + + rcs_record_iterator_t it_this (&lit_storage, this); + rcs_record_iterator_t it_record (&lit_storage, rec); + + it_this.skip (header_size ()); + it_record.skip (rec->header_size ()); + + for (ecma_length_t i = 0; i < get_length (); i++) + { + if (it_this.read () != it_record.read ()) + { + return false; + } + + it_this.skip (); + it_record.skip (); + } + + return true; +} /* lit_charset_record_t::equal */ + +/** + * Compares this lit_charset_record_t records with zero-terminated string for equality + * + * @return true if compared instances are equal + * false otherwise + */ +bool +lit_charset_record_t::equal_zt (const ecma_char_t *str) /**< zero-terminated string */ +{ + return equal_non_zt (str, ecma_zt_string_length (str)); +} /* lit_charset_record_t::equal_zt */ + +/** + * Compare this lit_charset_record_t record with string (which could contain '\0' characters) for equality + * + * @return true if compared instances are equal + * false otherwise + */ +bool +lit_charset_record_t::equal_non_zt (const ecma_char_t *str, /**< string to compare with */ + ecma_length_t len) /**< length of the string */ +{ + rcs_record_iterator_t it_this (&lit_storage, this); + + it_this.skip (header_size ()); + + for (ecma_length_t i = 0; i < get_length () && i < len; i++) + { + if (it_this.read () != str[i]) + { + return false; + } + + it_this.skip (); + } + + return get_length () == len; +} /* lit_charset_record_t::equal_non_zt */ + +/** + * Create charset record in the literal storage + * + * @return pointer to the created record + */ +lit_charset_record_t * +lit_literal_storage_t::create_charset_record (const ecma_char_t *str, /**< string to be placed in the record */ + size_t buf_size) /**< size in bytes of the buffer which holds the + * string */ +{ + const size_t alignment = lit_charset_record_t::size (buf_size) - (lit_charset_record_t::header_size () + buf_size); + + lit_charset_record_t *ret = alloc_record (LIT_STR, buf_size); + + ret->set_alignment_bytes_count (alignment); + ret->set_charset (str, buf_size); + ret->set_hash (ecma_chars_buffer_calc_hash_last_chars (str, ret->get_length ())); + + return ret; +} /* lit_literal_storage_t::create_charset_record */ + +/** + * Create magic string record in the literal storage + * + * @return pointer to the created record + */ +lit_magic_record_t * +lit_literal_storage_t::create_magic_record (ecma_magic_string_id_t id) /**< id of a magic string */ +{ + lit_magic_record_t *ret = alloc_record (LIT_MAGIC_STR); + + ret->set_magic_str_id (id); + + return ret; +} /* lit_literal_storage_t::create_magic_record */ + +/** + * Create number record in the literal storage + * + * @return pointer to the created record + */ +lit_number_record_t * +lit_literal_storage_t::create_number_record (ecma_number_t num) /**< number */ +{ + lit_number_record_t *ret = alloc_record (LIT_NUMBER); + rcs_record_iterator_t it_this (this, ret); + + it_this.skip (ret->header_size ()); + it_this.write (num); + + return ret; +} /* lit_literal_storage_t::create_number_record */ + +/** + * Dump the contents of the literal storage + */ +void +lit_literal_storage_t::dump () +{ + printf ("LITERALS:\n"); + + for (rcs_record_t *rec_p = lit_storage.get_first (); rec_p != NULL; rec_p = lit_storage.get_next (rec_p)) + { + printf ("%p ", rec_p); + printf ("[%3zu] ", get_record_size (rec_p)); + + switch (rec_p->get_type ()) + { + case LIT_STR: + { + lit_charset_record_t *lit_p = static_cast (rec_p); + rcs_record_iterator_t it_this (this, rec_p); + + it_this.skip (lit_charset_record_t::header_size ()); + + for (size_t i = 0; i < lit_p->get_length (); ++i) + { + printf ("%c", it_this.read ()); + it_this.skip (); + } + + printf (" : STRING"); + + break; + } + case LIT_MAGIC_STR: + { + lit_magic_record_t *lit_p = static_cast (rec_p); + + printf ("%s : MAGIC STRING", ecma_get_magic_string_zt (lit_p->get_magic_str_id ())); + printf (" [id=%d] ", lit_p->get_magic_str_id ()); + + break; + } + case LIT_NUMBER: + { + lit_number_record_t *lit_p = static_cast (rec_p); + + if (ecma_number_is_nan (lit_p->get_number ())) + { + printf ("%s : NUMBER", "NaN"); + } + else + { + ecma_char_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; + ecma_number_to_zt_string (lit_p->get_number (), buff, ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); + printf ("%s : NUMBER", buff); + } + + break; + } + default: + { + printf (" : EMPTY RECORD"); + } + } + + printf ("\n"); + } +} /* lit_literal_storage_t::dump */ + +/** + * Get previous record in the literal storage + * + * @return pointer to the previous record relative to rec_p, or NULL if rec_p is the first record in the storage + */ +rcs_record_t * +lit_literal_storage_t::get_prev (rcs_record_t *rec_p) /**< record, relative to which previous should be found */ +{ + switch (rec_p->get_type ()) + { + case LIT_STR: + { + return (static_cast (rec_p))->get_prev (); + } + case LIT_MAGIC_STR: + { + return (static_cast (rec_p))->get_prev (); + } + case LIT_NUMBER: + { + return (static_cast (rec_p))->get_prev (); + } + default: + { + JERRY_ASSERT (rec_p->get_type () < _first_type_id); + + return rcs_recordset_t::get_prev (rec_p); + } + } +} /* lit_literal_storage_t::get_prev */ + +/** + * Set pointer to the previous record + */ +void +lit_literal_storage_t::set_prev (rcs_record_t *rec_p, /**< record to modify */ + rcs_record_t *prev_rec_p) /**< record which should be set as previous */ +{ + switch (rec_p->get_type ()) + { + case LIT_STR: + { + return (static_cast (rec_p))->set_prev (prev_rec_p); + } + case LIT_MAGIC_STR: + { + return (static_cast (rec_p))->set_prev (prev_rec_p); + } + case LIT_NUMBER: + { + return (static_cast (rec_p))->set_prev (prev_rec_p); + } + default: + { + JERRY_ASSERT (rec_p->get_type () < _first_type_id); + + return rcs_recordset_t::set_prev (rec_p, prev_rec_p); + } + } +} /* lit_literal_storage_t::set_prev */ + +/** + * Get size of a record + * + * @return size of a record in bytes + */ +size_t +lit_literal_storage_t::get_record_size (rcs_record_t* rec_p) /**< pointer to a record */ +{ + switch (rec_p->get_type ()) + { + case LIT_STR: + { + return (static_cast (rec_p))->get_size (); + } + case LIT_MAGIC_STR: + { + return lit_magic_record_t::size (); + } + case LIT_NUMBER: + { + return lit_number_record_t::size (); + } + default: + { + JERRY_ASSERT (rec_p->get_type () < _first_type_id); + + return rcs_recordset_t::get_record_size (rec_p); + } + } +} /* lit_literal_storage_t::get_record_size */ + +template void rcs_record_iterator_t::skip (); +template void rcs_record_iterator_t::skip (); +template void rcs_record_iterator_t::skip (); + +template void rcs_record_iterator_t::write (ecma_char_t); +template ecma_char_t rcs_record_iterator_t::read (); + +template void rcs_record_iterator_t::write (ecma_number_t); +template ecma_number_t rcs_record_iterator_t::read (); + +template void rcs_record_iterator_t::write (uint16_t); +template uint16_t rcs_record_iterator_t::read (); + +template lit_charset_record_t *rcs_recordset_t::alloc_record (rcs_record_t::type_t type, + size_t size); +template lit_magic_record_t *rcs_recordset_t::alloc_record (rcs_record_t::type_t type); +template lit_number_record_t *rcs_recordset_t::alloc_record (rcs_record_t::type_t type); diff --git a/jerry-core/lit/lit-literal-storage.h b/jerry-core/lit/lit-literal-storage.h new file mode 100644 index 000000000..c62d4a0a6 --- /dev/null +++ b/jerry-core/lit/lit-literal-storage.h @@ -0,0 +1,434 @@ +/* Copyright 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. + */ + +#ifndef LIT_LITERAL_STORAGE_H +#define LIT_LITERAL_STORAGE_H + +#include "ecma-globals.h" +#include "rcs-recordset.h" + +class lit_literal_storage_t; +extern lit_literal_storage_t lit_storage; + +/** + * Charset record + * + * layout: + * ------- header ----------------------- + * type (4 bits) + * alignment (2 bits) + * unused (2 bits) + * hash (8 bits) + * length (16 bits) + * pointer to prev (16 bits) + * ------- characters ------------------- + * ... + * chars + * .... + * ------- alignment bytes -------------- + * unused bytes (their count is specified + * by 'alignment' field in header) + * -------------------------------------- + */ +class lit_charset_record_t : public rcs_record_t +{ + friend class rcs_recordset_t; + friend class lit_literal_storage_t; + +public: + /** + * Calculate the size that record will occupy inside the storage + * + * @return size of the record in bytes + */ + static size_t + size (size_t size) /**< size of the charset buffer */ + { + return JERRY_ALIGNUP (size + header_size (), RCS_DYN_STORAGE_LENGTH_UNIT); + } /* size */ + + /** + * Get the size of the header part of the record + * + * @return size of the header in bytes + */ + static size_t + header_size () + { + return _header_size; + } /* header_size */ + + /** + * Get the count of the alignment bytes at the end of record. + * These bytes are needed to align the record to RCS_DYN_STORAGE_ALIGNMENT. + * + * @return alignment bytes count (the value of the 'alignment' field in the header) + */ + size_t + get_alignment_bytes_count () const + { + return get_field (_alignment_field_pos, _alignment_field_width); + } /* get_alignment_bytes_count */ + + /** + * Set the count of the alignment bytes at the end of record (the value of the 'alignment' field in the header) + */ + void + set_alignment_bytes_count (size_t count) /**< count of the alignment bytes */ + { + JERRY_ASSERT (count <= RCS_DYN_STORAGE_ALIGNMENT); + set_field (_alignment_field_pos, _alignment_field_width, count); + } /* set_alignment_bytes_count */ + + /** + * Get hash value of the record's charset. + * + * @return hash value of the string (the value of the 'hash' field in the header) + */ + ecma_string_hash_t + get_hash () const + { + return (ecma_string_hash_t) get_field (_hash_field_pos, _hash_field_width); + } /* get_hash */ + + /** + * Get the length of the string, which is contained inside the record + * + * @return length of the string (count of the ecma_char_t characters inside the charset) + */ + ecma_length_t + get_length () const + { + return (ecma_length_t) ((get_size () - header_size () - get_alignment_bytes_count ()) / sizeof (ecma_char_t)); + } /* get_length */ + + /** + * Get the size of the record + * + * @return size of the record in bytes + */ + size_t + get_size () const + { + return get_field (_length_field_pos, _length_field_width) * RCS_DYN_STORAGE_LENGTH_UNIT; + } /* get_size */ + + rcs_record_t *get_prev () const; + + ecma_length_t get_charset (ecma_char_t *buff, size_t size); + + int compare_zt (const ecma_char_t *, size_t); + bool equal (lit_charset_record_t *); + bool equal_zt (const ecma_char_t *); + bool equal_non_zt (const ecma_char_t *, ecma_length_t); + +private: + /** + * Set record's size (the value of the 'length' field in the header) + */ + void + set_size (size_t size) /**< size in bytes */ + { + JERRY_ASSERT (JERRY_ALIGNUP (size, RCS_DYN_STORAGE_ALIGNMENT) == size); + + set_field (_length_field_pos, _length_field_width, size >> RCS_DYN_STORAGE_ALIGNMENT_LOG); + } /* set_size */ + + /** + * Set record's hash (the value of the 'hash' field in the header) + */ + void + set_hash (ecma_string_hash_t hash) /**< hash value */ + { + set_field (_hash_field_pos, _hash_field_width, hash); + } /* set_hash */ + + void set_prev (rcs_record_t *); + + void set_charset (const ecma_char_t *, size_t); + + /** + * Offset and length of 'alignment' field, in bits + */ + static const uint32_t _alignment_field_pos = _fields_offset_begin; + static const uint32_t _alignment_field_width = 2u; + + /** + * Offset and length of 'hash' field, in bits + */ + static const uint32_t _hash_field_pos = _alignment_field_pos + _alignment_field_width + 2u; + static const uint32_t _hash_field_width = 8u; + + /** + * Offset and length of 'alignment' field, in bits + */ + static const uint32_t _length_field_pos = _hash_field_pos + _hash_field_width; + static const uint32_t _length_field_width = 16u; + + /** + * Offset and length of 'prev' field, in bits + */ + static const uint32_t _prev_field_pos = _length_field_pos + _length_field_width; + static const uint32_t _prev_field_width = rcs_cpointer_t::bit_field_width; + + static const size_t _header_size = RCS_DYN_STORAGE_LENGTH_UNIT + RCS_DYN_STORAGE_LENGTH_UNIT / 2; +}; /* lit_charset_record_t */ + +/** + * Magic string record + * Doesn't hold any characters. Corresponding string is identified by its id. + * + * Layout: + * ------- header ----------------------- + * type (4 bits) + * magic string id (12 bits) + * pointer to prev (16 bits) + * -------------------------------------- + */ +class lit_magic_record_t : public rcs_record_t +{ + friend class rcs_recordset_t; + friend class lit_literal_storage_t; + +public: + /** + * Calculate the size that record will occupy inside the storage + * + * @return size of the record in bytes + */ + static size_t size () + { + return _size; + } /* size */ + + /** + * Get the size of the header part of the record + * + * @return size of the header in bytes + */ + static size_t header_size () + { + return _size; + } /* header_size */ + + /** + * Get the size of the record + * + * @return size of the record in bytes + */ + size_t get_size () const + { + return _size; + } /* get_size */ + + /** + * Get magic string id which is held by the record + * + * @return magic string id + */ + ecma_magic_string_id_t get_magic_str_id () const + { + uint32_t id = get_field (magic_field_pos, magic_field_width); + JERRY_ASSERT (id < ECMA_MAGIC_STRING__COUNT); + return (ecma_magic_string_id_t) id; + } /* get_magic_str_id */ + +private: + /** + * Set record's size (the value of the 'length' field in the header) + */ + void set_size (size_t size) /**< size in bytes */ + { + JERRY_ASSERT (size == get_size ()); + } /* set_size */ + + void set_magic_str_id (ecma_magic_string_id_t id) + { + set_field (magic_field_pos, magic_field_width, id); + } /* set_magic_str_id */ + + /** + * Get previous record in the literal storage + * + * @return pointer to the previous record relative to this record, or NULL if this is the first record in the + * storage + */ + rcs_record_t *get_prev () const + { + return get_pointer (prev_field_pos, prev_field_width); + } /* get_prev */ + + /** + * Set the pointer to the previous record inside the literal storage (sets 'prev' field in the header) + */ + void set_prev (rcs_record_t *prev_rec_p) + { + set_pointer (prev_field_pos, prev_field_width, prev_rec_p); + } /* set_prev */ + + + /** + * Offset and length of 'magic string id' field, in bits + */ + static const uint32_t magic_field_pos = _fields_offset_begin; + static const uint32_t magic_field_width = 12u; + + /** + * Offset and length of 'prev' field, in bits + */ + static const uint32_t prev_field_pos = magic_field_pos + magic_field_width; + static const uint32_t prev_field_width = rcs_cpointer_t::bit_field_width; + + static const size_t _size = RCS_DYN_STORAGE_LENGTH_UNIT; +}; /* lit_magic_record_t */ + + +/** + * Number record + * Doesn't hold any characters, holds a number. Numbers from source code are represented as number literals. + * + * Layout: + * ------- header ----------------------- + * type (4 bits) + * magic string id (12 bits) + * pointer to prev (16 bits) + * -------------------------------------- + */ +class lit_number_record_t : public rcs_record_t +{ + friend class rcs_recordset_t; + friend class lit_literal_storage_t; + +public: + + static size_t + size () + { + return _size; + } /* size */ + + /** + * Get the size of the header part of the record + * + * @return size of the header in bytes + */ + static size_t + header_size () + { + return _header_size; + } /* header_size */ + + /** + * Get the size of the record + * + * @return size of the record in bytes + */ + size_t + get_size () const + { + return _size; + } /* get_size */ + + /** + * Get previous record in the literal storage + * + * @return pointer to the previous record relative to this record, or NULL if this is the first record in the + * storage + */ + rcs_record_t * + get_prev () const + { + return get_pointer (prev_field_pos, prev_field_width); + } /* get_prev */ + + /** + * Set the pointer to the previous record inside the literal storage (sets 'prev' field in the header) + */ + void + set_prev (rcs_record_t *prev_rec_p) + { + set_pointer (prev_field_pos, prev_field_width, prev_rec_p); + } /* set_prev */ + + /** + * Get the number which is held by the record + * + * @return number + */ + ecma_number_t + get_number () const + { + rcs_record_iterator_t it ((rcs_recordset_t *)&lit_storage, + (rcs_record_t *)this); + it.skip (header_size ()); + + return it.read (); + } /* get_number */ + +private: + /** + * Set record's size (the value of the 'length' field in the header) + */ + void + set_size (size_t size) /**< size in bytes */ + { + JERRY_ASSERT (size == get_size ()); + } /* set_size */ + + /** + * Offset and length of 'prev' field, in bits + */ + static const uint32_t prev_field_pos = _fields_offset_begin + 12u; + static const uint32_t prev_field_width = rcs_cpointer_t::bit_field_width; + + static const size_t _header_size = RCS_DYN_STORAGE_LENGTH_UNIT; + static const size_t _size = _header_size + sizeof (ecma_number_t); +}; /* lit_number_record_t */ + +/** + * Literal storage + * + * Represents flexible storage for the literals. The following records could be created inside the storage: + * - charset literal (lit_charset_record_t) + * - magic string literal (lit_magic_record_t) + * - number literal (lit_number_record_t) + */ +class lit_literal_storage_t : public rcs_recordset_t +{ +public: + enum + { + LIT_STR = _first_type_id, + LIT_MAGIC_STR, + LIT_NUMBER + }; + + lit_charset_record_t *create_charset_record (const ecma_char_t *, size_t); + lit_magic_record_t *create_magic_record (ecma_magic_string_id_t); + lit_number_record_t *create_number_record (ecma_number_t); + + void dump (); + +private: + virtual rcs_record_t *get_prev (rcs_record_t *); + virtual void set_prev (rcs_record_t *, rcs_record_t *); + virtual size_t get_record_size (rcs_record_t *); +}; /* lit_literal_storage_t */ + +#define LIT_STR_T (lit_literal_storage_t::LIT_STR) +#define LIT_MAGIC_STR_T (lit_literal_storage_t::LIT_MAGIC_STR) +#define LIT_NUMBER_T (lit_literal_storage_t::LIT_NUMBER) + +#endif /* LIT_LITERAL_STORAGE_H */ diff --git a/jerry-core/lit/lit-literal.cpp b/jerry-core/lit/lit-literal.cpp new file mode 100644 index 000000000..50d112cb7 --- /dev/null +++ b/jerry-core/lit/lit-literal.cpp @@ -0,0 +1,504 @@ +/* Copyright 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. + */ + +#include "lit-literal.h" +#include "ecma-helpers.h" + +/** + * Initialize literal storage + */ +void +lit_init () +{ + new (&lit_storage) lit_literal_storage_t (); + lit_storage.init (); +} /* lit_init */ + +/** + * Finalize literal storage + */ +void +lit_finalize () +{ + lit_storage.cleanup (); + lit_storage.finalize (); +} /* lit_finalize */ + +/** + * Dump records from the literal storage + */ +void +lit_dump_literals () +{ + lit_storage.dump (); +} /* lit_dump_literals */ + +/** + * Create new literal in literal storage from characters buffer. + * Don't check if the same literal already exists. + * + * @return pointer to created record + */ +literal_t +lit_create_literal_from_charset (const ecma_char_t *str, /**< string to initialize the record, + * could be non-zero-terminated */ + ecma_length_t len) /**< length of the string */ +{ + JERRY_ASSERT (str || !len); + for (ecma_magic_string_id_t msi = (ecma_magic_string_id_t) 0; + msi < ECMA_MAGIC_STRING__COUNT; + msi = (ecma_magic_string_id_t) (msi + 1)) + { + if (ecma_zt_string_length (ecma_get_magic_string_zt (msi)) != len) + { + continue; + } + + if (!strncmp ((const char *) str, (const char *) ecma_get_magic_string_zt (msi), len)) + { + return lit_storage.create_magic_record (msi); + } + } + + return lit_storage.create_charset_record (str, len * sizeof (ecma_char_t)); +} /* lit_create_literal_from_charset */ + +/** + * Find a literal in literal storage. + * Only charset and magic string records are checked during search. + * + * @return pointer to a literal or NULL if no corresponding literal exists + */ +literal_t +lit_find_literal_by_charset (const ecma_char_t *str, /**< a string to search for */ + ecma_length_t len) /**< length of the string */ +{ + JERRY_ASSERT (str || !len); + for (literal_t lit = lit_storage.get_first (); lit != NULL; lit = lit_storage.get_next (lit)) + { + rcs_record_t::type_t type = lit->get_type (); + + if (type == LIT_STR_T) + { + if (static_cast(lit)->get_length () != len) + { + continue; + } + + if (!static_cast(lit)->compare_zt (str, len)) + { + return lit; + } + } + else if (type == LIT_MAGIC_STR_T) + { + ecma_magic_string_id_t magic_id = static_cast(lit)->get_magic_str_id (); + const char *magic_str = (const char *)ecma_get_magic_string_zt (magic_id); + + if (strlen (magic_str) != len) + { + continue; + } + + if (!strncmp (magic_str, (const char *) str, strlen (magic_str))) + { + return lit; + } + } + } + + return NULL; +} /* lit_find_literal_by_charset */ + +/** + * Check if a literal which holds the passed string exists. + * If it doesn't exist, create a new one. + * + * @return pointer to existing or newly created record + */ +literal_t +lit_find_or_create_literal_from_charset (const ecma_char_t *str, /**< string, could be non-zero-terminated */ + ecma_length_t len) /**< length of the string */ +{ + literal_t lit = lit_find_literal_by_charset (str, len); + + if (lit == NULL) + { + lit = lit_create_literal_from_charset (str, len); + } + + return lit; +} /* lit_find_or_create_literal_from_s */ + + +/** + * Create new literal in literal storage from number. + * + * @return pointer to a newly created record + */ +literal_t +lit_create_literal_from_num (ecma_number_t num) /**< number to initialize a new number literal */ +{ + return lit_storage.create_number_record (num); +} /* lit_create_literal_from_num */ + +/** + * Find existing or create new number literal in literal storage. + * + * @return pointer to existing or a newly created record + */ +literal_t +lit_find_or_create_literal_from_num (ecma_number_t num) /**< number which a literal should contain */ +{ + literal_t lit = lit_find_literal_by_num (num); + + if (lit == NULL) + { + lit = lit_create_literal_from_num (num); + } + + return lit; +} /* lit_find_or_create_literal_from_num */ + +/** + * Find an existing number literal which contains the passed number + * + * @return pointer to existing or null + */ +literal_t +lit_find_literal_by_num (ecma_number_t num) /**< a number to search for */ +{ + for (literal_t lit = lit_storage.get_first (); lit != NULL; lit = lit_storage.get_next (lit)) + { + rcs_record_t::type_t type = lit->get_type (); + + if (type != LIT_NUMBER_T) + { + continue; + } + + ecma_number_t lit_num = static_cast(lit)->get_number (); + + if (lit_num == num) + { + return lit; + } + } + + return NULL; +} /* lit_find_literal_by_num */ + +/** + * Check if literal equals to charset record + * + * @return true if equal + * false otherwise + */ +static bool +lit_literal_equal_charset_rec (literal_t lit, /**< literal to compare */ + lit_charset_record_t *record) /**< charset record to compare */ +{ + switch (lit->get_type ()) + { + case LIT_STR_T: + { + return static_cast(lit)->equal (record); + } + case LIT_MAGIC_STR_T: + { + return record->equal_zt (ecma_get_magic_string_zt (static_cast(lit)->get_magic_str_id ())); + } + case LIT_NUMBER_T: + { + ecma_char_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; + ecma_number_to_zt_string (static_cast(lit)->get_number (), + buff, + ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); + + return record->equal_zt (buff); + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* lit_literal_equal_charset_rec */ + +/** + * Check if literal equals to zero-terminated string + * + * @return true if equal + * false otherwise + */ +bool +lit_literal_equal_zt (literal_t lit, /**< literal to compare */ + const ecma_char_t *str) /**< zero-terminated string to compare */ +{ + switch (lit->get_type ()) + { + case LIT_STR_T: + { + return static_cast(lit)->equal_zt (str); + } + case LIT_MAGIC_STR_T: + { + ecma_magic_string_id_t magic_id = static_cast(lit)->get_magic_str_id (); + + return ecma_compare_zt_strings (str, ecma_get_magic_string_zt (magic_id)); + } + case LIT_NUMBER_T: + { + ecma_char_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; + ecma_number_to_zt_string (static_cast(lit)->get_number (), + buff, + ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); + + return ecma_compare_zt_strings (str, buff); + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* lit_literal_equal_zt */ + +/** + * Check if literal contains the string equal to the passed number + * + * @return true if equal + * false otherwise + */ +bool +lit_literal_equal_num (literal_t lit, /**< literal to check */ + ecma_number_t num) /**< number to compare with */ +{ + ecma_char_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; + ecma_number_to_zt_string (num, buff, ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); + + return lit_literal_equal_zt (lit, buff); +} /* lit_literal_equal_num */ + +/** + * Check if two literals are equal + * + * @return true if equal + * false otherwise + */ +bool +lit_literal_equal (literal_t lit1, /**< first literal */ + literal_t lit2) /**< second literal */ +{ + switch (lit2->get_type ()) + { + case lit_literal_storage_t::LIT_STR: + { + return lit_literal_equal_charset_rec (lit1, static_cast(lit2)); + } + case lit_literal_storage_t::LIT_MAGIC_STR: + { + ecma_magic_string_id_t magic_id = static_cast(lit2)->get_magic_str_id (); + + return lit_literal_equal_zt (lit1, ecma_get_magic_string_zt (magic_id)); + } + case lit_literal_storage_t::LIT_NUMBER: + { + return lit_literal_equal_num (lit1, static_cast(lit2)->get_number ()); + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* lit_literal_equal */ + +/** + * Check if literal equals to zero-terminated string. + * Check that literal is a string literal before performing detailed comparison. + * + * @return true if equal + * false otherwise + */ +bool +lit_literal_equal_type_zt (literal_t lit, /**< literal to compare */ + const ecma_char_t *str) /**< zero-terminated string */ +{ + if (lit->get_type () != LIT_STR_T + && lit->get_type () != LIT_MAGIC_STR_T) + { + return false; + } + + return lit_literal_equal_zt (lit, str); +} /* lit_literal_equal_type_zt */ + +/** + * Check if literal contains the string equal to the passed number. + * Check that literal is a number literal before performing detailed comparison. + * + * @return true if equal + * false otherwise + */ +bool +lit_literal_equal_type_num (literal_t lit, /**< literal to check */ + ecma_number_t num) /**< number to compare with */ +{ + if (lit->get_type () != lit_literal_storage_t::LIT_NUMBER) + { + return false; + } + + return lit_literal_equal_num (lit, num); +} /* lit_literal_equal_type_num */ + +/** + * Check if two literals are equal + * Compare types of literals before performing detailed comparison. + * + * @return true if equal + * false otherwise + */ +bool +lit_literal_equal_type (literal_t lit1, /**< first literal */ + literal_t lit2) /**< second literal */ +{ + if (lit1->get_type () != lit2->get_type ()) + { + return false; + } + + return lit_literal_equal (lit1, lit2); +} /* lit_literal_equal_type */ + + +/** + * Get the contents of the literal as a zero-terminated string. + * If literal is a magic string record, the corresponding string is not copied to the buffer, + * but is returned directly. + * + * @return pointer to the zero-terminated string. + */ +const ecma_char_t * +lit_literal_to_charset (literal_t lit, /**< literal to be processed */ + ecma_char_t *buff, /**< buffer to use as a string storage */ + size_t size) /**< size of the buffer */ +{ + JERRY_ASSERT (buff != NULL && size > sizeof (ecma_char_t)); + rcs_record_t::type_t type = lit->get_type (); + + switch (type) + { + case LIT_STR_T: + { + lit_charset_record_t *ch_rec_p = static_cast (lit); + ecma_length_t index = ch_rec_p->get_charset (buff, size); + + if (index != 0 && ((size_t)index + 1) * sizeof (ecma_char_t) > size) + { + index--; + } + buff[index] = '\0'; + + return buff; + } + case LIT_MAGIC_STR_T: + { + return ecma_get_magic_string_zt (static_cast (lit)->get_magic_str_id ()); + } + case LIT_NUMBER_T: + { + ecma_number_to_zt_string (static_cast (lit)->get_number (), buff, (ssize_t)size); + + return buff; + } + default: JERRY_UNREACHABLE (); + } + + JERRY_UNREACHABLE (); +} /* lit_literal_to_charset */ + +/** + * Get the contents of the literal as a C string. + * If literal holds a very long string, it would be trimmed to ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER characters. + * + * @return pointer to the C string. + */ +const char * +lit_literal_to_str_internal_buf (literal_t lit) /**< literal */ +{ + const ecma_length_t buff_size = ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER; + static ecma_char_t buff[buff_size]; + + return (const char *)lit_literal_to_charset (lit, buff, buff_size); +} /* lit_literal_to_str_internal_buf */ + + +/** + * Check if literal really exists in the storage + * + * @return true if literal exists in the storage + * false otherwise + */ +static bool +lit_literal_exists (literal_t lit) /**< literal to check for existence */ +{ + for (literal_t l = lit_storage.get_first (); l != NULL; l = lit_storage.get_next (l)) + { + if (l == lit) + { + return true; + } + } + + return false; +} /* lit_literal_exists */ + +/** + * Convert compressed pointer to literal + * + * @return literal + */ +literal_t +lit_get_literal_by_cp (lit_cpointer_t lit_cp) /**< compressed pointer to literal */ +{ + JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); + literal_t lit = lit_cpointer_t::decompress (lit_cp); + JERRY_ASSERT (lit_literal_exists (lit)); + + return lit; +} /* lit_get_literal_by_cp */ + +ecma_string_hash_t +lit_charset_literal_get_hash (literal_t lit) /**< literal */ +{ + return static_cast (lit)->get_hash (); +} /* lit_charset_literal_get_hash */ + +ecma_magic_string_id_t +lit_magic_record_get_magic_str_id (literal_t lit) /**< literal */ +{ + return static_cast (lit)->get_magic_str_id (); +} /* lit_magic_record_get_magic_str_id */ + +int32_t +lit_charset_record_get_length (literal_t lit) /**< literal */ +{ + return static_cast (lit)->get_length ();; +} /* lit_charset_record_get_length */ + +ecma_number_t +lit_charset_literal_get_number (literal_t lit) /**< literal */ +{ + return static_cast (lit)->get_number ();; +} /* lit_charset_literal_get_number */ diff --git a/jerry-core/lit/lit-literal.h b/jerry-core/lit/lit-literal.h new file mode 100644 index 000000000..1795b487c --- /dev/null +++ b/jerry-core/lit/lit-literal.h @@ -0,0 +1,55 @@ +/* Copyright 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. + */ + +#ifndef LIT_LITERAL_H +#define LIT_LITERAL_H + +#include "ecma-globals.h" +#include "lit-literal-storage.h" + +#define LITERAL_TO_REWRITE (INVALID_VALUE - 1) + +void lit_init (); +void lit_finalize (); +void lit_dump_literals (); + +literal_t lit_create_literal_from_charset (const ecma_char_t *, ecma_length_t); +literal_t lit_find_literal_by_charset (const ecma_char_t *, ecma_length_t); +literal_t lit_find_or_create_literal_from_charset (const ecma_char_t *, ecma_length_t); + +literal_t lit_create_literal_from_num (ecma_number_t); +literal_t lit_find_literal_by_num (ecma_number_t); +literal_t lit_find_or_create_literal_from_num (ecma_number_t); + +bool lit_literal_equal_zt (literal_t, const ecma_char_t *); +bool lit_literal_equal_num (literal_t, ecma_number_t); +bool lit_literal_equal (literal_t, literal_t); + +bool lit_literal_equal_type_zt (literal_t, const ecma_char_t *); +bool lit_literal_equal_type_num (literal_t, ecma_number_t); +bool lit_literal_equal_type (literal_t, literal_t); + +const ecma_char_t *lit_literal_to_charset (literal_t, ecma_char_t *, size_t); +const char *lit_literal_to_str_internal_buf (literal_t); + +literal_t lit_get_literal_by_cp (lit_cpointer_t); + +ecma_string_hash_t lit_charset_literal_get_hash (literal_t); +ecma_number_t lit_charset_literal_get_number (literal_t); +int32_t lit_charset_record_get_length (literal_t); + +ecma_magic_string_id_t lit_magic_record_get_magic_str_id (literal_t); + +#endif /* LIT_LITERAL_H */ diff --git a/jerry-core/rcs/rcs-chunked-list.cpp b/jerry-core/rcs/rcs-chunked-list.cpp index 29a28bee2..def0ada18 100644 --- a/jerry-core/rcs/rcs-chunked-list.cpp +++ b/jerry-core/rcs/rcs-chunked-list.cpp @@ -35,6 +35,15 @@ rcs_chunked_list_t::free (void) JERRY_ASSERT (tail_p == NULL); } /* rcs_chunked_list_t::free */ +void +rcs_chunked_list_t::cleanup (void) +{ + while (head_p) + { + remove (head_p); + } +} /* rcs_chunked_list_t::cleanup */ + /** * Get first node of the list * diff --git a/jerry-core/rcs/rcs-chunked-list.h b/jerry-core/rcs/rcs-chunked-list.h index 64c18cfb9..ff54e464f 100644 --- a/jerry-core/rcs/rcs-chunked-list.h +++ b/jerry-core/rcs/rcs-chunked-list.h @@ -55,6 +55,7 @@ public: } node_t; void init (void); + void cleanup (void); void free (void); node_t *get_first (void) const; diff --git a/jerry-core/rcs/rcs-recordset.h b/jerry-core/rcs/rcs-recordset.h index 3128d98a3..fab08382a 100644 --- a/jerry-core/rcs/rcs-recordset.h +++ b/jerry-core/rcs/rcs-recordset.h @@ -74,6 +74,12 @@ public: _chunk_list.free (); } /* finalize */ + /* Free memory occupied by the dynamic storage */ + void cleanup (void) + { + _chunk_list.cleanup (); + } /* cleanup */ + /** * Record type */ diff --git a/tests/unit/test_literal_storage.cpp b/tests/unit/test_literal_storage.cpp new file mode 100644 index 000000000..d052d08cf --- /dev/null +++ b/tests/unit/test_literal_storage.cpp @@ -0,0 +1,155 @@ +/* Copyright 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. + */ + +#include "ecma-helpers.h" +#include "lit-literal.h" + +extern "C" +{ + extern void srand (unsigned int __seed); + extern int rand (void); + extern long int time (long int *__timer); + extern int printf (__const char *__restrict __format, ...); + extern void *memset (void *__s, int __c, size_t __n); +} + +// Heap size is 32K +#define test_heap_size (32 * 1024) + +// Iterations count +#define test_iters 64 + +// Subiterations count +#define test_sub_iters 64 + +// Max characters in a string +#define max_characters_in_string 256 + +static void +generate_string (ecma_char_t *str, ecma_length_t len) +{ + static const ecma_char_t characters[] = "!@#$%^&*()_+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; + static const ecma_length_t length = (ecma_length_t) (sizeof (characters) / sizeof (ecma_char_t) - 1); + for (ecma_length_t i = 0; i < len; ++i) + { + str[i] = characters[(unsigned long) rand () % length]; + } +} + +static ecma_number_t +generate_number () +{ + ecma_number_t num = ((ecma_number_t) rand () / 32767.0); + if (rand () % 2) + { + num = -num; + } + int power = rand () % 30; + while (power-- > 0) + { + num *= 10; + } + return num; +} + +int +main (int __attr_unused___ argc, + char __attr_unused___ **argv) +{ + const ecma_char_t *ptrs[test_sub_iters]; + ecma_number_t numbers[test_sub_iters]; + ecma_char_t strings[test_sub_iters][max_characters_in_string + 1]; + ecma_length_t lengths[test_sub_iters]; + + mem_init (); + lit_init (); + + srand ((unsigned int) time (NULL)); + int k = rand (); + printf ("seed=%d\n", k); + srand ((unsigned int) k); + + for (uint32_t i = 0; i < test_iters; i++) + { + memset (numbers, 0, sizeof (ecma_number_t) * test_sub_iters); + memset (lengths, 0, sizeof (ecma_length_t) * test_sub_iters); + memset (ptrs, 0, sizeof (ecma_char_t *) * test_sub_iters); + + for (uint32_t j = 0; j < test_sub_iters; j++) + { + int type = rand () % 3; + if (type == 0) + { + lengths[j] = (ecma_length_t) (rand () % max_characters_in_string + 1); + generate_string (strings[j], lengths[j]); + lit_create_literal_from_charset (strings[j], lengths[j]); + strings[j][lengths[j]] = '\0'; + ptrs[j] = strings[j]; + JERRY_ASSERT (ptrs[j]); + } + else if (type == 1) + { + ecma_magic_string_id_t msi = (ecma_magic_string_id_t) (rand () % ECMA_MAGIC_STRING__COUNT); + ptrs[j] = ecma_get_magic_string_zt (msi); + JERRY_ASSERT (ptrs[j]); + lengths[j] = (ecma_length_t) ecma_zt_string_length (ptrs[j]); + lit_create_literal_from_charset (ptrs[j], lengths[j]); + } + else + { + ecma_number_t num = generate_number (); + lengths[j] = ecma_number_to_zt_string (num, strings[j], max_characters_in_string); + lit_create_literal_from_num (num); + } + } + + // Add empty string + lit_create_literal_from_charset (NULL, 0); + + for (uint32_t j = 0; j < test_sub_iters; j++) + { + literal_t lit1; + literal_t lit2; + if (ptrs[j]) + { + lit1 = lit_find_or_create_literal_from_charset (ptrs[j], lengths[j]); + lit2 = lit_find_literal_by_charset (ptrs[j], lengths[j]); + JERRY_ASSERT (lit_literal_equal_zt (lit1, ptrs[j])); + JERRY_ASSERT (lit_literal_equal_type_zt (lit2, ptrs[j])); + } + else + { + lit1 = lit_find_or_create_literal_from_num (numbers[j]); + lit2 = lit_find_literal_by_num (numbers[j]); + JERRY_ASSERT (lit_literal_equal_num (lit1, numbers[j])); + JERRY_ASSERT (lit_literal_equal_type_num (lit2, numbers[j])); + } + JERRY_ASSERT (lit1); + JERRY_ASSERT (lit2); + JERRY_ASSERT (lit1 == lit2); + JERRY_ASSERT (lit_literal_equal (lit1, lit2)); + } + + // Check empty string exists + JERRY_ASSERT (lit_find_literal_by_charset (NULL, 0)); + + lit_storage.cleanup (); + JERRY_ASSERT (lit_storage.get_first () == NULL); + } + + lit_finalize (); + mem_finalize (true); + return 0; +}