diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index 40dfc8989..3699c1468 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -31,6 +31,7 @@ project (JerryCore CXX C ASM) OUTPUT_STRIP_TRAILING_WHITESPACE) set(DEFINES_JERRY + JERRY_ENABLE_SNAPSHOT JERRY_BUILD_DATE="${JERRY_BUILD_DATE}" JERRY_COMMIT_HASH="${JERRY_GIT_COMMIT}" JERRY_BRANCH_NAME="${JERRY_GIT_BRANCH}") diff --git a/jerry-core/jerry-api.h b/jerry-core/jerry-api.h index b25ca759a..2ab5566d6 100644 --- a/jerry-core/jerry-api.h +++ b/jerry-core/jerry-api.h @@ -36,8 +36,10 @@ */ typedef enum { - JERRY_COMPLETION_CODE_OK = 0, /**< successful completion */ - JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION = 1, /**< exception occured and it was not handled */ + JERRY_COMPLETION_CODE_OK = 0, /**< successful completion */ + JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION = 1, /**< exception occured and it was not handled */ + JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_VERSION = 2, /**< snapshot version mismatch */ + JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT = 3, /**< snapshot format is not valid */ } jerry_completion_code_t; /** @@ -256,6 +258,19 @@ void jerry_register_external_magic_strings (const jerry_api_char_ptr_t* ex_str_i uint32_t count, const jerry_api_length_t* str_lengths); +extern EXTERN_C +size_t jerry_parse_and_save_snapshot (const jerry_api_char_t* source_p, + size_t source_size, + bool is_for_global_or_eval, + uint8_t *buffer_p, + size_t buffer_size); + +extern EXTERN_C +jerry_completion_code_t jerry_exec_snapshot (const void *snapshot_p, + size_t snapshot_size, + bool is_copy, + jerry_api_value_t *retval_p); + /** * @} */ diff --git a/jerry-core/jerry-internal.h b/jerry-core/jerry-internal.h index 12203e2d6..3f43a38be 100644 --- a/jerry-core/jerry-internal.h +++ b/jerry-core/jerry-internal.h @@ -36,4 +36,22 @@ jerry_dispatch_object_free_callback (ecma_external_pointer_t freecb_p, extern bool jerry_is_abort_on_fail (void); +/** + * Snapshot header + */ +typedef struct +{ + uint32_t lit_table_size; /**< size of literal table */ + uint32_t bytecode_size; /**< size of instructions array */ + uint32_t idx_to_lit_map_size; /** size of idx-to-lit map */ + uint32_t is_run_global : 1; /**< flag, indicating whether the snapshot + * was dumped as 'Global scope'-mode code (true) + * or as eval-mode code (false) */ +} jerry_snapshot_header_t; + +/** + * Jerry snapshot format version + */ +#define JERRY_SNAPSHOT_VERSION (1u) + #endif /* !JERRY_INTERNAL_H */ diff --git a/jerry-core/jerry.cpp b/jerry-core/jerry.cpp index ac9113b6d..47082cd13 100644 --- a/jerry-core/jerry.cpp +++ b/jerry-core/jerry.cpp @@ -269,6 +269,48 @@ jerry_api_convert_api_value_to_ecma_value (ecma_value_t *out_value_p, /**< out: } } /* jerry_api_convert_api_value_to_ecma_value */ +/** + * Convert completion of 'eval' to API value and completion code + * + * Note: + * if the output value contains string / object, it should be freed + * with jerry_api_release_string / jerry_api_release_object, + * just when it becomes unnecessary. + * + * @return completion code + */ +static jerry_completion_code_t +jerry_api_convert_eval_completion_to_retval (jerry_api_value_t *retval_p, /**< out: api value */ + ecma_completion_value_t completion) /**< completion of 'eval'-mode + * code execution */ +{ + jerry_completion_code_t ret_code; + + if (ecma_is_completion_value_normal (completion)) + { + ret_code = JERRY_COMPLETION_CODE_OK; + + jerry_api_convert_ecma_value_to_api_value (retval_p, + ecma_get_completion_value_value (completion)); + } + else + { + jerry_api_convert_ecma_value_to_api_value (retval_p, ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); + + if (ecma_is_completion_value_throw (completion)) + { + ret_code = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION; + } + else + { + JERRY_ASSERT (ecma_is_completion_value_empty (completion)); + + ret_code = JERRY_COMPLETION_CODE_OK; + } + } + + return ret_code; +} /* jerry_api_convert_eval_completion_to_retval */ /** * @} @@ -1268,28 +1310,7 @@ jerry_api_eval (const jerry_api_char_t *source_p, /**< source code */ is_direct, is_strict); - if (ecma_is_completion_value_normal (completion)) - { - status = JERRY_COMPLETION_CODE_OK; - - jerry_api_convert_ecma_value_to_api_value (retval_p, - ecma_get_completion_value_value (completion)); - } - else - { - jerry_api_convert_ecma_value_to_api_value (retval_p, ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); - - if (ecma_is_completion_value_throw (completion)) - { - status = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION; - } - else - { - JERRY_ASSERT (ecma_is_completion_value_empty (completion)); - - status = JERRY_COMPLETION_CODE_OK; - } - } + status = jerry_api_convert_eval_completion_to_retval (retval_p, completion); ecma_free_completion_value (completion); @@ -1539,3 +1560,239 @@ jerry_register_external_magic_strings (const jerry_api_char_ptr_t* ex_str_items, { lit_magic_strings_ex_set ((const lit_utf8_byte_t **) ex_str_items, count, (const lit_utf8_size_t *) str_lengths); } /* jerry_register_external_magic_strings */ + +/** + * Generate snapshot from specified source + * + * @return size of snapshot, if it was generated succesfully + * (i.e. there are no syntax errors in source code, buffer size is sufficient, + * and snapshot support is enabled in current configuration through JERRY_ENABLE_SNAPSHOT), + * 0 - otherwise. + */ +size_t +jerry_parse_and_save_snapshot (const jerry_api_char_t* source_p, /**< script source */ + size_t source_size, /**< script source size */ + bool is_for_global, /**< snapshot would be executed as global (true) + * or eval (false) */ + uint8_t *buffer_p, /**< buffer to dump snapshot to */ + size_t buffer_size) /**< the buffer's size */ +{ +#ifdef JERRY_ENABLE_SNAPSHOT + jsp_status_t parse_status; + const bytecode_data_header_t *bytecode_data_p; + + if (is_for_global) + { + parse_status = parser_parse_script (source_p, source_size, &bytecode_data_p); + } + else + { + bool code_contains_functions; + + parse_status = parser_parse_eval (source_p, + source_size, + false, + &bytecode_data_p, + &code_contains_functions); + } + + if (parse_status != JSP_STATUS_OK) + { + return 0; + } + + size_t buffer_write_offset = 0; + + jerry_snapshot_header_t header; + header.is_run_global = is_for_global; + + uint64_t version = JERRY_SNAPSHOT_VERSION; + if (!jrt_write_to_buffer_by_offset (buffer_p, + buffer_size, + &buffer_write_offset, + version)) + { + return 0; + } + + size_t header_offset = buffer_write_offset; + + if (buffer_write_offset + sizeof (jerry_snapshot_header_t) > buffer_size) + { + return 0; + } + buffer_write_offset += sizeof (jerry_snapshot_header_t); + + lit_mem_to_snapshot_id_map_entry_t* lit_map_p = NULL; + uint32_t literals_num; + + if (!lit_dump_literals_for_snapshot (buffer_p, + buffer_size, + &buffer_write_offset, + &lit_map_p, + &literals_num, + &header.lit_table_size)) + { + JERRY_ASSERT (lit_map_p == NULL); + return 0; + } + + size_t bytecode_offset = sizeof (version) + sizeof (jerry_snapshot_header_t) + header.lit_table_size; + JERRY_ASSERT (JERRY_ALIGNUP (bytecode_offset, MEM_ALIGNMENT) == bytecode_offset); + + bool is_ok = serializer_dump_bytecode_with_idx_map (buffer_p, + buffer_size, + &buffer_write_offset, + bytecode_data_p, + lit_map_p, + literals_num, + &header.bytecode_size, + &header.idx_to_lit_map_size); + + if (lit_map_p != NULL) + { + mem_heap_free_block (lit_map_p); + } + + if (!is_ok) + { + return 0; + } + + is_ok = jrt_write_to_buffer_by_offset (buffer_p, buffer_size, &header_offset, header); + JERRY_ASSERT (is_ok && header_offset < buffer_write_offset); + + return buffer_write_offset; +#else /* JERRY_ENABLE_SNAPSHOT */ + (void) source_p; + (void) source_size; + (void) is_for_global; + (void) buffer_p; + (void) buffer_size; + + return 0; +#endif /* !JERRY_ENABLE_SNAPSHOT */ +} /* jerry_parse_and_save_snapshot */ + +/** + * Execute snapshot from specified buffer + * + * @return completion code + */ +jerry_completion_code_t +jerry_exec_snapshot (const void *snapshot_p, /**< snapshot */ + size_t snapshot_size, /**< size of snapshot */ + bool is_copy, /**< flag, indicating whether the passed snapshot + * buffer should be copied to engine's memory, + * so engine should not reference the buffer + * after the function returns (in the case, the passed + * buffer could be freed after the call); + * otherwise (if flag not set) - the buffer could be freed + * only after engine stops (i.e. after call to jerry_cleanup). */ + jerry_api_value_t *retval_p) /**< out: returned value (ECMA-262 'undefined' if code is executed + * as global scope code) */ +{ + jerry_api_convert_ecma_value_to_api_value (retval_p, ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); + +#ifdef JERRY_ENABLE_SNAPSHOT + JERRY_ASSERT (snapshot_p != NULL); + + const uint8_t *snapshot_data_p = (uint8_t *) snapshot_p; + size_t snapshot_read = 0; + uint64_t version; + + if (!jrt_read_from_buffer_by_offset (snapshot_data_p, + snapshot_size, + &snapshot_read, + &version)) + { + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; + } + + if (version != JERRY_SNAPSHOT_VERSION) + { + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_VERSION; + } + + const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) (snapshot_data_p + snapshot_read); + if (snapshot_read + sizeof (jerry_snapshot_header_t) > snapshot_size) + { + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; + } + + snapshot_read += sizeof (jerry_snapshot_header_t); + + if (snapshot_read + header_p->lit_table_size > snapshot_size) + { + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; + } + + lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL; + uint32_t literals_num; + + if (!lit_load_literals_from_snapshot (snapshot_data_p + snapshot_read, + header_p->lit_table_size, + &lit_map_p, + &literals_num, + is_copy)) + { + JERRY_ASSERT (lit_map_p == NULL); + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; + } + + snapshot_read += header_p->lit_table_size; + + if (snapshot_read + header_p->bytecode_size + header_p->idx_to_lit_map_size > snapshot_size) + { + mem_heap_free_block (lit_map_p); + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; + } + + const bytecode_data_header_t *bytecode_data_p; + bytecode_data_p = serializer_load_bytecode_with_idx_map (snapshot_data_p + snapshot_read, + header_p->bytecode_size, + header_p->idx_to_lit_map_size, + lit_map_p, + literals_num, + is_copy); + + if (lit_map_p != NULL) + { + mem_heap_free_block (lit_map_p); + } + + if (bytecode_data_p == NULL) + { + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_FORMAT; + } + + jerry_completion_code_t ret_code; + + if (header_p->is_run_global) + { + vm_init (bytecode_data_p, false); + + ret_code = vm_run_global (); + + vm_finalize (); + } + else + { + /* vm should be already initialized */ + ecma_completion_value_t completion = vm_run_eval (bytecode_data_p, false); + + ret_code = jerry_api_convert_eval_completion_to_retval (retval_p, completion); + + ecma_free_completion_value (completion); + } + + return ret_code; +#else /* JERRY_ENABLE_SNAPSHOT */ + (void) snapshot_p; + (void) snapshot_size; + (void) is_copy; + (void) retval_p; + + return JERRY_COMPLETION_CODE_INVALID_SNAPSHOT_VERSION; +#endif /* !JERRY_ENABLE_SNAPSHOT */ +} /* jerry_exec_snapshot */ diff --git a/jerry-core/jrt/jrt.h b/jerry-core/jrt/jrt.h index f54a55362..e5f121100 100644 --- a/jerry-core/jrt/jrt.h +++ b/jerry-core/jrt/jrt.h @@ -17,6 +17,8 @@ #define JERRY_GLOBALS_H #include +#include + #include "jerry.h" #include "jrt-types.h" @@ -227,4 +229,60 @@ inline void* operator new (size_t, void* where) return where; } /* operator new */ +/** + * Read data of specified type (T) from specified buffer + * + * Note: + * Offset is in-out and is incremented if the read operation completes successfully. + * + * @return true, if read was successful, i.e. offset + sizeof (T) doesn't exceed buffer size, + * false - otherwise. + */ +template +bool __attr_always_inline___ +jrt_read_from_buffer_by_offset (const uint8_t *buffer_p, /**< buffer */ + size_t buffer_size, /**< size of buffer */ + size_t *in_out_buffer_offset_p, /**< in: offset to read from, + * out: offset, incremented on sizeof (T) */ + T *out_data_p) /**< out: data */ +{ + if (*in_out_buffer_offset_p + sizeof (T) > buffer_size) + { + return false; + } + + memcpy (out_data_p, buffer_p + *in_out_buffer_offset_p, sizeof (T)); + *in_out_buffer_offset_p += sizeof (T); + + return true; +} /* jrt_read_from_buffer_by_offset */ + +/** + * Write data of specified type (T) to specified buffer + * + * Note: + * Offset is in-out and is incremented if the write operation completes successfully. + * + * @return true, if write was successful, i.e. offset + sizeof (T) doesn't exceed buffer size, + * false - otherwise. + */ +template +bool __attr_always_inline___ +jrt_write_to_buffer_by_offset (uint8_t *buffer_p, /**< buffer */ + size_t buffer_size, /**< size of buffer */ + size_t *in_out_buffer_offset_p, /**< in: offset to read from, + * out: offset, incremented on sizeof (T) */ + T data) /**< data */ +{ + if (*in_out_buffer_offset_p + sizeof (T) > buffer_size) + { + return false; + } + + memcpy (buffer_p + *in_out_buffer_offset_p, &data, sizeof (T)); + *in_out_buffer_offset_p += sizeof (T); + + return true; +} /* jrt_write_to_buffer_by_offset */ + #endif /* !JERRY_GLOBALS_H */ diff --git a/jerry-core/lit/lit-literal-storage.cpp b/jerry-core/lit/lit-literal-storage.cpp index 86c10b6db..ae156fd23 100644 --- a/jerry-core/lit/lit-literal-storage.cpp +++ b/jerry-core/lit/lit-literal-storage.cpp @@ -13,8 +13,9 @@ * limitations under the License. */ -#include "lit-literal-storage.h" #include "ecma-helpers.h" +#include "jrt.h" +#include "lit-literal-storage.h" #include "lit-literal.h" #include "lit-magic-strings.h" @@ -219,6 +220,63 @@ lit_charset_record_t::is_equal_utf8_string (const lit_utf8_byte_t *str, /**< str return get_length () == str_size; } /* lit_charset_record_t::equal_non_zt */ +/** + * Dump charset record to specified snapshot buffer + * + * @return number of bytes dumped, + * or 0 - upon dump failure + */ +uint32_t +lit_charset_record_t::dump_for_snapshot (uint8_t *buffer_p, /**< buffer to dump to */ + size_t buffer_size, /**< buffer size */ + size_t *in_out_buffer_offset_p) /**< in-out: buffer write offset */ +{ + lit_utf8_size_t length = get_length (); + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, length)) + { + return 0; + } + + rcs_record_iterator_t it_this (&lit_storage, this); + + it_this.skip (header_size ()); + + for (lit_utf8_size_t i = 0; i < length; i++) + { + lit_utf8_byte_t next_byte = it_this.read (); + it_this.skip (); + + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, next_byte)) + { + return 0; + } + } + + return ((uint32_t) sizeof (length) + length * (uint32_t) sizeof (lit_utf8_byte_t)); +} /* lit_charset_record_t::dump_for_snapshot */ + +/** + * Dump number record to specified snapshot buffer + * + * @return number of bytes dumped, + * or 0 - upon dump failure + */ +uint32_t +lit_number_record_t::dump_for_snapshot (uint8_t *buffer_p, /**< buffer to dump to */ + size_t buffer_size, /**< buffer size */ + size_t *in_out_buffer_offset_p) const /**< in-out: buffer write offset */ +{ + /* dumping as double (not ecma_number_t), because ecma_number_t can be float or double, + * depending on engine compile-time configuration */ + double num = get_number (); + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, num)) + { + return 0; + } + + return ((uint32_t) sizeof (num)); +} /* lit_number_record_t::dump_for_snapshot */ + /** * Create charset record in the literal storage * @@ -285,6 +343,29 @@ lit_literal_storage_t::create_number_record (ecma_number_t num) /**< number */ return ret; } /* lit_literal_storage_t::create_number_record */ +/** + * Count literal records in the storage + * + * @return number of literals + */ +uint32_t +lit_literal_storage_t::count_literals (void) +{ + uint32_t num = 0; + + 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 >= _first_type_id) + { + num++; + } + } + + return num; +} /* lit_literal_storage_t::count_literals */ + /** * Dump the contents of the literal storage */ @@ -460,6 +541,317 @@ lit_literal_storage_t::get_record_size (rcs_record_t* rec_p) /**< pointer to a r } } /* lit_literal_storage_t::get_record_size */ +#ifdef JERRY_ENABLE_SNAPSHOT +/** + * Dump literals to specified snapshot buffer + * + * @return true, if dump was performed successfully (i.e. buffer size is sufficient), + * false - otherwise. + */ +bool +lit_dump_literals_for_snapshot (uint8_t *buffer_p, /**< 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_num_p, /**< out: number of literals */ + uint32_t *out_lit_table_size_p) /**< out: number of bytes, dumped to snapshot buffer */ +{ + uint32_t literals_num = lit_storage.count_literals (); + uint32_t lit_table_size = 0; + + *out_map_p = NULL; + *out_map_num_p = 0; + *out_lit_table_size_p = 0; + + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, literals_num)) + { + return false; + } + lit_table_size += (uint32_t) sizeof (literals_num); + + if (literals_num != 0) + { + bool is_ok = true; + + size_t id_map_size = sizeof (lit_mem_to_snapshot_id_map_entry_t) * literals_num; + lit_mem_to_snapshot_id_map_entry_t *id_map_p; + id_map_p = (lit_mem_to_snapshot_id_map_entry_t *) mem_heap_alloc_block (id_map_size, MEM_HEAP_ALLOC_SHORT_TERM); + + uint32_t literal_index = 0; + + 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_literal_storage_t::LIT_TYPE_FIRST + && type <= lit_literal_storage_t::LIT_TYPE_LAST)) + { + continue; + } + + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, type)) + { + is_ok = false; + break; + } + + uint32_t sz; + + if (type == LIT_STR_T) + { + lit_charset_record_t *rec_p = static_cast (lit); + + sz = rec_p->dump_for_snapshot (buffer_p, buffer_size, in_out_buffer_offset_p); + } + else if (type == LIT_MAGIC_STR_T + || type == LIT_MAGIC_STR_EX_T) + { + lit_magic_record_t *rec_p = static_cast (lit); + + if (type == LIT_MAGIC_STR_T) + { + sz = rec_p->dump_for_snapshot (buffer_p, buffer_size, in_out_buffer_offset_p); + } + else + { + JERRY_ASSERT (type == LIT_MAGIC_STR_EX_T); + + sz = rec_p->dump_for_snapshot (buffer_p, buffer_size, in_out_buffer_offset_p); + } + } + else + { + JERRY_ASSERT (type == LIT_NUMBER_T); + + lit_number_record_t *rec_p = static_cast (lit); + + sz = rec_p->dump_for_snapshot (buffer_p, buffer_size, in_out_buffer_offset_p); + } + + if (sz == 0) + { + /* write failed */ + is_ok = false; + break; + } + else + { + lit_cpointer_t lit_cp = lit_cpointer_t::compress (lit); + id_map_p[literal_index].literal_id = lit_cp; + id_map_p[literal_index].literal_offset = lit_table_size; + + lit_table_size += (uint32_t) sizeof (type); + lit_table_size += sz; + } + + literal_index++; + } + + if (!is_ok) + { + mem_heap_free_block (id_map_p); + + return false; + } + else + { + JERRY_ASSERT (literal_index == literals_num); + + *out_map_p = id_map_p; + } + } + + uint32_t aligned_size = JERRY_ALIGNUP (lit_table_size, MEM_ALIGNMENT); + + if (aligned_size != lit_table_size) + { + JERRY_ASSERT (aligned_size > lit_table_size); + + uint8_t padding = 0; + uint32_t padding_bytes_num = (uint32_t) (aligned_size - lit_table_size); + + for (uint32_t i = 0; i < padding_bytes_num; i++) + { + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, padding)) + { + return false; + } + } + } + + *out_map_num_p = literals_num; + *out_lit_table_size_p = aligned_size; + + return true; +} /* lit_dump_literals_for_snapshot */ + +/** + * Load literals from snapshot + * + * @return true, if load was performed successfully (i.e. literals dump in the snapshot is consistent), + * false - otherwise (i.e. snapshot is incorrect). + */ +bool +lit_load_literals_from_snapshot (const uint8_t *lit_table_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_num_p, /**< out: literals number */ + bool is_copy) /** flag, indicating whether the passed in-snapshot data + * should be copied to engine's memory (true), + * or it can be referenced until engine is stopped + * (i.e. until call to jerry_cleanup) */ +{ + /* currently, always performing literals copy */ + (void) is_copy; + + *out_map_p = NULL; + *out_map_num_p = 0; + + size_t lit_table_read = 0; + + uint32_t literals_num; + if (!jrt_read_from_buffer_by_offset (lit_table_p, + lit_table_size, + &lit_table_read, + &literals_num)) + { + return false; + } + + if (literals_num == 0) + { + return true; + } + + size_t id_map_size = sizeof (lit_mem_to_snapshot_id_map_entry_t) * literals_num; + lit_mem_to_snapshot_id_map_entry_t *id_map_p; + id_map_p = (lit_mem_to_snapshot_id_map_entry_t *) mem_heap_alloc_block (id_map_size, MEM_HEAP_ALLOC_SHORT_TERM); + + bool is_ok = true; + + for (uint32_t lit_index = 0; lit_index < literals_num; lit_index++) + { + uint32_t offset = (uint32_t) lit_table_read; + JERRY_ASSERT (offset == lit_table_read); + + rcs_record_t::type_t type; + if (!jrt_read_from_buffer_by_offset (lit_table_p, + lit_table_size, + &lit_table_read, + &type)) + { + is_ok = false; + break; + } + + literal_t lit; + + if (type == LIT_STR_T) + { + lit_utf8_size_t length; + if (!jrt_read_from_buffer_by_offset (lit_table_p, + lit_table_size, + &lit_table_read, + &length) + || (lit_table_read + length > lit_table_size)) + { + is_ok = false; + break; + } + + lit = lit_find_or_create_literal_from_utf8_string (lit_table_p + lit_table_read, length); + lit_table_read += length; + } + else if (type == LIT_MAGIC_STR_T) + { + lit_magic_string_id_t id; + if (!jrt_read_from_buffer_by_offset (lit_table_p, + lit_table_size, + &lit_table_read, + &id)) + { + is_ok = false; + break; + } + + const lit_utf8_byte_t *magic_str_p = lit_get_magic_string_utf8 (id); + lit_utf8_size_t magic_str_sz = lit_get_magic_string_size (id); + + /* + * TODO: + * Consider searching literal storage by magic string identifier instead of by its value + */ + lit = lit_find_or_create_literal_from_utf8_string (magic_str_p, magic_str_sz); + } + else if (type == LIT_MAGIC_STR_EX_T) + { + lit_magic_string_ex_id_t id; + if (!jrt_read_from_buffer_by_offset (lit_table_p, + lit_table_size, + &lit_table_read, + &id)) + { + is_ok = false; + break; + } + + const lit_utf8_byte_t *magic_str_ex_p = lit_get_magic_string_ex_utf8 (id); + lit_utf8_size_t magic_str_ex_sz = lit_get_magic_string_ex_size (id); + + /* + * TODO: + * Consider searching literal storage by magic string identifier instead of by its value + */ + lit = lit_find_or_create_literal_from_utf8_string (magic_str_ex_p, magic_str_ex_sz); + } + else if (type == LIT_NUMBER_T) + { + double num; + if (!jrt_read_from_buffer_by_offset (lit_table_p, + lit_table_size, + &lit_table_read, + &num)) + { + is_ok = false; + break; + } + + lit = lit_find_or_create_literal_from_num ((ecma_number_t) num); + } + else + { + is_ok = false; + break; + } + + id_map_p[lit_index].literal_offset = offset; + id_map_p[lit_index].literal_id = lit_cpointer_t::compress (lit); + } + + if (is_ok) + { + *out_map_p = id_map_p; + *out_map_num_p = literals_num; + + return true; + } + else + { + mem_heap_free_block (id_map_p); + + return false; + } +} /* lit_load_literals_from_snapshot */ +#endif /* JERRY_ENABLE_SNAPSHOT */ + template void rcs_record_iterator_t::skip (); template void rcs_record_iterator_t::skip (); template void rcs_record_iterator_t::skip (); @@ -477,6 +869,8 @@ template lit_magic_string_id_t lit_magic_record_t::get_magic_str_id() const; template void lit_magic_record_t::set_magic_str_id(lit_magic_string_id_t); template void lit_magic_record_t::set_magic_str_id(lit_magic_string_ex_id_t); +template uint32_t lit_magic_record_t::dump_for_snapshot(uint8_t *, size_t, size_t *) const; +template uint32_t lit_magic_record_t::dump_for_snapshot(uint8_t *, size_t, size_t *) const; template lit_charset_record_t *rcs_recordset_t::alloc_record (rcs_record_t::type_t type, size_t size); diff --git a/jerry-core/lit/lit-literal-storage.h b/jerry-core/lit/lit-literal-storage.h index 1e9bfee43..c74117c00 100644 --- a/jerry-core/lit/lit-literal-storage.h +++ b/jerry-core/lit/lit-literal-storage.h @@ -133,6 +133,7 @@ public: bool is_equal (lit_charset_record_t *); bool is_equal_utf8_string (const lit_utf8_byte_t *, lit_utf8_size_t); + uint32_t dump_for_snapshot (uint8_t *buffer_p, size_t buffer_size, size_t *in_out_buffer_offset_p); private: /** * Set record's size (the value of the 'length' field in the header) @@ -244,6 +245,25 @@ public: return (magic_string_id_t) id; } /* get_magic_str_id */ + /** + * Dump magic string record to specified snapshot buffer + * + * @return number of bytes dumped, + * or 0 - upon dump failure + */ + template + uint32_t dump_for_snapshot (uint8_t *buffer_p, /**< buffer to dump to */ + size_t buffer_size, /**< buffer size */ + size_t *in_out_buffer_offset_p) const /**< in-out: buffer write offset */ + { + magic_string_id_t id = get_magic_str_id (); + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, id)) + { + return 0; + } + + return ((uint32_t) sizeof (id)); + } /* dump_for_snapshot */ private: /** * Set record's size (the value of the 'length' field in the header) @@ -377,6 +397,9 @@ public: return it.read (); } /* get_number */ + uint32_t dump_for_snapshot (uint8_t *buffer_p, + size_t buffer_size, + size_t *in_out_buffer_offset_p) const; private: /** * Set record's size (the value of the 'length' field in the header) @@ -410,10 +433,14 @@ class lit_literal_storage_t : public rcs_recordset_t public: enum { - LIT_STR = _first_type_id, + LIT_TYPE_FIRST = _first_type_id, + + LIT_STR = LIT_TYPE_FIRST, LIT_MAGIC_STR, LIT_MAGIC_STR_EX, - LIT_NUMBER + LIT_NUMBER, + + LIT_TYPE_LAST = LIT_NUMBER }; lit_charset_record_t *create_charset_record (const lit_utf8_byte_t *, lit_utf8_size_t); @@ -421,6 +448,8 @@ public: lit_magic_record_t *create_magic_record_ex (lit_magic_string_ex_id_t); lit_number_record_t *create_number_record (ecma_number_t); + uint32_t count_literals (void); + void dump (); private: @@ -434,4 +463,29 @@ private: #define LIT_MAGIC_STR_EX_T (lit_literal_storage_t::LIT_MAGIC_STR_EX) #define LIT_NUMBER_T (lit_literal_storage_t::LIT_NUMBER) +#ifdef JERRY_ENABLE_SNAPSHOT +/** + * Map from literal identifiers to the literal offsets in snapshot (or reverse) + */ +typedef struct +{ + lit_cpointer_t literal_id; /**< identifier of literal in literal storage */ + uint32_t literal_offset; /**< offset of the literal in snapshot */ +} lit_mem_to_snapshot_id_map_entry_t; + +extern bool +lit_dump_literals_for_snapshot (uint8_t *buffer_p, + size_t buffer_size, + size_t *in_out_buffer_offset_p, + lit_mem_to_snapshot_id_map_entry_t **out_map_p, + uint32_t *out_map_num_p, + uint32_t *out_lit_table_size_p); +extern bool lit_load_literals_from_snapshot (const uint8_t *lit_table_p, + uint32_t lit_table_size, + lit_mem_to_snapshot_id_map_entry_t **out_map_p, + uint32_t *out_map_num_p, + bool is_copy); +#endif /* JERRY_ENABLE_SNAPSHOT */ + + #endif /* LIT_LITERAL_STORAGE_H */ diff --git a/jerry-core/parser/js/collections/lit-id-hash-table.cpp b/jerry-core/parser/js/collections/lit-id-hash-table.cpp index 4a45b5ac7..f4c8bf297 100644 --- a/jerry-core/parser/js/collections/lit-id-hash-table.cpp +++ b/jerry-core/parser/js/collections/lit-id-hash-table.cpp @@ -14,6 +14,7 @@ */ #include "lit-id-hash-table.h" +#include "lit-literal-storage.h" #include "bytecode-data.h" /** \addtogroup jsparser ECMAScript parser @@ -122,6 +123,203 @@ lit_id_hash_table_lookup (lit_id_hash_table *table_p, /**< table's header */ return table_p->buckets[block_id][uid]; } /* lit_id_hash_table_lookup */ +/** + * Dump literal identifiers hash table to snapshot buffer + * + * @return number of bytes dumper - upon success, + * 0 - upon failure + */ +uint32_t +lit_id_hash_table_dump_for_snapshot (uint8_t *buffer_p, /**< buffer to dump to */ + size_t buffer_size, /**< buffer size */ + size_t *in_out_buffer_offset_p, /**< in-out: buffer write offset */ + lit_id_hash_table *table_p, /**< hash table to dump */ + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map from literal + * identifiers in + * literal storage + * to literal offsets + * in snapshot */ + uint32_t literals_num, /**< number of literals */ + vm_instr_counter_t instrs_num) /**< number of instructions in corresponding + * byte-code array */ +{ + size_t begin_offset = *in_out_buffer_offset_p; + + uint32_t idx_num_total = (uint32_t) table_p->current_bucket_pos; + JERRY_ASSERT (idx_num_total == table_p->current_bucket_pos); + + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, idx_num_total)) + { + return 0; + } + + size_t blocks_num = JERRY_ALIGNUP (instrs_num, BLOCK_SIZE) / BLOCK_SIZE; + + for (size_t block_index = 0, next_block_index; + block_index < blocks_num; + ) + { + uint32_t idx_num_in_block; + + next_block_index = block_index + 1u; + + while (next_block_index < blocks_num + && table_p->buckets[next_block_index] == NULL) + { + next_block_index++; + } + + if (next_block_index != blocks_num) + { + idx_num_in_block = (uint32_t) (table_p->buckets[next_block_index] - table_p->buckets[block_index]); + } + else if (table_p->buckets[block_index] != NULL) + { + idx_num_in_block = (uint32_t) (table_p->current_bucket_pos + - (size_t) (table_p->buckets[block_index] - table_p->buckets[0])); + } + else + { + idx_num_in_block = 0; + } + + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, idx_num_in_block)) + { + return 0; + } + + for (size_t block_idx_pair_index = 0; + block_idx_pair_index < idx_num_in_block; + block_idx_pair_index++) + { + lit_cpointer_t lit_cp = table_p->buckets[block_index][block_idx_pair_index]; + + uint32_t lit_index; + for (lit_index = 0; lit_index < literals_num; lit_index++) + { + if (lit_map_p[lit_index].literal_id.packed_value == lit_cp.packed_value) + { + break; + } + } + JERRY_ASSERT (lit_index < literals_num); + + uint32_t offset = lit_map_p[lit_index].literal_offset; + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, offset)) + { + return 0; + } + } + + while (++block_index < next_block_index) + { + idx_num_in_block = 0; + + if (!jrt_write_to_buffer_by_offset (buffer_p, buffer_size, in_out_buffer_offset_p, idx_num_in_block)) + { + return 0; + } + } + } + + size_t bytes_written = (*in_out_buffer_offset_p - begin_offset); + + JERRY_ASSERT (bytes_written == (uint32_t) bytes_written); + return (uint32_t) bytes_written; +} /* lit_id_hash_table_dump_for_snapshot */ + +/** + * Load literal identifiers hash table from specified snapshot buffer + * + * @return true - upon successful load (i.e. data in snapshot is consistent), + * false - upon failure (in case, snapshot is incorrect) + */ +bool +lit_id_hash_table_load_from_snapshot (size_t blocks_count, /**< number of byte-code blocks + * in corresponding byte-code array */ + uint32_t idx_num_total, /**< total number of (byte-code block, idx) + * pairs in snapshot */ + const uint8_t *idx_to_lit_map_p, /**< idx-to-lit map in snapshot */ + size_t idx_to_lit_map_size, /**< size of the map */ + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map of in-snapshot + * literal offsets + * to literal identifiers, + * created in literal + * storage */ + uint32_t literals_num, /**< number of literals in snapshot */ + uint8_t *buffer_for_hash_table_p, /**< buffer to initialize hash table in */ + size_t buffer_for_hash_table_size) /**< size of the buffer */ +{ + lit_id_hash_table *hash_table_p = lit_id_hash_table_init (buffer_for_hash_table_p, + buffer_for_hash_table_size, + idx_num_total, + blocks_count); + + size_t idx_to_lit_map_offset = 0; + uint32_t idx_num_counter = 0; + for (size_t block_idx = 0; block_idx < blocks_count; block_idx++) + { + uint32_t idx_num_in_block; + if (!jrt_read_from_buffer_by_offset (idx_to_lit_map_p, + idx_to_lit_map_size, + &idx_to_lit_map_offset, + &idx_num_in_block)) + { + return false; + } + + hash_table_p->buckets[block_idx] = hash_table_p->raw_buckets + hash_table_p->current_bucket_pos; + + if (idx_num_counter + idx_num_in_block < idx_num_counter) + { + return false; + } + idx_num_counter += idx_num_in_block; + + if (idx_num_counter > idx_num_total) + { + return false; + } + + for (uint32_t idx_in_block = 0; idx_in_block < idx_num_in_block; idx_in_block++) + { + uint32_t lit_offset_from_snapshot; + if (!jrt_read_from_buffer_by_offset (idx_to_lit_map_p, + idx_to_lit_map_size, + &idx_to_lit_map_offset, + &lit_offset_from_snapshot)) + { + return false; + } + + /** + * TODO: implement binary search here + */ + lit_cpointer_t lit_cp = lit_cpointer_t::null_cp (); + uint32_t i; + for (i = 0; i < literals_num; i++) + { + if (lit_map_p[i].literal_offset == lit_offset_from_snapshot) + { + lit_cp.packed_value = lit_map_p[i].literal_id.packed_value; + break; + } + } + + if (i == literals_num) + { + return false; + } + + JERRY_ASSERT (hash_table_p->current_bucket_pos < idx_num_total); + hash_table_p->raw_buckets[hash_table_p->current_bucket_pos++] = lit_cp; + } + } + + return true; +} /* lit_id_hash_table_load_from_snapshot */ + + /** * @} * @} diff --git a/jerry-core/parser/js/collections/lit-id-hash-table.h b/jerry-core/parser/js/collections/lit-id-hash-table.h index 996b11e33..620caf13a 100644 --- a/jerry-core/parser/js/collections/lit-id-hash-table.h +++ b/jerry-core/parser/js/collections/lit-id-hash-table.h @@ -33,5 +33,19 @@ size_t lit_id_hash_table_get_size_for_table (size_t, size_t); void lit_id_hash_table_free (lit_id_hash_table *); void lit_id_hash_table_insert (lit_id_hash_table *, vm_idx_t, vm_instr_counter_t, lit_cpointer_t); lit_cpointer_t lit_id_hash_table_lookup (lit_id_hash_table *, vm_idx_t, vm_instr_counter_t); - +uint32_t lit_id_hash_table_dump_for_snapshot (uint8_t *buffer_p, + size_t buffer_size, + size_t *in_out_buffer_offset_p, + lit_id_hash_table *table_p, + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, + uint32_t literals_num, + vm_instr_counter_t instrs_num); +bool lit_id_hash_table_load_from_snapshot (size_t blocks_count, + uint32_t idx_num_total, + const uint8_t *idx_to_lit_map_p, + size_t idx_to_lit_map_size, + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, + uint32_t literals_num, + uint8_t *buffer_for_hash_table_p, + size_t buffer_for_hash_table_size); #endif /* LIT_ID_HASH_TABLE */ diff --git a/jerry-core/parser/js/serializer.cpp b/jerry-core/parser/js/serializer.cpp index 8bd8af696..190f7fe18 100644 --- a/jerry-core/parser/js/serializer.cpp +++ b/jerry-core/parser/js/serializer.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "lit-id-hash-table.h" #include "serializer.h" #include "bytecode-data.h" #include "pretty-printer.h" @@ -343,3 +344,152 @@ serializer_free (void) mem_heap_free_block (header_p); } } + +#ifdef JERRY_ENABLE_SNAPSHOT +/** + * Dump byte-code and idx-to-literal map to snapshot + * + * @return true, upon success (i.e. buffer size is enough), + * false - otherwise. + */ +bool +serializer_dump_bytecode_with_idx_map (uint8_t *buffer_p, /**< buffer to dump to */ + size_t buffer_size, /**< buffer size */ + size_t *in_out_buffer_offset_p, /**< in-out: buffer write offset */ + const bytecode_data_header_t *bytecode_data_p, /**< byte-code data */ + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map from literal + * identifiers in + * literal storage + * to literal offsets + * in snapshot */ + uint32_t literals_num, /**< literals number */ + uint32_t *out_bytecode_size_p, /**< out: size of dumped instructions array */ + uint32_t *out_idx_to_lit_map_size_p) /**< out: side of dumped + * idx to literals map */ +{ + JERRY_ASSERT (bytecode_data_p->next_header_cp == MEM_CP_NULL); + + vm_instr_counter_t instrs_num = bytecode_data_p->instrs_count; + + const size_t instrs_array_size = sizeof (vm_instr_t) * instrs_num; + if (*in_out_buffer_offset_p + instrs_array_size > buffer_size) + { + return false; + } + memcpy (buffer_p + *in_out_buffer_offset_p, bytecode_data_p->instrs_p, instrs_array_size); + *in_out_buffer_offset_p += instrs_array_size; + + *out_bytecode_size_p = (uint32_t) (sizeof (vm_instr_t) * instrs_num); + + lit_id_hash_table *lit_id_hash_p = MEM_CP_GET_POINTER (lit_id_hash_table, bytecode_data_p->lit_id_hash_cp); + uint32_t idx_to_lit_map_size = lit_id_hash_table_dump_for_snapshot (buffer_p, + buffer_size, + in_out_buffer_offset_p, + lit_id_hash_p, + lit_map_p, + literals_num, + instrs_num); + + if (idx_to_lit_map_size == 0) + { + return false; + } + else + { + *out_idx_to_lit_map_size_p = idx_to_lit_map_size; + + return true; + } +} /* serializer_dump_bytecode_with_idx_map */ + +/** + * Register bytecode and idx map from snapshot + * + * NOTE: + * If is_copy flag is set, bytecode is copied from snapshot, else bytecode is referenced directly + * from snapshot + * + * @return pointer to byte-code header, upon success, + * NULL - upon failure (i.e., in case snapshot format is not valid) + */ +const bytecode_data_header_t * +serializer_load_bytecode_with_idx_map (const uint8_t *bytecode_and_idx_map_p, /**< buffer with instructions array + * and idx to literals map from + * snapshot */ + uint32_t bytecode_size, /**< size of instructions array */ + uint32_t idx_to_lit_map_size, /**< size of the idx to literals map */ + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< map of in-snapshot + * literal offsets + * to literal identifiers, + * created in literal + * storage */ + uint32_t literals_num, /**< number of literals */ + bool is_copy) /** flag, indicating whether the passed in-snapshot data + * should be copied to engine's memory (true), + * or it can be referenced until engine is stopped + * (i.e. until call to jerry_cleanup) */ +{ + const uint8_t *idx_to_lit_map_p = bytecode_and_idx_map_p + bytecode_size; + + size_t instructions_number = bytecode_size / sizeof (vm_instr_t); + size_t blocks_count = JERRY_ALIGNUP (instructions_number, BLOCK_SIZE) / BLOCK_SIZE; + + uint32_t idx_num_total; + size_t idx_to_lit_map_offset = 0; + if (!jrt_read_from_buffer_by_offset (idx_to_lit_map_p, + idx_to_lit_map_size, + &idx_to_lit_map_offset, + &idx_num_total)) + { + return NULL; + } + + const size_t bytecode_alloc_size = JERRY_ALIGNUP (bytecode_size, MEM_ALIGNMENT); + const size_t hash_table_size = lit_id_hash_table_get_size_for_table (idx_num_total, blocks_count); + const size_t header_and_hash_table_size = JERRY_ALIGNUP (sizeof (bytecode_data_header_t) + hash_table_size, + MEM_ALIGNMENT); + const size_t alloc_size = header_and_hash_table_size + (is_copy ? bytecode_alloc_size : 0); + + uint8_t *buffer_p = (uint8_t*) mem_heap_alloc_block (alloc_size, MEM_HEAP_ALLOC_LONG_TERM); + bytecode_data_header_t *header_p = (bytecode_data_header_t *) buffer_p; + + vm_instr_t *instrs_p; + vm_instr_t *snapshot_instrs_p = (vm_instr_t *) bytecode_and_idx_map_p; + if (is_copy) + { + instrs_p = (vm_instr_t *) (buffer_p + header_and_hash_table_size); + memcpy (instrs_p, snapshot_instrs_p, bytecode_size); + } + else + { + instrs_p = snapshot_instrs_p; + } + + uint8_t *lit_id_hash_table_buffer_p = buffer_p + sizeof (bytecode_data_header_t); + if (lit_id_hash_table_load_from_snapshot (blocks_count, + idx_num_total, + idx_to_lit_map_p + idx_to_lit_map_offset, + idx_to_lit_map_size - idx_to_lit_map_offset, + lit_map_p, + literals_num, + lit_id_hash_table_buffer_p, + hash_table_size) + && (vm_instr_counter_t) instructions_number == instructions_number) + { + MEM_CP_SET_NON_NULL_POINTER (header_p->lit_id_hash_cp, lit_id_hash_table_buffer_p); + header_p->instrs_p = instrs_p; + header_p->instrs_count = (vm_instr_counter_t) instructions_number; + MEM_CP_SET_POINTER (header_p->next_header_cp, first_bytecode_header_p); + + first_bytecode_header_p = header_p; + + return header_p; + } + else + { + mem_heap_free_block (buffer_p); + return NULL; + } +} /* serializer_load_bytecode_with_idx_map */ + +#endif /* JERRY_ENABLE_SNAPSHOT */ diff --git a/jerry-core/parser/js/serializer.h b/jerry-core/parser/js/serializer.h index 3e6f1fa88..78941088a 100644 --- a/jerry-core/parser/js/serializer.h +++ b/jerry-core/parser/js/serializer.h @@ -44,4 +44,26 @@ void serializer_rewrite_op_meta (vm_instr_counter_t, op_meta); void serializer_remove_bytecode_data (const bytecode_data_header_t *); void serializer_free (void); -#endif // SERIALIZER_H +#ifdef JERRY_ENABLE_SNAPSHOT +/* + * Snapshot-related + */ +bool serializer_dump_bytecode_with_idx_map (uint8_t *buffer_p, + size_t buffer_size, + size_t *in_out_buffer_offset_p, + const bytecode_data_header_t *bytecode_data_p, + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, + uint32_t literals_num, + uint32_t *out_bytecode_size_p, + uint32_t *out_idx_to_lit_map_size_p); + +const bytecode_data_header_t * +serializer_load_bytecode_with_idx_map (const uint8_t *bytecode_and_idx_map_p, + uint32_t bytecode_size, + uint32_t idx_to_lit_map_size, + const lit_mem_to_snapshot_id_map_entry_t *lit_map_p, + uint32_t literals_num, + bool is_copy); +#endif /* JERRY_ENABLE_SNAPSHOT */ + +#endif /* SERIALIZER_H */ diff --git a/jerry-core/vm/opcodes-helpers-variables.cpp b/jerry-core/vm/opcodes-helpers-variables.cpp index b14238fb9..1ffefe8af 100644 --- a/jerry-core/vm/opcodes-helpers-variables.cpp +++ b/jerry-core/vm/opcodes-helpers-variables.cpp @@ -14,7 +14,6 @@ */ #include "opcodes-ecma-support.h" -#include "opcodes.h" #ifndef JERRY_NDEBUG /** diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 9dfb07991..94aca89f3 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -446,7 +446,7 @@ vm_run_global (void) */ ecma_completion_value_t vm_run_eval (const bytecode_data_header_t *bytecode_data_p, /**< byte-code data header */ - bool is_direct) /**< is eval called directly? */ + bool is_direct) /**< is eval called in direct mode? */ { vm_instr_counter_t first_instr_index = 0u; opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (bytecode_data_p->instrs_p, diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index ab6df091c..bda4dc2e5 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -16,7 +16,6 @@ #ifndef VM_H #define VM_H -#include "bytecode-data.h" #include "ecma-globals.h" #include "jrt.h" #include "opcodes.h" diff --git a/main-linux.cpp b/main-linux.cpp index a5f741887..e25ea99c5 100644 --- a/main-linux.cpp +++ b/main-linux.cpp @@ -26,9 +26,9 @@ #define JERRY_MAX_COMMAND_LINE_ARGS (64) /** - * Maximum size of source code buffer + * Maximum size of source code / snapshots buffer */ -#define JERRY_SOURCE_BUFFER_SIZE (1048576) +#define JERRY_BUFFER_SIZE (1048576) /** * Standalone Jerry exit codes @@ -36,7 +36,7 @@ #define JERRY_STANDALONE_EXIT_CODE_OK (0) #define JERRY_STANDALONE_EXIT_CODE_FAIL (1) -static uint8_t source_buffer[ JERRY_SOURCE_BUFFER_SIZE ]; +static uint8_t buffer[ JERRY_BUFFER_SIZE ]; static const jerry_api_char_t * read_sources (const char *script_file_names[], @@ -44,7 +44,7 @@ read_sources (const char *script_file_names[], size_t *out_source_size_p) { int i; - uint8_t *source_buffer_tail = source_buffer; + uint8_t *source_buffer_tail = buffer; for (i = 0; i < files_count; i++) { @@ -76,7 +76,7 @@ read_sources (const char *script_file_names[], const size_t current_source_size = (size_t)script_len; - if (source_buffer_tail + current_source_size >= source_buffer + sizeof (source_buffer)) + if (source_buffer_tail + current_source_size >= buffer + sizeof (buffer)) { fclose (file); break; @@ -102,14 +102,67 @@ read_sources (const char *script_file_names[], } else { - const size_t source_size = (size_t) (source_buffer_tail - source_buffer); + const size_t source_size = (size_t) (source_buffer_tail - buffer); *out_source_size_p = source_size; - return (const jerry_api_char_t *) source_buffer; + return (const jerry_api_char_t *) buffer; } } +static bool +read_snapshot (const char *snapshot_file_name_p, + size_t *out_snapshot_size_p) +{ + JERRY_ASSERT (snapshot_file_name_p != NULL); + JERRY_ASSERT (out_snapshot_size_p != NULL); + + *out_snapshot_size_p = 0; + + FILE *file = fopen (snapshot_file_name_p, "r"); + + if (file == NULL) + { + return false; + } + + int fseek_status = fseek (file, 0, SEEK_END); + + if (fseek_status != 0) + { + fclose (file); + return false; + } + + long snapshot_len = ftell (file); + + if (snapshot_len < 0) + { + fclose (file); + return false; + } + + rewind (file); + + if ((size_t) snapshot_len > sizeof (buffer)) + { + fclose (file); + return false; + } + + size_t bytes_read = fread (buffer, 1u, (size_t) snapshot_len, file); + if (bytes_read < (size_t) snapshot_len) + { + fclose (file); + return false; + } + + *out_snapshot_size_p = (size_t) snapshot_len; + + fclose (file); + return true; +} /* read_snapshot */ + /** * Provide the 'assert' implementation for the engine. * @@ -158,6 +211,13 @@ main (int argc, jerry_flag_t flags = JERRY_FLAG_EMPTY; + const char *exec_snapshot_file_names[JERRY_MAX_COMMAND_LINE_ARGS]; + int exec_snapshots_count = 0; + + bool is_dump_snapshot_mode = false; + bool is_dump_snapshot_mode_for_global_or_eval = false; + const char *dump_snapshot_file_name_p = NULL; + #ifdef JERRY_ENABLE_LOG const char *log_file_name = NULL; #endif /* JERRY_ENABLE_LOG */ @@ -190,6 +250,38 @@ main (int argc, { flags |= JERRY_FLAG_SHOW_OPCODES; } + else if (!strcmp ("--dump-snapshot-for-global", argv[i]) + || !strcmp ("--dump-snapshot-for-eval", argv[i])) + { + is_dump_snapshot_mode = true; + is_dump_snapshot_mode_for_global_or_eval = !strcmp ("--dump-snapshot-for-global", argv[i]); + + flags |= JERRY_FLAG_PARSE_ONLY; + + if (dump_snapshot_file_name_p == NULL + && ++i < argc) + { + dump_snapshot_file_name_p = argv[i]; + } + else + { + JERRY_ERROR_MSG ("Error: wrong format of the arguments\n"); + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + } + else if (!strcmp ("--exec-snapshot", argv[i])) + { + if (++i < argc) + { + JERRY_ASSERT (exec_snapshots_count < JERRY_MAX_COMMAND_LINE_ARGS); + exec_snapshot_file_names[exec_snapshots_count++] = argv[i]; + } + else + { + JERRY_ERROR_MSG ("Error: wrong format of the arguments\n"); + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + } else if (!strcmp ("--log-level", argv[i])) { flags |= JERRY_FLAG_ENABLE_LOG; @@ -230,90 +322,162 @@ main (int argc, } } - if (files_counter == 0) + if (is_dump_snapshot_mode) + { + if (files_counter == 0) + { + JERRY_ERROR_MSG ("--dump-snapshot argument is passed, but no script was specified on command line\n"); + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + + if (exec_snapshots_count != 0) + { + JERRY_ERROR_MSG ("--dump-snapshot and --exec-snapshot options can't be passed simultaneously\n"); + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + } + + if (files_counter == 0 + && exec_snapshots_count == 0) { return JERRY_STANDALONE_EXIT_CODE_OK; } else { - size_t source_size; - const jerry_api_char_t *source_p = read_sources (file_names, files_counter, &source_size); - - if (source_p == NULL) +#ifdef JERRY_ENABLE_LOG + if (log_file_name) { - return JERRY_STANDALONE_EXIT_CODE_FAIL; + jerry_log_file = fopen (log_file_name, "w"); + if (jerry_log_file == NULL) + { + JERRY_ERROR_MSG ("Failed to open log file: %s\n", log_file_name); + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } } else { -#ifdef JERRY_ENABLE_LOG - if (log_file_name) - { - jerry_log_file = fopen (log_file_name, "w"); - if (jerry_log_file == NULL) - { - JERRY_ERROR_MSG ("Failed to open log file: %s\n", log_file_name); - return JERRY_STANDALONE_EXIT_CODE_FAIL; - } - } - else - { - jerry_log_file = stdout; - } + jerry_log_file = stdout; + } #endif /* JERRY_ENABLE_LOG */ - jerry_init (flags); + jerry_init (flags); - jerry_api_object_t *global_obj_p = jerry_api_get_global (); - jerry_api_object_t *assert_func_p = jerry_api_create_external_function (assert_handler); - jerry_api_value_t assert_value; - assert_value.type = JERRY_API_DATA_TYPE_OBJECT; - assert_value.v_object = assert_func_p; + jerry_api_object_t *global_obj_p = jerry_api_get_global (); + jerry_api_object_t *assert_func_p = jerry_api_create_external_function (assert_handler); + jerry_api_value_t assert_value; + assert_value.type = JERRY_API_DATA_TYPE_OBJECT; + assert_value.v_object = assert_func_p; - bool is_assert_added = jerry_api_set_object_field_value (global_obj_p, - (jerry_api_char_t *) "assert", - &assert_value); + bool is_assert_added = jerry_api_set_object_field_value (global_obj_p, + (jerry_api_char_t *) "assert", + &assert_value); - jerry_api_release_value (&assert_value); - jerry_api_release_object (global_obj_p); + jerry_api_release_value (&assert_value); + jerry_api_release_object (global_obj_p); - if (!is_assert_added) + if (!is_assert_added) + { + JERRY_ERROR_MSG ("Failed to register 'assert' method."); + } + + jerry_completion_code_t ret_code = JERRY_COMPLETION_CODE_OK; + + bool is_ok = true; + for (int i = 0; i < exec_snapshots_count; i++) + { + size_t snapshot_size; + + if (!read_snapshot (exec_snapshot_file_names[i], &snapshot_size)) { - JERRY_ERROR_MSG ("Failed to register 'assert' method."); - } - - jerry_completion_code_t ret_code = JERRY_COMPLETION_CODE_OK; - - if (!jerry_parse (source_p, source_size)) - { - /* unhandled SyntaxError */ ret_code = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION; } else { - if ((flags & JERRY_FLAG_PARSE_ONLY) == 0) + jerry_api_value_t ret_value; + ret_code = jerry_exec_snapshot ((void *) buffer, + snapshot_size, + true, + &ret_value); + JERRY_ASSERT (ret_value.type == JERRY_API_DATA_TYPE_UNDEFINED); + } + + if (ret_code != JERRY_COMPLETION_CODE_OK) + { + is_ok = false; + + break; + } + } + + if (is_ok) + { + size_t source_size; + const jerry_api_char_t *source_p = NULL; + + if (files_counter != 0) + { + source_p = read_sources (file_names, files_counter, &source_size); + + if (source_p == NULL) { - ret_code = jerry_run (); + return JERRY_STANDALONE_EXIT_CODE_FAIL; } } - jerry_cleanup (); + if (source_p != NULL) + { + if (is_dump_snapshot_mode) + { + static uint8_t snapshot_dump_buffer[ JERRY_BUFFER_SIZE ]; + + size_t snapshot_size = jerry_parse_and_save_snapshot (source_p, + source_size, + is_dump_snapshot_mode_for_global_or_eval, + snapshot_dump_buffer, + JERRY_BUFFER_SIZE); + if (snapshot_size == 0) + { + ret_code = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION; + } + else + { + FILE *snapshot_file_p = fopen (dump_snapshot_file_name_p, "w"); + fwrite (snapshot_dump_buffer, sizeof (uint8_t), snapshot_size, snapshot_file_p); + fclose (snapshot_file_p); + } + } + else + { + if (!jerry_parse (source_p, source_size)) + { + /* unhandled SyntaxError */ + ret_code = JERRY_COMPLETION_CODE_UNHANDLED_EXCEPTION; + } + else if ((flags & JERRY_FLAG_PARSE_ONLY) == 0) + { + ret_code = jerry_run (); + } + } + } + } + + jerry_cleanup (); #ifdef JERRY_ENABLE_LOG - if (jerry_log_file && jerry_log_file != stdout) - { - fclose (jerry_log_file); - jerry_log_file = NULL; - } + if (jerry_log_file && jerry_log_file != stdout) + { + fclose (jerry_log_file); + jerry_log_file = NULL; + } #endif /* JERRY_ENABLE_LOG */ - if (ret_code == JERRY_COMPLETION_CODE_OK) - { - return JERRY_STANDALONE_EXIT_CODE_OK; - } - else - { - return JERRY_STANDALONE_EXIT_CODE_FAIL; - } + if (ret_code == JERRY_COMPLETION_CODE_OK) + { + return JERRY_STANDALONE_EXIT_CODE_OK; + } + else + { + return JERRY_STANDALONE_EXIT_CODE_FAIL; } } } diff --git a/tests/unit/test-api.cpp b/tests/unit/test-api.cpp index d1906fe9b..948cd50be 100644 --- a/tests/unit/test-api.cpp +++ b/tests/unit/test-api.cpp @@ -577,5 +577,56 @@ main (void) jerry_cleanup (); + // Dump / execute snapshot + static uint8_t global_mode_snapshot_buffer[1024]; + static uint8_t eval_mode_snapshot_buffer[1024]; + + const char *code_to_snapshot_p = "(function () { return 'string from snapshot'; }) ();"; + + jerry_init (JERRY_FLAG_SHOW_OPCODES); + size_t global_mode_snapshot_size = jerry_parse_and_save_snapshot ((jerry_api_char_t *) code_to_snapshot_p, + strlen (code_to_snapshot_p), + true, + global_mode_snapshot_buffer, + sizeof (global_mode_snapshot_buffer)); + JERRY_ASSERT (global_mode_snapshot_size != 0); + jerry_cleanup (); + + jerry_init (JERRY_FLAG_SHOW_OPCODES); + size_t eval_mode_snapshot_size = jerry_parse_and_save_snapshot ((jerry_api_char_t *) code_to_snapshot_p, + strlen (code_to_snapshot_p), + false, + eval_mode_snapshot_buffer, + sizeof (eval_mode_snapshot_buffer)); + JERRY_ASSERT (eval_mode_snapshot_size != 0); + jerry_cleanup (); + + jerry_init (JERRY_FLAG_SHOW_OPCODES); + + is_ok = (jerry_exec_snapshot (global_mode_snapshot_buffer, + global_mode_snapshot_size, + false, + &res) == JERRY_COMPLETION_CODE_OK); + + JERRY_ASSERT (is_ok + && res.type == JERRY_API_DATA_TYPE_UNDEFINED); + jerry_api_release_value (&res); + + is_ok = (jerry_exec_snapshot (eval_mode_snapshot_buffer, + eval_mode_snapshot_size, + false, + &res) == JERRY_COMPLETION_CODE_OK); + + JERRY_ASSERT (is_ok + && res.type == JERRY_API_DATA_TYPE_STRING); + sz = jerry_api_string_to_char_buffer (res.v_string, NULL, 0); + JERRY_ASSERT (sz == -20); + sz = jerry_api_string_to_char_buffer (res.v_string, (jerry_api_char_t *) buffer, -sz); + JERRY_ASSERT (sz == 20); + jerry_api_release_value (&res); + JERRY_ASSERT (!strncmp (buffer, "string from snapshot", (size_t) sz)); + + jerry_cleanup (); + return 0; }