diff options
-rw-r--r-- | CMakeLists.txt | 22 | ||||
-rw-r--r-- | docs/02.API-REFERENCE.md | 79 | ||||
-rw-r--r-- | jerry-core/CMakeLists.txt | 8 | ||||
-rw-r--r-- | jerry-core/api/jerry-snapshot.c | 461 | ||||
-rw-r--r-- | jerry-core/api/jerry-snapshot.h | 37 | ||||
-rw-r--r-- | jerry-core/ecma/base/ecma-literal-storage.c | 157 | ||||
-rw-r--r-- | jerry-core/ecma/base/ecma-literal-storage.h | 16 | ||||
-rw-r--r-- | jerry-core/include/jerryscript-snapshot.h | 4 | ||||
-rw-r--r-- | jerry-main/CMakeLists.txt | 5 | ||||
-rw-r--r-- | jerry-main/cli.c | 188 | ||||
-rw-r--r-- | jerry-main/cli.h | 11 | ||||
-rw-r--r-- | jerry-main/main-unix-snapshot.c | 308 | ||||
-rw-r--r-- | jerry-main/main-unix.c | 29 | ||||
-rw-r--r-- | tests/unit-core/test-api.c | 122 | ||||
-rw-r--r-- | tests/unit-core/test-snapshot.c | 216 | ||||
-rwxr-xr-x | tools/build.py | 3 | ||||
-rwxr-xr-x | tools/run-tests.py | 2 |
17 files changed, 1243 insertions, 425 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index bb47fbd7..32f9f89d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,14 +38,15 @@ if(NOT CMAKE_BUILD_TYPE) endif() # Optional components -set(JERRY_CMDLINE ON CACHE BOOL "Build jerry command line tool?") -set(JERRY_CMDLINE_MINIMAL OFF CACHE BOOL "Build jerry minimal command line tool?") -set(JERRY_PORT_DEFAULT ON CACHE BOOL "Build default jerry port implementation?") -set(JERRY_EXT ON CACHE BOOL "Build jerry-ext?") -set(JERRY_LIBC ON CACHE BOOL "Build and use jerry-libc?") -set(JERRY_LIBM ON CACHE BOOL "Build and use jerry-libm?") -set(UNITTESTS OFF CACHE BOOL "Build unit tests?") -set(DOCTESTS OFF CACHE BOOL "Build doc tests?") +set(JERRY_CMDLINE ON CACHE BOOL "Build jerry command line tool?") +set(JERRY_CMDLINE_MINIMAL OFF CACHE BOOL "Build jerry minimal command line tool?") +set(JERRY_CMDLINE_SNAPSHOT OFF CACHE BOOL "Build jerry snapshot command line tool?") +set(JERRY_PORT_DEFAULT ON CACHE BOOL "Build default jerry port implementation?") +set(JERRY_EXT ON CACHE BOOL "Build jerry-ext?") +set(JERRY_LIBC ON CACHE BOOL "Build and use jerry-libc?") +set(JERRY_LIBM ON CACHE BOOL "Build and use jerry-libm?") +set(UNITTESTS OFF CACHE BOOL "Build unit tests?") +set(DOCTESTS OFF CACHE BOOL "Build doc tests?") # Optional build settings set(ENABLE_ALL_IN_ONE OFF CACHE BOOL "Enable all-in-one build?") @@ -57,7 +58,7 @@ set(ENABLE_STRIP ON CACHE BOOL "Enable stripping all symbols from release set(FEATURE_INIT_FINI OFF CACHE BOOL "Enable init/fini arrays?") # Option overrides -if(JERRY_CMDLINE OR JERRY_CMDLINE_MINIMAL OR UNITTESTS OR DOCTESTS) +if(JERRY_CMDLINE OR JERRY_CMDLINE_MINIMAL OR JERRY_CMDLINE_SNAPSHOT OR UNITTESTS OR DOCTESTS) set(JERRY_PORT_DEFAULT ON) set(JERRY_PORT_DEFAULT_MESSAGE " (FORCED BY CMDLINE OR TESTS)") @@ -103,6 +104,7 @@ message(STATUS "ENABLE_STATIC_LINK " ${ENABLE_STATIC_LINK} ${ENABLE_STATI message(STATUS "ENABLE_STRIP " ${ENABLE_STRIP} ${ENABLE_STRIP_MESSAGE}) message(STATUS "JERRY_CMDLINE " ${JERRY_CMDLINE}) message(STATUS "JERRY_CMDLINE_MINIMAL " ${JERRY_CMDLINE_MINIMAL}) +message(STATUS "JERRY_CMDLINE_SNAPSHOT " ${JERRY_CMDLINE_SNAPSHOT}) message(STATUS "JERRY_PORT_DEFAULT " ${JERRY_PORT_DEFAULT} ${JERRY_PORT_DEFAULT_MESSAGE}) message(STATUS "JERRY_EXT " ${JERRY_EXT} ${JERRY_EXT_MESSAGE}) message(STATUS "JERRY_LIBC " ${JERRY_LIBC} ${JERRY_LIBC_MESSAGE}) @@ -268,7 +270,7 @@ if(JERRY_EXT) endif() # Jerry command line tool -if(JERRY_CMDLINE OR JERRY_CMDLINE_MINIMAL) +if(JERRY_CMDLINE OR JERRY_CMDLINE_MINIMAL OR JERRY_CMDLINE_SNAPSHOT) add_subdirectory(jerry-main) endif() diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 9113a02a..0babd353 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -4236,6 +4236,85 @@ main (void) - [jerry_init](#jerry_init) - [jerry_cleanup](#jerry_cleanup) +- [jerry_exec_snapshot_at](#jerry_exec_snapshot_at) +- [jerry_parse_and_save_snapshot](#jerry_parse_and_save_snapshot) + + +## jerry_exec_snapshot_at + +**Summary** + +Execute the selected snapshot function from the specified buffer. + +Same function as [jerry_exec_snapshot](#jerry_exec_snapshot) except +the executed function index can be specified. + +*Note*: Returned value must be freed with [jerry_release_value](#jerry_release_value) when it +is no longer needed. + +**Prototype** + +```c +jerry_value_t +jerry_exec_snapshot_at (const uint32_t *snapshot_p, + size_t snapshot_size, + size_t func_index, + bool copy_bytecode); +``` + +- `snapshot_p` - pointer to snapshot +- `snapshot_size` - size of snapshot +- `func_index` - index of executed function +- `copy_bytecode` - flag, indicating whether the passed snapshot buffer should be copied to the + engine's memory. If set the engine should not reference the buffer after the function returns + (in this case, the passed buffer could be freed after the call). Otherwise (if the flag is not + set) - the buffer could only be freed after the engine stops (i.e. after call to jerry_cleanup). +- return value + - result of bytecode, if run was successful + - thrown error, otherwise + +**Example** + +[doctest]: # () + +```c +#include <string.h> +#include "jerryscript.h" + +int +main (void) +{ + static uint32_t global_mode_snapshot_buffer[256]; + const jerry_char_t *code_to_snapshot_p = (const jerry_char_t *) "(function () { return 'string from snapshot'; }) ();"; + + jerry_init (JERRY_INIT_EMPTY); + size_t global_mode_snapshot_size = jerry_parse_and_save_snapshot (code_to_snapshot_p, + strlen ((const char *) code_to_snapshot_p), + true, + false, + global_mode_snapshot_buffer, + sizeof (global_mode_snapshot_buffer) / sizeof (uint32_t)); + jerry_cleanup (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t res = jerry_exec_snapshot_at (global_mode_snapshot_buffer, + global_mode_snapshot_size, + 0, + false); + + jerry_release_value (res); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_init](#jerry_init) +- [jerry_cleanup](#jerry_cleanup) +- [jerry_exec_snapshot](#jerry_exec_snapshot) - [jerry_parse_and_save_snapshot](#jerry_parse_and_save_snapshot) diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index 8294148c..6bd36d46 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -50,6 +50,12 @@ if(NOT FEATURE_JS_PARSER) set(FEATURE_PARSER_DUMP_MESSAGE " (FORCED BY DISABLED JS PARSER)") endif() +if(JERRY_CMDLINE_SNAPSHOT) + set(FEATURE_SNAPSHOT_SAVE ON) + + set(FEATURE_SNAPSHOT_SAVE_MESSAGE " (FORCED BY SNAPSHOT TOOL)") +endif() + # Status messages message(STATUS "FEATURE_CPOINTER_32_BIT " ${FEATURE_CPOINTER_32_BIT} ${FEATURE_CPOINTER_32_BIT_MESSAGE}) message(STATUS "FEATURE_DEBUGGER " ${FEATURE_DEBUGGER}) @@ -62,7 +68,7 @@ message(STATUS "FEATURE_PARSER_DUMP " ${FEATURE_PARSER_DUMP} ${FEATURE_PAR message(STATUS "FEATURE_PROFILE " ${FEATURE_PROFILE}) message(STATUS "FEATURE_REGEXP_DUMP " ${FEATURE_REGEXP_DUMP}) message(STATUS "FEATURE_SNAPSHOT_EXEC " ${FEATURE_SNAPSHOT_EXEC} ${FEATURE_SNAPSHOT_EXEC_MESSAGE}) -message(STATUS "FEATURE_SNAPSHOT_SAVE " ${FEATURE_SNAPSHOT_SAVE}) +message(STATUS "FEATURE_SNAPSHOT_SAVE " ${FEATURE_SNAPSHOT_SAVE} ${FEATURE_SNAPSHOT_SAVE_MESSAGE}) message(STATUS "FEATURE_SYSTEM_ALLOCATOR " ${FEATURE_SYSTEM_ALLOCATOR}) message(STATUS "FEATURE_VALGRIND " ${FEATURE_VALGRIND}) message(STATUS "FEATURE_VALGRIND_FREYA " ${FEATURE_VALGRIND_FREYA}) diff --git a/jerry-core/api/jerry-snapshot.c b/jerry-core/api/jerry-snapshot.c index 9d305b4d..81799ce1 100644 --- a/jerry-core/api/jerry-snapshot.c +++ b/jerry-core/api/jerry-snapshot.c @@ -24,6 +24,47 @@ #include "lit-char-helpers.h" #include "re-compiler.h" +#if defined JERRY_ENABLE_SNAPSHOT_SAVE || defined JERRY_ENABLE_SNAPSHOT_EXEC + +/** + * Get snapshot configuration flags. + * + * @return configuration flags + */ +static inline uint32_t __attr_always_inline___ +snapshot_get_global_flags (bool has_regex) /**< regex literal is present */ +{ + JERRY_UNUSED (has_regex); + + uint32_t flags = 0; + +#ifdef JERRY_CPOINTER_32_BIT + flags |= JERRY_SNAPSHOT_FOUR_BYTE_CPOINTER; +#endif /* JERRY_CPOINTER_32_BIT */ +#ifndef CONFIG_DISABLE_REGEXP_BUILTIN + flags |= (has_regex ? JERRY_SNAPSHOT_HAS_REGEX_LITERAL : 0); +#endif /* CONFIG_DISABLE_REGEXP_BUILTIN */ + + return flags; +} /* snapshot_get_global_flags */ + +/** + * Checks whether the global_flags argument matches to the current feature set. + * + * @return true if global_flags accepted, false otherwise + */ +static inline bool __attr_always_inline___ +snapshot_check_global_flags (uint32_t global_flags) /**< global flags */ +{ +#ifndef CONFIG_DISABLE_REGEXP_BUILTIN + global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_REGEX_LITERAL; +#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */ + + return global_flags == snapshot_get_global_flags (false); +} /* snapshot_check_global_flags */ + +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE || JERRY_ENABLE_SNAPSHOT_EXEC */ + #ifdef JERRY_ENABLE_SNAPSHOT_SAVE /** @@ -31,8 +72,9 @@ */ typedef struct { - bool snapshot_error_occured; size_t snapshot_buffer_write_offset; + bool snapshot_error_occured; + bool regex_found; } snapshot_globals_t; /** \addtogroup jerrysnapshot Jerry snapshot operations @@ -91,7 +133,10 @@ snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled return 0; } - uint16_t start_offset = (uint16_t) (globals_p->snapshot_buffer_write_offset >> JMEM_ALIGNMENT_LOG); + /* The snapshot generator always parses a single file, + * so the base always starts right after the snapshot header. */ + size_t buffer_offset = globals_p->snapshot_buffer_write_offset - sizeof (jerry_snapshot_header_t); + uint16_t start_offset = (uint16_t) (buffer_offset >> JMEM_ALIGNMENT_LOG); uint8_t *copied_code_start_p = snapshot_buffer_p + globals_p->snapshot_buffer_write_offset; ecma_compiled_code_t *copied_code_p = (ecma_compiled_code_t *) copied_code_start_p; @@ -129,6 +174,7 @@ snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled ECMA_FINALIZE_UTF8_STRING (buffer_p, buffer_size); + globals_p->regex_found = true; globals_p->snapshot_buffer_write_offset = JERRY_ALIGNUP (globals_p->snapshot_buffer_write_offset, JMEM_ALIGNMENT); @@ -252,10 +298,10 @@ jerry_snapshot_set_offsets (uint32_t *buffer_p, /**< buffer */ { for (uint32_t i = 0; i < argument_end; i++) { - lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; - if (literal_start_p[i] != JMEM_CP_NULL) { + lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; + while (current_p->literal_id != literal_start_p[i]) { current_p++; @@ -316,12 +362,14 @@ jerry_snapshot_set_offsets (uint32_t *buffer_p, /**< buffer */ * @return byte code */ static ecma_compiled_code_t * -snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data */ +snapshot_load_compiled_code (const uint8_t *base_addr_p, /**< base address of the + * current primary function */ size_t offset, /**< byte code offset */ - lit_mem_to_snapshot_id_map_entry_t *lit_map_p, /**< literal map */ + const uint8_t *literal_base_p, /**< literal start */ + const uint8_t *number_base_p, /**< literal number start */ bool copy_bytecode) /**< byte code should be copied to memory */ { - ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) (snapshot_data_p + offset); + ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) (base_addr_p + offset); uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION)) @@ -377,7 +425,7 @@ snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data jmem_stats_allocate_byte_code_bytes (code_size); #endif /* JMEM_STATS */ - memcpy (bytecode_p, snapshot_data_p + offset, code_size); + memcpy (bytecode_p, base_addr_p + offset, code_size); } else { @@ -392,7 +440,7 @@ snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data jmem_stats_allocate_byte_code_bytes (total_size); #endif /* JMEM_STATS */ - memcpy (bytecode_p, snapshot_data_p + offset, code_size); + memcpy (bytecode_p, base_addr_p + offset, code_size); bytecode_p->size = (uint16_t) (total_size >> JMEM_ALIGNMENT_LOG); @@ -412,17 +460,9 @@ snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data for (uint32_t i = 0; i < const_literal_end; i++) { - lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; - - if (literal_start_p[i] != 0) - { - while (current_p->literal_offset != literal_start_p[i]) - { - current_p++; - } - - literal_start_p[i] = current_p->literal_id; - } + literal_start_p[i] = ecma_snapshot_get_literal (literal_base_p, + number_base_p, + literal_start_p[i]); } for (uint32_t i = const_literal_end; i < literal_end; i++) @@ -438,9 +478,10 @@ snapshot_load_compiled_code (const uint8_t *snapshot_data_p, /**< snapshot data else { ecma_compiled_code_t *literal_bytecode_p; - literal_bytecode_p = snapshot_load_compiled_code (snapshot_data_p, + literal_bytecode_p = snapshot_load_compiled_code (base_addr_p, literal_offset, - lit_map_p, + literal_base_p, + number_base_p, copy_bytecode); ECMA_SET_NON_NULL_POINTER (literal_start_p[i], @@ -474,10 +515,12 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source snapshot_globals_t globals; ecma_value_t parse_status; ecma_compiled_code_t *bytecode_data_p; + const uint32_t aligned_header_size = JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), + JMEM_ALIGNMENT); - globals.snapshot_buffer_write_offset = JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), - JMEM_ALIGNMENT); + globals.snapshot_buffer_write_offset = aligned_header_size; globals.snapshot_error_occured = false; + globals.regex_found = false; parse_status = parser_parse_script (NULL, 0, @@ -500,9 +543,17 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source } jerry_snapshot_header_t header; + header.magic = JERRY_SNAPSHOT_MAGIC; header.version = JERRY_SNAPSHOT_VERSION; + header.global_flags = snapshot_get_global_flags (globals.regex_found); header.lit_table_offset = (uint32_t) globals.snapshot_buffer_write_offset; - header.is_run_global = is_for_global; + header.number_of_funcs = 1; + header.func_offsets[0] = aligned_header_size; + + if (!is_for_global) + { + header.func_offsets[0] |= JERRY_SNAPSHOT_EVAL_CONTEXT; + } lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL; uint32_t literals_num; @@ -511,16 +562,14 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source buffer_size, &globals.snapshot_buffer_write_offset, &lit_map_p, - &literals_num, - &header.lit_table_size)) + &literals_num)) { JERRY_ASSERT (lit_map_p == NULL); return 0; } - jerry_snapshot_set_offsets (buffer_p + (JERRY_ALIGNUP (sizeof (jerry_snapshot_header_t), - JMEM_ALIGNMENT) / sizeof (uint32_t)), - (uint32_t) (header.lit_table_offset - sizeof (jerry_snapshot_header_t)), + jerry_snapshot_set_offsets (buffer_p + (aligned_header_size / sizeof (uint32_t)), + (uint32_t) (header.lit_table_offset - aligned_header_size), lit_map_p); size_t header_offset = 0; @@ -561,20 +610,21 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source * thrown error - otherwise */ jerry_value_t -jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ - size_t snapshot_size, /**< size of snapshot */ - bool copy_bytecode) /**< flag, indicating whether the passed snapshot - * buffer should be copied to the engine's memory. - * If set the engine should not reference the buffer - * after the function returns (in this case, the passed - * buffer could be freed after the call). - * Otherwise (if the flag is not set) - the buffer could only be - * freed after the engine stops (i.e. after call to jerry_cleanup). */ +jerry_exec_snapshot_at (const uint32_t *snapshot_p, /**< snapshot */ + size_t snapshot_size, /**< size of snapshot */ + size_t func_index, /**< index of primary function */ + bool copy_bytecode) /**< flag, indicating whether the passed snapshot + * buffer should be copied to the engine's memory. + * If set the engine should not reference the buffer + * after the function returns (in this case, the passed + * buffer could be freed after the call). + * Otherwise (if the flag is not set) - the buffer could only be + * freed after the engine stops (i.e. after call to jerry_cleanup). */ { #ifdef JERRY_ENABLE_SNAPSHOT_EXEC JERRY_ASSERT (snapshot_p != NULL); - static const char * const invalid_version_error_p = "Invalid snapshot version"; + static const char * const invalid_version_error_p = "Invalid snapshot version or unsupported features present"; static const char * const invalid_format_error_p = "Invalid snapshot format"; const uint8_t *snapshot_data_p = (uint8_t *) snapshot_p; @@ -585,39 +635,39 @@ jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) snapshot_data_p; - if (header_p->version != JERRY_SNAPSHOT_VERSION) + if (header_p->magic != JERRY_SNAPSHOT_MAGIC + || header_p->version != JERRY_SNAPSHOT_VERSION + || !snapshot_check_global_flags (header_p->global_flags)) { return ecma_raise_type_error (invalid_version_error_p); } - lit_mem_to_snapshot_id_map_entry_t *lit_map_p = NULL; - uint32_t literals_num; - if (header_p->lit_table_offset >= snapshot_size) { return ecma_raise_type_error (invalid_version_error_p); } - JERRY_ASSERT ((header_p->lit_table_offset % sizeof (uint32_t)) == 0); - if (!ecma_load_literals_from_snapshot ((uint32_t *) (snapshot_data_p + header_p->lit_table_offset), - header_p->lit_table_size, - &lit_map_p, - &literals_num)) + if (func_index >= header_p->number_of_funcs) { - JERRY_ASSERT (lit_map_p == NULL); - return ecma_raise_type_error (invalid_format_error_p); + return ecma_raise_range_error (ECMA_ERR_MSG ("Function index is higher than maximum")); } + JERRY_ASSERT ((header_p->lit_table_offset % sizeof (uint32_t)) == 0); + + const uint8_t *literal_base_p; + const uint8_t *number_base_p; + + literal_base_p = ecma_snapshot_get_literals_base ((uint32_t *) (snapshot_data_p + header_p->lit_table_offset), + &number_base_p); + ecma_compiled_code_t *bytecode_p; - bytecode_p = snapshot_load_compiled_code (snapshot_data_p, - sizeof (jerry_snapshot_header_t), - lit_map_p, - copy_bytecode); - if (lit_map_p != NULL) - { - jmem_heap_free_block (lit_map_p, literals_num * sizeof (lit_mem_to_snapshot_id_map_entry_t)); - } + uint32_t func_offset = header_p->func_offsets[func_index] & ~JERRY_SNAPSHOT_EVAL_CONTEXT; + bytecode_p = snapshot_load_compiled_code (snapshot_data_p + func_offset, + 0, + literal_base_p, + number_base_p, + copy_bytecode); if (bytecode_p == NULL) { @@ -626,20 +676,52 @@ jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ ecma_value_t ret_val; - if (header_p->is_run_global) + if (header_p->func_offsets[func_index] & JERRY_SNAPSHOT_EVAL_CONTEXT) { - ret_val = vm_run_global (bytecode_p); - ecma_bytecode_deref (bytecode_p); + ret_val = vm_run_eval (bytecode_p, false); } else { - ret_val = vm_run_eval (bytecode_p, false); + ret_val = vm_run_global (bytecode_p); + ecma_bytecode_deref (bytecode_p); } return ret_val; #else /* !JERRY_ENABLE_SNAPSHOT_EXEC */ JERRY_UNUSED (snapshot_p); JERRY_UNUSED (snapshot_size); + JERRY_UNUSED (func_index); + JERRY_UNUSED (copy_bytecode); + + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_FALSE); +#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ +} /* jerry_exec_snapshot_at */ + +/** + * Execute snapshot from specified buffer + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return result of bytecode - if run was successful + * thrown error - otherwise + */ +jerry_value_t +jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ + size_t snapshot_size, /**< size of snapshot */ + bool copy_bytecode) /**< flag, indicating whether the passed snapshot + * buffer should be copied to the engine's memory. + * If set the engine should not reference the buffer + * after the function returns (in this case, the passed + * buffer could be freed after the call). + * Otherwise (if the flag is not set) - the buffer could only be + * freed after the engine stops (i.e. after call to jerry_cleanup). */ +{ +#ifdef JERRY_ENABLE_SNAPSHOT_EXEC + return jerry_exec_snapshot_at (snapshot_p, snapshot_size, 0, copy_bytecode); +#else /* !JERRY_ENABLE_SNAPSHOT_EXEC */ + JERRY_UNUSED (snapshot_p); + JERRY_UNUSED (snapshot_size); JERRY_UNUSED (copy_bytecode); return ecma_make_simple_value (ECMA_SIMPLE_VALUE_FALSE); @@ -653,6 +735,259 @@ jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ #ifdef JERRY_ENABLE_SNAPSHOT_SAVE /** + * Collect all literals from a snapshot file. + */ +static void +scan_snapshot_functions (const uint8_t *buffer_p, /**< snapshot buffer start */ + const uint8_t *buffer_end_p, /**< snapshot buffer end */ + const uint8_t *literal_base_p, /**< start of literal data */ + const uint8_t *number_base_p) /**< start of number data */ +{ + JERRY_ASSERT (buffer_end_p > buffer_p); + + do + { + ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p; + uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION) + { + jmem_cpointer_t *literal_start_p; + uint32_t const_literal_end; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + literal_start_p = (jmem_cpointer_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); + + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; + const_literal_end = args_p->const_literal_end; + } + else + { + literal_start_p = (jmem_cpointer_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); + + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; + const_literal_end = args_p->const_literal_end; + } + + for (uint32_t i = 0; i < const_literal_end; i++) + { + if (literal_start_p[i] != JMEM_CP_NULL) + { + literal_start_p[i] = ecma_snapshot_get_literal (literal_base_p, number_base_p, literal_start_p[i]); + } + } + } + + buffer_p += code_size; + } + while (buffer_p < buffer_end_p); +} /* scan_snapshot_functions */ + +/** + * Update all literal offsets in a snapshot data. + */ +static void +update_literal_offsets (uint8_t *buffer_p, /**< snapshot buffer start */ + const uint8_t *buffer_end_p, /**< snapshot buffer start */ + lit_mem_to_snapshot_id_map_entry_t *lit_map_p) /**< literal map */ +{ + JERRY_ASSERT (buffer_end_p > buffer_p); + + do + { + ecma_compiled_code_t *bytecode_p = (ecma_compiled_code_t *) buffer_p; + uint32_t code_size = ((uint32_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_FUNCTION) + { + jmem_cpointer_t *literal_start_p; + uint32_t const_literal_end; + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + literal_start_p = (jmem_cpointer_t *) (buffer_p + sizeof (cbc_uint16_arguments_t)); + + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; + const_literal_end = args_p->const_literal_end; + } + else + { + literal_start_p = (jmem_cpointer_t *) (buffer_p + sizeof (cbc_uint8_arguments_t)); + + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; + const_literal_end = args_p->const_literal_end; + } + + for (uint32_t i = 0; i < const_literal_end; i++) + { + if (literal_start_p[i] != JMEM_CP_NULL) + { + lit_mem_to_snapshot_id_map_entry_t *current_p = lit_map_p; + + while (current_p->literal_id != literal_start_p[i]) + { + current_p++; + } + + literal_start_p[i] = current_p->literal_offset; + } + } + } + + buffer_p += code_size; + } + while (buffer_p < buffer_end_p); +} /* update_literal_offsets */ + +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ + +/** + * Merge multiple snapshots into a single buffer + * + * @return length of merged snapshot file + * 0 on error + */ +size_t +jerry_merge_snapshots (const uint32_t **inp_buffers_p, /**< array of (pointers to start of) input buffers */ + size_t *inp_buffer_sizes_p, /**< array of input buffer sizes */ + size_t number_of_snapshots, /**< number of snapshots */ + uint32_t *out_buffer_p, /**< output buffer */ + size_t out_buffer_size, /**< output buffer size */ + const char **error_p) /**< error description */ +{ +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + uint32_t number_of_funcs = 0; + uint32_t merged_global_flags = 0; + size_t functions_size = sizeof (jerry_snapshot_header_t); + + if (number_of_snapshots < 2) + { + *error_p = "at least two snapshots must be passed"; + return 0; + } + + for (uint32_t i = 0; i < number_of_snapshots; i++) + { + if (inp_buffer_sizes_p[i] < sizeof (jerry_snapshot_header_t)) + { + *error_p = "invalid snapshot file"; + return 0; + } + + const jerry_snapshot_header_t *header_p = (const jerry_snapshot_header_t *) inp_buffers_p[i]; + + if (header_p->magic != JERRY_SNAPSHOT_MAGIC + || header_p->version != JERRY_SNAPSHOT_VERSION + || !snapshot_check_global_flags (header_p->global_flags)) + { + *error_p = "invalid snapshot version or unsupported features present"; + return 0; + } + + merged_global_flags |= header_p->global_flags; + + uint32_t start_offset = header_p->func_offsets[0] & ~JERRY_SNAPSHOT_EVAL_CONTEXT; + const uint8_t *data_p = (const uint8_t *) inp_buffers_p[i]; + const uint8_t *literal_base_p; + const uint8_t *number_base_p; + + literal_base_p = ecma_snapshot_get_literals_base ((uint32_t *) (data_p + header_p->lit_table_offset), + &number_base_p); + + JERRY_ASSERT (header_p->number_of_funcs > 0); + + number_of_funcs += header_p->number_of_funcs; + functions_size += header_p->lit_table_offset - start_offset; + + scan_snapshot_functions (data_p + start_offset, + data_p + header_p->lit_table_offset, + literal_base_p, + number_base_p); + } + + JERRY_ASSERT (number_of_funcs > 0); + + functions_size += JERRY_ALIGNUP ((number_of_funcs - 1) * sizeof (uint32_t), JMEM_ALIGNMENT); + + if (functions_size >= out_buffer_size) + { + *error_p = "output buffer is too small"; + return 0; + } + + jerry_snapshot_header_t *header_p = (jerry_snapshot_header_t *) out_buffer_p; + + header_p->magic = JERRY_SNAPSHOT_MAGIC; + header_p->version = JERRY_SNAPSHOT_VERSION; + header_p->global_flags = merged_global_flags; + header_p->lit_table_offset = (uint32_t) functions_size; + header_p->number_of_funcs = number_of_funcs; + + lit_mem_to_snapshot_id_map_entry_t *lit_map_p; + uint32_t literals_num; + + if (!ecma_save_literals_for_snapshot (out_buffer_p, + out_buffer_size, + &functions_size, + &lit_map_p, + &literals_num)) + { + *error_p = "buffer is too small"; + return 0; + } + + uint32_t *func_offset_p = header_p->func_offsets; + uint8_t *dst_p = ((uint8_t *) out_buffer_p) + sizeof (jerry_snapshot_header_t); + dst_p += JERRY_ALIGNUP ((number_of_funcs - 1) * sizeof (uint32_t), JMEM_ALIGNMENT); + + for (uint32_t i = 0; i < number_of_snapshots; i++) + { + const jerry_snapshot_header_t *current_header_p = (const jerry_snapshot_header_t *) inp_buffers_p[i]; + + uint32_t start_offset = current_header_p->func_offsets[0] & ~JERRY_SNAPSHOT_EVAL_CONTEXT; + + memcpy (dst_p, + ((const uint8_t *) inp_buffers_p[i]) + start_offset, + current_header_p->lit_table_offset - start_offset); + + update_literal_offsets (dst_p, + dst_p + current_header_p->lit_table_offset - start_offset, + lit_map_p); + + uint32_t current_offset = (uint32_t) (dst_p - (uint8_t *) out_buffer_p) - start_offset; + + for (uint32_t j = 0; j < current_header_p->number_of_funcs; j++) + { + /* Updating offset without changing any flags. */ + *func_offset_p++ = current_header_p->func_offsets[j] + current_offset; + } + + dst_p += current_header_p->lit_table_offset - start_offset; + } + + JERRY_ASSERT ((uint32_t) (dst_p - (uint8_t *) out_buffer_p) == header_p->lit_table_offset); + + jmem_heap_free_block (lit_map_p, literals_num * sizeof (lit_mem_to_snapshot_id_map_entry_t)); + + *error_p = NULL; + return functions_size; +#else /* !JERRY_ENABLE_SNAPSHOT_SAVE */ + JERRY_UNUSED (inp_buffers_p); + JERRY_UNUSED (inp_buffer_sizes_p); + JERRY_UNUSED (number_of_snapshots); + JERRY_UNUSED (out_buffer_p); + JERRY_UNUSED (out_buffer_size); + JERRY_UNUSED (error_p); + + *error_p = "snapshot merge not supported"; + return 0; +#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ +} /* jerry_merge_snapshots */ + +#ifdef JERRY_ENABLE_SNAPSHOT_SAVE + +/** * ====================== Functions for literal saving ========================== */ diff --git a/jerry-core/api/jerry-snapshot.h b/jerry-core/api/jerry-snapshot.h index 58555ddd..8a91a43a 100644 --- a/jerry-core/api/jerry-snapshot.h +++ b/jerry-core/api/jerry-snapshot.h @@ -24,18 +24,39 @@ typedef struct { /* The size of this structure is recommended to be divisible by - * JMEM_ALIGNMENT. Otherwise some bytes after the header are wasted. */ + * uint32_t alignment. Otherwise some bytes after the header are wasted. */ + uint32_t magic; /**< four byte magic number */ uint32_t version; /**< version number */ - uint32_t lit_table_offset; /**< offset of the literal table */ - uint32_t lit_table_size; /**< size of literal table */ - uint32_t is_run_global; /**< flag, indicating whether the snapshot - * was saved as 'Global scope'-mode code (true) - * or as eval-mode code (false) */ + uint32_t global_flags; /**< global configuration and feature flags */ + uint32_t lit_table_offset; /**< byte offset of the literal table */ + uint32_t number_of_funcs; /**< number of primary ECMAScript functions */ + uint32_t func_offsets[1]; /**< function offsets (lowest bit: global(0) or eval(1) context) */ } jerry_snapshot_header_t; /** - * Jerry snapshot format version + * Evaluate this function on the top of the scope chain. */ -#define JERRY_SNAPSHOT_VERSION (7u) +#define JERRY_SNAPSHOT_EVAL_CONTEXT 0x1u + +/** + * Jerry snapshot magic marker. + */ +#define JERRY_SNAPSHOT_MAGIC (0x5952524Au) + +/** + * Jerry snapshot format version. + */ +#define JERRY_SNAPSHOT_VERSION (8u) + +/** + * Snapshot configuration flags. + */ +typedef enum +{ + /* 8 bits are reserved for dynamic features */ + JERRY_SNAPSHOT_HAS_REGEX_LITERAL = (1u << 0), /**< byte code has regex literal */ + /* 24 bits are reserved for compile time features */ + JERRY_SNAPSHOT_FOUR_BYTE_CPOINTER = (1u << 8) /**< compressed pointers are four byte long */ +} jerry_snapshot_global_flags_t; #endif /* !JERRY_SNAPSHOT_H */ diff --git a/jerry-core/ecma/base/ecma-literal-storage.c b/jerry-core/ecma/base/ecma-literal-storage.c index c0d40e20..e1627f0a 100644 --- a/jerry-core/ecma/base/ecma-literal-storage.c +++ b/jerry-core/ecma/base/ecma-literal-storage.c @@ -233,13 +233,11 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot lit_mem_to_snapshot_id_map_entry_t **out_map_p, /**< [out] map from literal identifiers * to the literal offsets * in snapshot */ - uint32_t *out_map_len_p, /**< [out] number of literals */ - uint32_t *out_lit_table_size_p) /**< [out] number of bytes, saved to snapshot buffer */ + uint32_t *out_map_len_p) /**< [out] number of literals */ { /* Count literals and literal space. */ - uint32_t string_count = 0; - uint32_t number_count = 0; - uint32_t lit_table_size = 2 * sizeof (uint32_t); + uint32_t lit_table_size = sizeof (uint32_t); + uint32_t total_count = 0; ecma_lit_storage_item_t *string_list_p = JERRY_CONTEXT (string_list_first_p); @@ -254,13 +252,15 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot lit_table_size += (uint32_t) JERRY_ALIGNUP (sizeof (uint16_t) + ecma_string_get_size (string_p), JERRY_SNAPSHOT_LITERAL_ALIGNMENT); - string_count++; + total_count++; } } string_list_p = JMEM_CP_GET_POINTER (ecma_lit_storage_item_t, string_list_p->next_cp); } + uint32_t number_offset = lit_table_size; + ecma_lit_storage_item_t *number_list_p = JERRY_CONTEXT (number_list_first_p); while (number_list_p != NULL) @@ -270,7 +270,7 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot if (number_list_p->values[i] != JMEM_CP_NULL) { lit_table_size += (uint32_t) sizeof (ecma_number_t); - number_count++; + total_count++; } } @@ -289,7 +289,6 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot return false; } - uint32_t total_count = string_count + number_count; lit_mem_to_snapshot_id_map_entry_t *map_p; map_p = jmem_heap_alloc_block (total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); @@ -300,7 +299,6 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot *in_out_buffer_offset_p += lit_table_size; *out_map_p = map_p; *out_map_len_p = total_count; - *out_lit_table_size_p = lit_table_size; /* Write data into the buffer. */ @@ -308,9 +306,7 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot * constant so the first literal must have offset one. */ uint32_t literal_offset = JERRY_SNAPSHOT_LITERAL_ALIGNMENT; - buffer_p[0] = string_count; - buffer_p[1] = number_count; - buffer_p += 2; + *buffer_p++ = number_offset; string_list_p = JERRY_CONTEXT (string_list_first_p); @@ -383,137 +379,52 @@ ecma_save_literals_for_snapshot (uint32_t *buffer_p, /**< [out] output snapshot #endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ -#ifdef JERRY_ENABLE_SNAPSHOT_EXEC +#if defined JERRY_ENABLE_SNAPSHOT_EXEC || defined JERRY_ENABLE_SNAPSHOT_SAVE /** - * Helper function for ecma_load_literals_from_snapshot. - * - * Note: always inline because it is used only once. + * Computes the base pointer of the literals and starting offset of numbers. * - * @return true - if load was performed successfully - * false - otherwise (i.e. buffer length is incorrect) + * @return the base pointer of the literals */ -static inline bool __attr_always_inline___ -ecma_load_literals_from_buffer (const uint16_t *buffer_p, /**< buffer with literal table in snapshot */ - uint32_t lit_table_size, /**< size of literal table in snapshot */ - lit_mem_to_snapshot_id_map_entry_t *map_p, /**< literal map */ - uint32_t string_count, /**< number of strings */ - uint32_t number_count) /**< number of numbers */ +const uint8_t * +ecma_snapshot_get_literals_base (uint32_t *buffer_p, /**< literal buffer start */ + const uint8_t **number_base_p) /**< [out] literal number start */ { - /* The zero value is reserved for NULL (no literal) - * constant so the first literal must have offset one. */ - uint32_t literal_offset = JERRY_SNAPSHOT_LITERAL_ALIGNMENT; - - /* Load strings first. */ - while (string_count > 0) - { - if (lit_table_size < literal_offset + sizeof (uint32_t)) - { - /* Buffer is not sufficent. */ - return false; - } - - lit_utf8_size_t length = *buffer_p; - lit_utf8_size_t aligned_length = JERRY_ALIGNUP (sizeof (uint16_t) + length, - JERRY_SNAPSHOT_LITERAL_ALIGNMENT); + *number_base_p = ((uint8_t *) buffer_p) + buffer_p[0]; - if (lit_table_size < literal_offset + aligned_length) - { - /* Buffer is not sufficent. */ - return false; - } - - map_p->literal_id = ecma_find_or_create_literal_string (((lit_utf8_byte_t *) buffer_p) + sizeof (uint16_t), length); - map_p->literal_offset = (jmem_cpointer_t) (literal_offset >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); - map_p++; - - JERRY_ASSERT ((aligned_length % sizeof (uint16_t)) == 0); - buffer_p += aligned_length / sizeof (uint16_t); - literal_offset += aligned_length; - - string_count--; - } - - /* Load numbers. */ - while (number_count > 0) - { - if (lit_table_size < literal_offset + sizeof (ecma_number_t)) - { - /* Buffer is not sufficent. */ - return false; - } - - ecma_number_t num; - memcpy (&num, buffer_p, sizeof (ecma_number_t)); - - map_p->literal_id = ecma_find_or_create_literal_number (num); - map_p->literal_offset = (jmem_cpointer_t) (literal_offset >> JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); - map_p++; - - ecma_length_t length = JERRY_ALIGNUP (sizeof (ecma_number_t), - JERRY_SNAPSHOT_LITERAL_ALIGNMENT); - - JERRY_ASSERT ((length % sizeof (uint16_t)) == 0); - buffer_p += length / sizeof (uint16_t); - literal_offset += length; - - number_count--; - } - - return (lit_table_size == (literal_offset + 2 * sizeof (uint32_t) - JERRY_SNAPSHOT_LITERAL_ALIGNMENT)); -} /* ecma_load_literals_from_buffer */ + return ((uint8_t *) (buffer_p + 1)) - JERRY_SNAPSHOT_LITERAL_ALIGNMENT; +} /* ecma_snapshot_get_literals_base */ /** - * Load literals from snapshot. + * Get the compressed pointer of a given literal. * - * @return true - if load was performed successfully (i.e. literals saved in the snapshot are consistent), - * false - otherwise (i.e. snapshot is incorrect) + * @return literal compressed pointer */ -bool -ecma_load_literals_from_snapshot (const uint32_t *buffer_p, /**< buffer with literal table in snapshot */ - uint32_t lit_table_size, /**< size of literal table in snapshot */ - lit_mem_to_snapshot_id_map_entry_t **out_map_p, /**< [out] map from literal offsets - * in snapshot to identifiers - * of loaded literals in literal - * storage */ - uint32_t *out_map_len_p) /**< [out] literals number */ +jmem_cpointer_t +ecma_snapshot_get_literal (const uint8_t *literal_base_p, /**< literal start */ + const uint8_t *number_base_p, /**< literal number start */ + jmem_cpointer_t offset) { - *out_map_p = NULL; - - if (lit_table_size < 2 * sizeof (uint32_t)) + if (offset == 0) { - /* Buffer is not sufficent. */ - return false; + return ECMA_NULL_POINTER; } - uint32_t string_count = buffer_p[0]; - uint32_t number_count = buffer_p[1]; - buffer_p += 2; + const uint8_t *literal_p = literal_base_p + (((size_t) offset) << JERRY_SNAPSHOT_LITERAL_ALIGNMENT_LOG); - uint32_t total_count = string_count + number_count; - lit_mem_to_snapshot_id_map_entry_t *map_p; - - *out_map_len_p = total_count; - - if (total_count == 0) + if (literal_p >= number_base_p) { - return true; + ecma_number_t num; + memcpy (&num, literal_p, sizeof (ecma_number_t)); + return ecma_find_or_create_literal_number (num); } - map_p = jmem_heap_alloc_block (total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); - *out_map_p = map_p; - - if (ecma_load_literals_from_buffer ((uint16_t *) buffer_p, lit_table_size, map_p, string_count, number_count)) - { - return true; - } + uint16_t length = *(const uint16_t *) literal_p; - jmem_heap_free_block (map_p, total_count * sizeof (lit_mem_to_snapshot_id_map_entry_t)); - *out_map_p = NULL; - return false; -} /* ecma_load_literals_from_snapshot */ + return ecma_find_or_create_literal_string (literal_p + sizeof (uint16_t), length); +} /* ecma_snapshot_get_literal */ -#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ +#endif /* JERRY_ENABLE_SNAPSHOT_EXEC || JERRY_ENABLE_SNAPSHOT_SAVE */ /** * @} diff --git a/jerry-core/ecma/base/ecma-literal-storage.h b/jerry-core/ecma/base/ecma-literal-storage.h index e9e1ec66..ec646910 100644 --- a/jerry-core/ecma/base/ecma-literal-storage.h +++ b/jerry-core/ecma/base/ecma-literal-storage.h @@ -43,15 +43,17 @@ jmem_cpointer_t ecma_find_or_create_literal_number (ecma_number_t number_arg); #ifdef JERRY_ENABLE_SNAPSHOT_SAVE bool -ecma_save_literals_for_snapshot (uint32_t *, size_t, size_t *, - lit_mem_to_snapshot_id_map_entry_t **, uint32_t *, uint32_t *); +ecma_save_literals_for_snapshot (uint32_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_len_p); #endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ -#ifdef JERRY_ENABLE_SNAPSHOT_EXEC -bool -ecma_load_literals_from_snapshot (const uint32_t *, uint32_t, - lit_mem_to_snapshot_id_map_entry_t **, uint32_t *); -#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ +#if defined JERRY_ENABLE_SNAPSHOT_EXEC || defined JERRY_ENABLE_SNAPSHOT_SAVE +const uint8_t * +ecma_snapshot_get_literals_base (uint32_t *buffer_p, const uint8_t **number_base_p); +jmem_cpointer_t +ecma_snapshot_get_literal (const uint8_t *literal_base_p, const uint8_t *number_base_p, + jmem_cpointer_t offset); +#endif /* JERRY_ENABLE_SNAPSHOT_EXEC || JERRY_ENABLE_SNAPSHOT_SAVE */ /** * @} diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 62aa8a8d..6dac088d 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -33,6 +33,10 @@ extern "C" size_t jerry_parse_and_save_snapshot (const jerry_char_t *source_p, size_t source_size, bool is_for_global, bool is_strict, uint32_t *buffer_p, size_t buffer_size); jerry_value_t jerry_exec_snapshot (const uint32_t *snapshot_p, size_t snapshot_size, bool copy_bytecode); +jerry_value_t jerry_exec_snapshot_at (const uint32_t *snapshot_p, size_t snapshot_size, + size_t func_index, bool copy_bytecode); +size_t jerry_merge_snapshots (const uint32_t **inp_buffers_p, size_t *inp_buffer_sizes_p, size_t number_of_snapshots, + uint32_t *out_buffer_p, size_t out_buffer_size, const char **error_p); size_t jerry_parse_and_save_literals (const jerry_char_t *source_p, size_t source_size, bool is_strict, uint32_t *buffer_p, size_t buffer_size, bool is_c_format); diff --git a/jerry-main/CMakeLists.txt b/jerry-main/CMakeLists.txt index d37b1dc5..bffe1f63 100644 --- a/jerry-main/CMakeLists.txt +++ b/jerry-main/CMakeLists.txt @@ -68,3 +68,8 @@ if(JERRY_CMDLINE_MINIMAL) jerry_create_executable("jerry-minimal" "main-unix-minimal.c") target_link_libraries("jerry-minimal" jerry-port-default-minimal) endif() + +if(JERRY_CMDLINE_SNAPSHOT) + jerry_create_executable("jerry-snapshot" "main-unix-snapshot.c" "cli.c") + target_link_libraries("jerry-snapshot" jerry-port-default) +endif() diff --git a/jerry-main/cli.c b/jerry-main/cli.c index 1bf41f3c..77a7c755 100644 --- a/jerry-main/cli.c +++ b/jerry-main/cli.c @@ -62,7 +62,7 @@ * @return the state that should be passed to other cli_ functions. */ cli_state_t -cli_init (const cli_opt_t *options, /**< array of option definitions, terminated by CLI_OPT_DEFAULT */ +cli_init (const cli_opt_t *options_p, /**< array of option definitions, terminated by CLI_OPT_DEFAULT */ int argc, /**< number of command line arguments */ char **argv) /**< array of command line arguments */ { @@ -72,35 +72,45 @@ cli_init (const cli_opt_t *options, /**< array of option definitions, terminated .arg = NULL, .argc = argc, .argv = argv, - .opts = options + .opts = options_p }; } /* cli_init */ /** + * Use another option list. + */ +void +cli_change_opts (cli_state_t *state_p, /**< state of the command line option processor */ + const cli_opt_t *options_p) /**< array of option definitions, terminated by CLI_OPT_DEFAULT */ +{ + state_p->opts = options_p; +} /* cli_change_opts */ + +/** * Checks whether the current argument is an option. * * Note: - * The state->error is not NULL on error and it contains the error message. + * The state_p->error is not NULL on error and it contains the error message. * * @return the ID of the option that was found or a CLI_OPT_ constant otherwise. */ int -cli_consume_option (cli_state_t *state) /**< state of the command line option processor */ +cli_consume_option (cli_state_t *state_p) /**< state of the command line option processor */ { - if (state->error != NULL) + if (state_p->error != NULL) { return CLI_OPT_END; } - if (state->argc <= 0) + if (state_p->argc <= 0) { - state->arg = NULL; + state_p->arg = NULL; return CLI_OPT_END; } - const char *arg = state->argv[0]; + const char *arg = state_p->argv[0]; - state->arg = arg; + state_p->arg = arg; if (arg[0] != '-') { @@ -111,33 +121,33 @@ cli_consume_option (cli_state_t *state) /**< state of the command line option pr { arg += 2; - for (const cli_opt_t *opt = state->opts; opt->id != CLI_OPT_DEFAULT; opt++) + for (const cli_opt_t *opt = state_p->opts; opt->id != CLI_OPT_DEFAULT; opt++) { if (opt->longopt != NULL && strcmp (arg, opt->longopt) == 0) { - state->argc--; - state->argv++; + state_p->argc--; + state_p->argv++; return opt->id; } } - state->error = "Unknown long option"; + state_p->error = "Unknown long option"; return CLI_OPT_END; } arg++; - for (const cli_opt_t *opt = state->opts; opt->id != CLI_OPT_DEFAULT; opt++) + for (const cli_opt_t *opt = state_p->opts; opt->id != CLI_OPT_DEFAULT; opt++) { if (opt->opt != NULL && strcmp (arg, opt->opt) == 0) { - state->argc--; - state->argv++; + state_p->argc--; + state_p->argv++; return opt->id; } } - state->error = "Unknown option"; + state_p->error = "Unknown option"; return CLI_OPT_END; } /* cli_consume_option */ @@ -145,69 +155,69 @@ cli_consume_option (cli_state_t *state) /**< state of the command line option pr * Returns the next argument as string. * * Note: - * The state->error is not NULL on error and it contains the error message. + * The state_p->error is not NULL on error and it contains the error message. * * @return argument string */ const char * -cli_consume_string (cli_state_t *state) /**< state of the command line option processor */ +cli_consume_string (cli_state_t *state_p) /**< state of the command line option processor */ { - if (state->error != NULL) + if (state_p->error != NULL) { return NULL; } - if (state->argc <= 0) + if (state_p->argc <= 0) { - state->error = "Expected string argument"; - state->arg = NULL; + state_p->error = "Expected string argument"; + state_p->arg = NULL; return NULL; } - state->arg = state->argv[0]; + state_p->arg = state_p->argv[0]; - state->argc--; - state->argv++; - return state->arg; + state_p->argc--; + state_p->argv++; + return state_p->arg; } /* cli_consume_string */ /** * Returns the next argument as integer. * * Note: - * The state->error is not NULL on error and it contains the error message. + * The state_p->error is not NULL on error and it contains the error message. * * @return argument integer */ int -cli_consume_int (cli_state_t *state) /**< state of the command line option processor */ +cli_consume_int (cli_state_t *state_p) /**< state of the command line option processor */ { - if (state->error != NULL) + if (state_p->error != NULL) { return 0; } - state->error = "Expected integer argument"; + state_p->error = "Expected integer argument"; - if (state->argc <= 0) + if (state_p->argc <= 0) { - state->arg = NULL; + state_p->arg = NULL; return 0; } - state->arg = state->argv[0]; + state_p->arg = state_p->argv[0]; char *endptr; - long int value = strtol (state->arg, &endptr, 10); + long int value = strtol (state_p->arg, &endptr, 10); if (*endptr != '\0') { return 0; } - state->error = NULL; - state->argc--; - state->argv++; + state_p->error = NULL; + state_p->argc--; + state_p->argv++; return (int) value; } /* cli_consume_int */ @@ -244,26 +254,41 @@ cli_print_prefix (const char *str, /**< string to print */ * Print usage summary of options. */ static void -cli_opt_usage (const char *progname, /**< program name, typically argv[0] */ - const cli_opt_t *opts) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */ +cli_opt_usage (const char *prog_name_p, /**< program name, typically argv[0] */ + const char *command_name_p, /**< command name if available */ + const cli_opt_t *opts_p) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */ { - int length = (int) strlen (progname); - const cli_opt_t *o = opts; + int length = (int) strlen (prog_name_p); + const cli_opt_t *current_opt_p = opts_p; + + printf ("%s", prog_name_p); + + if (command_name_p != NULL) + { + int command_length = (int) strlen (command_name_p); - printf ("%s", progname); + if (length + 1 + command_length > CLI_LINE_LENGTH) + { + length = CLI_LINE_INDENT - 1; + printf ("\n"); + cli_print_pad (length); + } + + printf (" %s", command_name_p); + } - while (o->id != CLI_OPT_DEFAULT) + while (current_opt_p->id != CLI_OPT_DEFAULT) { - const char *opt = o->opt; + const char *opt_p = current_opt_p->opt; int opt_length = 2 + 1; - if (opt == NULL) + if (opt_p == NULL) { - opt = o->longopt; + opt_p = current_opt_p->longopt; opt_length++; } - opt_length += (int) strlen (opt); + opt_length += (int) strlen (opt_p); if (length + 1 + opt_length >= CLI_LINE_LENGTH) { @@ -275,29 +300,29 @@ cli_opt_usage (const char *progname, /**< program name, typically argv[0] */ printf (" ["); - if (o->opt != NULL) + if (current_opt_p->opt != NULL) { - printf ("-%s", opt); + printf ("-%s", opt_p); } else { - printf ("--%s", opt); + printf ("--%s", opt_p); } - if (o->meta != NULL) + if (current_opt_p->meta != NULL) { - printf (" %s", o->meta); + printf (" %s", current_opt_p->meta); } printf ("]"); - o++; + current_opt_p++; } - if (o->meta != NULL) + if (current_opt_p->meta != NULL) { - const char *opt = o->meta; - int opt_length = (int) (2 + strlen (opt)); + const char *opt_p = current_opt_p->meta; + int opt_length = (int) (2 + strlen (opt_p)); if (length + 1 + opt_length >= CLI_LINE_LENGTH) { @@ -306,7 +331,7 @@ cli_opt_usage (const char *progname, /**< program name, typically argv[0] */ cli_print_pad (length); } - printf (" [%s]", opt); + printf (" [%s]", opt_p); } printf ("\n\n"); @@ -354,43 +379,44 @@ cli_print_help (const char *help) /**< the help message to print */ * Print detailed help for options. */ void -cli_help (const char *progname, /**< program name, typically argv[0] */ - const cli_opt_t *options) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */ +cli_help (const char *prog_name_p, /**< program name, typically argv[0] */ + const char *command_name_p, /**< command name if available */ + const cli_opt_t *options_p) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */ { - cli_opt_usage (progname, options); + cli_opt_usage (prog_name_p, command_name_p, options_p); - const cli_opt_t *opt = options; + const cli_opt_t *opt_p = options_p; - while (opt->id != CLI_OPT_DEFAULT) + while (opt_p->id != CLI_OPT_DEFAULT) { int length = CLI_LINE_INDENT; cli_print_pad (CLI_LINE_INDENT); - if (opt->opt != NULL) + if (opt_p->opt != NULL) { - printf ("-%s", opt->opt); - length += (int) (strlen (opt->opt) + 1); + printf ("-%s", opt_p->opt); + length += (int) (strlen (opt_p->opt) + 1); } - if (opt->opt != NULL && opt->longopt != NULL) + if (opt_p->opt != NULL && opt_p->longopt != NULL) { printf (", "); length += 2; } - if (opt->longopt != NULL) + if (opt_p->longopt != NULL) { - printf ("--%s", opt->longopt); - length += (int) (strlen (opt->longopt) + 2); + printf ("--%s", opt_p->longopt); + length += (int) (strlen (opt_p->longopt) + 2); } - if (opt->meta != NULL) + if (opt_p->meta != NULL) { - printf (" %s", opt->meta); - length += 1 + (int) strlen (opt->meta); + printf (" %s", opt_p->meta); + length += 1 + (int) strlen (opt_p->meta); } - if (opt->help != NULL) + if (opt_p->help != NULL) { if (length >= CLI_LINE_TAB) { @@ -400,23 +426,23 @@ cli_help (const char *progname, /**< program name, typically argv[0] */ cli_print_pad (CLI_LINE_TAB - length); length = CLI_LINE_TAB; - cli_print_help (opt->help); + cli_print_help (opt_p->help); } printf ("\n"); - opt++; + opt_p++; } - if (opt->help != NULL) + if (opt_p->help != NULL) { int length = 0; - if (opt->meta != NULL) + if (opt_p->meta != NULL) { - length = (int) (CLI_LINE_INDENT + strlen (opt->meta)); + length = (int) (CLI_LINE_INDENT + strlen (opt_p->meta)); cli_print_pad (CLI_LINE_INDENT); - printf ("%s", opt->meta); + printf ("%s", opt_p->meta); } if (length >= CLI_LINE_TAB) @@ -427,7 +453,7 @@ cli_help (const char *progname, /**< program name, typically argv[0] */ cli_print_pad (CLI_LINE_TAB - length); - cli_print_help (opt->help); + cli_print_help (opt_p->help); printf ("\n"); } } /* cli_help */ diff --git a/jerry-main/cli.h b/jerry-main/cli.h index 3117e325..43c65f89 100644 --- a/jerry-main/cli.h +++ b/jerry-main/cli.h @@ -66,10 +66,11 @@ typedef struct * Functions for CLI. */ -cli_state_t cli_init (const cli_opt_t *options, int argc, char **argv); -int cli_consume_option (cli_state_t *state); -const char * cli_consume_string (cli_state_t *state); -int cli_consume_int (cli_state_t *state); -void cli_help (const char *progname, const cli_opt_t *options); +cli_state_t cli_init (const cli_opt_t *options_p, int argc, char **argv); +void cli_change_opts (cli_state_t *state_p, const cli_opt_t *options_p); +int cli_consume_option (cli_state_t *state_p); +const char * cli_consume_string (cli_state_t *state_p); +int cli_consume_int (cli_state_t *state_p); +void cli_help (const char *prog_name_p, const char *command_name_p, const cli_opt_t *options_p); #endif /* CLI_H */ diff --git a/jerry-main/main-unix-snapshot.c b/jerry-main/main-unix-snapshot.c new file mode 100644 index 00000000..b4c12888 --- /dev/null +++ b/jerry-main/main-unix-snapshot.c @@ -0,0 +1,308 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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 <string.h> + +#include "jerryscript.h" +#include "jerryscript-port.h" + +#include "cli.h" + +/** + * Maximum size for loaded snapshots + */ +#define JERRY_BUFFER_SIZE (1048576) + +/** + * Standalone Jerry exit codes + */ +#define JERRY_STANDALONE_EXIT_CODE_OK (0) +#define JERRY_STANDALONE_EXIT_CODE_FAIL (1) + +static uint8_t input_buffer[ JERRY_BUFFER_SIZE ]; +static uint32_t output_buffer[ JERRY_BUFFER_SIZE / 4 ]; + +/** + * Loading a single file into the memory. + * + * @return size of file - if loading is successful + * 0 - otherwise + */ +static size_t +read_file (uint8_t *input_pos_p, /**< next position in the input buffer */ + const char *file_name) /**< file name */ +{ + FILE *file = fopen (file_name, "r"); + + if (file == NULL) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name); + return 0; + } + + size_t max_size = (size_t) (input_buffer + JERRY_BUFFER_SIZE - input_pos_p); + + size_t bytes_read = fread (input_pos_p, 1u, max_size, file); + fclose (file); + + if (bytes_read == 0) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name); + return 0; + } + + if (bytes_read == max_size) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: file too large: %s\n", file_name); + return 0; + } + + printf ("Input file '%s' (%d bytes) loaded.\n", file_name, (int) bytes_read); + return bytes_read; +} /* read_file */ + +/** + * Merge command line option IDs + */ +typedef enum +{ + OPT_MERGE_HELP, + OPT_MERGE_OUT, +} merge_opt_id_t; + +/** + * Merge command line options + */ +static const cli_opt_t merge_opts[] = +{ + CLI_OPT_DEF (.id = OPT_MERGE_HELP, .opt = "h", .longopt = "help", + .help = "print this help and exit"), + CLI_OPT_DEF (.id = OPT_MERGE_OUT, .opt = "o", + .help = "specify output file name (default: merged.snapshot)"), + CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE", + .help = "input snapshot files, minimum two") +}; + +/** + * Process 'merge' command. + * + * @return error code (0 - no error) + */ +static int +process_merge (cli_state_t *cli_state_p, /**< cli state */ + int argc, /**< number of arguments */ + char *prog_name_p) /**< program name */ +{ + jerry_init (JERRY_INIT_EMPTY); + + uint8_t *input_pos_p = input_buffer; + + cli_change_opts (cli_state_p, merge_opts); + + const char *output_file_name_p = "merged.snapshot"; + const uint32_t *merge_buffers[argc]; + size_t merge_buffer_sizes[argc]; + uint32_t number_of_files = 0; + + for (int id = cli_consume_option (cli_state_p); id != CLI_OPT_END; id = cli_consume_option (cli_state_p)) + { + switch (id) + { + case OPT_MERGE_HELP: + { + cli_help (prog_name_p, "merge", merge_opts); + return JERRY_STANDALONE_EXIT_CODE_OK; + } + case OPT_MERGE_OUT: + { + output_file_name_p = cli_consume_string (cli_state_p); + break; + } + case CLI_OPT_DEFAULT: + { + const char *file_name_p = cli_consume_string (cli_state_p); + + if (cli_state_p->error == NULL) + { + size_t size = read_file (input_pos_p, file_name_p); + + if (size == 0) + { + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + + merge_buffers[number_of_files] = (const uint32_t *) input_pos_p; + merge_buffer_sizes[number_of_files] = size; + + number_of_files++; + const uintptr_t mask = sizeof (uint32_t) - 1; + input_pos_p = (uint8_t *) ((((uintptr_t) input_pos_p) + size + mask) & ~mask); + } + break; + } + default: + { + cli_state_p->error = "Internal error"; + break; + } + } + } + + if (cli_state_p->error != NULL) + { + if (cli_state_p->arg != NULL) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state_p->error, cli_state_p->arg); + } + else + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state_p->error); + } + + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + + if (number_of_files < 2) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: at least two input files must be passed.\n"); + + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + + const char *error_p; + size_t size = jerry_merge_snapshots (merge_buffers, + merge_buffer_sizes, + number_of_files, + output_buffer, + JERRY_BUFFER_SIZE, + &error_p); + + if (size == 0) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", error_p); + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + + FILE *file_p = fopen (output_file_name_p, "w"); + + if (file_p != NULL) + { + fwrite (output_buffer, 1u, size, file_p); + fclose (file_p); + } + else + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: cannot open file: '%s'\n", output_file_name_p); + } + + return JERRY_STANDALONE_EXIT_CODE_OK; +} /* process_merge */ + +/** + * Command line option IDs + */ +typedef enum +{ + OPT_HELP, +} main_opt_id_t; + +/** + * Command line options + */ +static const cli_opt_t main_opts[] = +{ + CLI_OPT_DEF (.id = OPT_HELP, .opt = "h", .longopt = "help", + .help = "print this help and exit"), + CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "COMMAND", + .help = "specify the command") +}; + +/** + * Print available commands. + */ +static void +print_commands (char *prog_name_p) /**< program name */ +{ + cli_help (prog_name_p, NULL, main_opts); + + printf ("\nAvailable commands:\n" + " merge\n" + "\nPassing -h or --help after a command displays its help.\n"); +} /* print_commands */ + +/** + * Main function. + * + * @return error code (0 - no error) + */ +int +main (int argc, /**< number of arguments */ + char **argv) /**< argument list */ +{ + cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1); + + for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state)) + { + switch (id) + { + case OPT_MERGE_HELP: + { + /* Help is always printed if no command is provided. */ + break; + } + case CLI_OPT_DEFAULT: + { + const char *command_p = cli_consume_string (&cli_state); + + if (cli_state.error != NULL) + { + break; + } + + if (!strcmp ("merge", command_p)) + { + return process_merge (&cli_state, argc, argv[0]); + } + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: unknown command: %s\n\n", command_p); + print_commands (argv[0]); + + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + default: + { + cli_state.error = "Internal error"; + break; + } + } + } + + if (cli_state.error != NULL) + { + if (cli_state.arg != NULL) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state.error, cli_state.arg); + } + else + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state.error); + } + + return JERRY_STANDALONE_EXIT_CODE_FAIL; + } + + print_commands (argv[0]); + return JERRY_STANDALONE_EXIT_CODE_OK; +} /* main */ diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index 4d2b4167..65dd5ff5 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -319,6 +319,7 @@ typedef enum OPT_SAVE_LIT_LIST, OPT_SAVE_LIT_C, OPT_EXEC_SNAP, + OPT_EXEC_SNAP_FUNC, OPT_LOG_LEVEL, OPT_ABORT_ON_FAIL, OPT_NO_PROMPT @@ -357,6 +358,8 @@ static const cli_opt_t main_opts[] = .help = "export literals found in parsed JS input (in C source format)"), CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE", .help = "execute input snapshot file(s)"), + CLI_OPT_DEF (.id = OPT_EXEC_SNAP_FUNC, .longopt = "exec-snapshot-func", .meta = "FILE NUM", + .help = "execute specific function from input snapshot file(s)"), CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "log-level", .meta = "NUM", .help = "set log level (0-3)"), CLI_OPT_DEF (.id = OPT_ABORT_ON_FAIL, .longopt = "abort-on-fail", @@ -427,6 +430,7 @@ main (int argc, jerry_init_flag_t flags = JERRY_INIT_EMPTY; const char *exec_snapshot_file_names[argc]; + uint32_t exec_snapshot_file_indices[argc]; int exec_snapshots_count = 0; bool is_parse_only = false; @@ -452,7 +456,7 @@ main (int argc, { case OPT_HELP: { - cli_help (argv[0], main_opts); + cli_help (argv[0], NULL, main_opts); return JERRY_STANDALONE_EXIT_CODE_OK; } case OPT_VERSION: @@ -544,7 +548,21 @@ main (int argc, { if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg)) { - exec_snapshot_file_names[exec_snapshots_count++] = cli_consume_string (&cli_state); + exec_snapshot_file_names[exec_snapshots_count] = cli_consume_string (&cli_state); + exec_snapshot_file_indices[exec_snapshots_count++] = 0; + } + else + { + cli_consume_string (&cli_state); + } + break; + } + case OPT_EXEC_SNAP_FUNC: + { + if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg)) + { + exec_snapshot_file_names[exec_snapshots_count] = cli_consume_string (&cli_state); + exec_snapshot_file_indices[exec_snapshots_count++] = (uint32_t) cli_consume_int (&cli_state); } else { @@ -650,9 +668,10 @@ main (int argc, } else { - ret_value = jerry_exec_snapshot (snapshot_p, - snapshot_size, - true); + ret_value = jerry_exec_snapshot_at (snapshot_p, + snapshot_size, + exec_snapshot_file_indices[i], + true); } if (jerry_value_has_error_flag (ret_value)) diff --git a/tests/unit-core/test-api.c b/tests/unit-core/test-api.c index c7e714ba..7de12e01 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -18,11 +18,6 @@ #include "test-common.h" -/** - * Maximum size of snapshots buffer - */ -#define SNAPSHOT_BUFFER_SIZE (256) - const char *test_source = ( "function assert (arg) { " " if (!arg) { " @@ -1181,122 +1176,5 @@ main (void) jerry_cleanup (); - /* Dump / execute snapshot */ - if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_SAVE) - && jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_EXEC)) - { - static uint32_t global_mode_snapshot_buffer[SNAPSHOT_BUFFER_SIZE]; - static uint32_t eval_mode_snapshot_buffer[SNAPSHOT_BUFFER_SIZE]; - - const char *code_to_snapshot_p = "(function () { return 'string from snapshot'; }) ();"; - - jerry_init (JERRY_INIT_SHOW_OPCODES); - size_t global_mode_snapshot_size = jerry_parse_and_save_snapshot ((jerry_char_t *) code_to_snapshot_p, - strlen (code_to_snapshot_p), - true, - false, - global_mode_snapshot_buffer, - SNAPSHOT_BUFFER_SIZE); - TEST_ASSERT (global_mode_snapshot_size != 0); - jerry_cleanup (); - - jerry_init (JERRY_INIT_SHOW_OPCODES); - size_t eval_mode_snapshot_size = jerry_parse_and_save_snapshot ((jerry_char_t *) code_to_snapshot_p, - strlen (code_to_snapshot_p), - false, - false, - eval_mode_snapshot_buffer, - SNAPSHOT_BUFFER_SIZE); - TEST_ASSERT (eval_mode_snapshot_size != 0); - jerry_cleanup (); - - jerry_init (JERRY_INIT_SHOW_OPCODES); - - res = jerry_exec_snapshot (global_mode_snapshot_buffer, - global_mode_snapshot_size, - false); - - TEST_ASSERT (!jerry_value_has_error_flag (res)); - TEST_ASSERT (jerry_value_is_string (res)); - sz = jerry_get_string_size (res); - TEST_ASSERT (sz == 20); - sz = jerry_string_to_char_buffer (res, (jerry_char_t *) buffer, sz); - TEST_ASSERT (sz == 20); - jerry_release_value (res); - TEST_ASSERT (!strncmp (buffer, "string from snapshot", (size_t) sz)); - - res = jerry_exec_snapshot (eval_mode_snapshot_buffer, - eval_mode_snapshot_size, - false); - - TEST_ASSERT (!jerry_value_has_error_flag (res)); - TEST_ASSERT (jerry_value_is_string (res)); - sz = jerry_get_string_size (res); - TEST_ASSERT (sz == 20); - sz = jerry_string_to_char_buffer (res, (jerry_char_t *) buffer, sz); - TEST_ASSERT (sz == 20); - jerry_release_value (res); - TEST_ASSERT (!strncmp (buffer, "string from snapshot", (size_t) sz)); - - jerry_cleanup (); - } - - /* Save literals */ - if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_SAVE)) - { - /* C format generation */ - jerry_init (JERRY_INIT_EMPTY); - - static uint32_t literal_buffer_c[SNAPSHOT_BUFFER_SIZE]; - static const char *code_for_c_format_p = "var object = { aa:'fo o', Bb:'max', aaa:'xzy0' };"; - - size_t literal_sizes_c_format = jerry_parse_and_save_literals ((jerry_char_t *) code_for_c_format_p, - strlen (code_for_c_format_p), - false, - literal_buffer_c, - SNAPSHOT_BUFFER_SIZE, - true); - TEST_ASSERT (literal_sizes_c_format == 203); - - static const char *expected_c_format = ( - "jerry_length_t literal_count = 4;\n\n" - "jerry_char_ptr_t literals[4] =\n" - "{\n" - " \"Bb\",\n" - " \"aa\",\n" - " \"aaa\",\n" - " \"xzy0\"\n" - "};\n\n" - "jerry_length_t literal_sizes[4] =\n" - "{\n" - " 2 /* Bb */,\n" - " 2 /* aa */,\n" - " 3 /* aaa */,\n" - " 4 /* xzy0 */\n" - "};\n" - ); - - TEST_ASSERT (!strncmp ((char *) literal_buffer_c, expected_c_format, literal_sizes_c_format)); - jerry_cleanup (); - - /* List format generation */ - jerry_init (JERRY_INIT_EMPTY); - - static uint32_t literal_buffer_list[SNAPSHOT_BUFFER_SIZE]; - static const char *code_for_list_format_p = "var obj = { a:'aa', bb:'Bb' };"; - - size_t literal_sizes_list_format = jerry_parse_and_save_literals ((jerry_char_t *) code_for_list_format_p, - strlen (code_for_list_format_p), - false, - literal_buffer_list, - SNAPSHOT_BUFFER_SIZE, - false); - - TEST_ASSERT (literal_sizes_list_format == 25); - TEST_ASSERT (!strncmp ((char *) literal_buffer_list, "1 a\n2 Bb\n2 aa\n2 bb\n3 obj\n", literal_sizes_list_format)); - - jerry_cleanup (); - } - return 0; } /* main */ diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c new file mode 100644 index 00000000..1ef26ed2 --- /dev/null +++ b/tests/unit-core/test-snapshot.c @@ -0,0 +1,216 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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 "config.h" +#include "jerryscript.h" + +#include "test-common.h" + +/** + * Maximum size of snapshots buffer + */ +#define SNAPSHOT_BUFFER_SIZE (256) + +int +main (void) +{ + TEST_INIT (); + + /* Dump / execute snapshot */ + if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_SAVE) + && jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_EXEC)) + { + static uint32_t global_mode_snapshot_buffer[SNAPSHOT_BUFFER_SIZE]; + static uint32_t eval_mode_snapshot_buffer[SNAPSHOT_BUFFER_SIZE]; + char string_data[32]; + + const char *code_to_snapshot_p = "(function () { return 'string from snapshot'; }) ();"; + + jerry_init (JERRY_INIT_EMPTY); + size_t global_mode_snapshot_size = jerry_parse_and_save_snapshot ((jerry_char_t *) code_to_snapshot_p, + strlen (code_to_snapshot_p), + true, + false, + global_mode_snapshot_buffer, + SNAPSHOT_BUFFER_SIZE); + TEST_ASSERT (global_mode_snapshot_size != 0); + jerry_cleanup (); + + jerry_init (JERRY_INIT_EMPTY); + size_t eval_mode_snapshot_size = jerry_parse_and_save_snapshot ((jerry_char_t *) code_to_snapshot_p, + strlen (code_to_snapshot_p), + false, + false, + eval_mode_snapshot_buffer, + SNAPSHOT_BUFFER_SIZE); + TEST_ASSERT (eval_mode_snapshot_size != 0); + jerry_cleanup (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t res = jerry_exec_snapshot (global_mode_snapshot_buffer, + global_mode_snapshot_size, + false); + + TEST_ASSERT (!jerry_value_has_error_flag (res)); + TEST_ASSERT (jerry_value_is_string (res)); + jerry_size_t sz = jerry_get_string_size (res); + TEST_ASSERT (sz == 20); + sz = jerry_string_to_char_buffer (res, (jerry_char_t *) string_data, sz); + TEST_ASSERT (sz == 20); + jerry_release_value (res); + TEST_ASSERT (!strncmp (string_data, "string from snapshot", (size_t) sz)); + + res = jerry_exec_snapshot (eval_mode_snapshot_buffer, + eval_mode_snapshot_size, + false); + + TEST_ASSERT (!jerry_value_has_error_flag (res)); + TEST_ASSERT (jerry_value_is_string (res)); + sz = jerry_get_string_size (res); + TEST_ASSERT (sz == 20); + sz = jerry_string_to_char_buffer (res, (jerry_char_t *) string_data, sz); + TEST_ASSERT (sz == 20); + jerry_release_value (res); + TEST_ASSERT (!strncmp (string_data, "string from snapshot", (size_t) sz)); + + jerry_cleanup (); + } + + /* Merge snapshot */ + if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_SAVE) + && jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_EXEC)) + { + static uint32_t snapshot_buffer_0[SNAPSHOT_BUFFER_SIZE]; + static uint32_t snapshot_buffer_1[SNAPSHOT_BUFFER_SIZE]; + size_t snapshot_sizes[2]; + static uint32_t merged_snapshot_buffer[SNAPSHOT_BUFFER_SIZE]; + + const char *code_to_snapshot_p = "123"; + + jerry_init (JERRY_INIT_EMPTY); + snapshot_sizes[0] = jerry_parse_and_save_snapshot ((jerry_char_t *) code_to_snapshot_p, + strlen (code_to_snapshot_p), + true, + false, + snapshot_buffer_0, + SNAPSHOT_BUFFER_SIZE); + TEST_ASSERT (snapshot_sizes[0] != 0); + jerry_cleanup (); + + code_to_snapshot_p = "456"; + + jerry_init (JERRY_INIT_EMPTY); + snapshot_sizes[1] = jerry_parse_and_save_snapshot ((jerry_char_t *) code_to_snapshot_p, + strlen (code_to_snapshot_p), + true, + false, + snapshot_buffer_1, + SNAPSHOT_BUFFER_SIZE); + TEST_ASSERT (snapshot_sizes[1] != 0); + jerry_cleanup (); + + jerry_init (JERRY_INIT_EMPTY); + + const char *error_p; + const uint32_t *snapshot_buffers[2]; + + snapshot_buffers[0] = snapshot_buffer_0; + snapshot_buffers[1] = snapshot_buffer_1; + + size_t merged_size = jerry_merge_snapshots (snapshot_buffers, + snapshot_sizes, + 2, + merged_snapshot_buffer, + SNAPSHOT_BUFFER_SIZE, + &error_p); + + jerry_cleanup (); + + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t res = jerry_exec_snapshot_at (merged_snapshot_buffer, merged_size, 0, false); + TEST_ASSERT (!jerry_value_has_error_flag (res)); + TEST_ASSERT (jerry_get_number_value (res) == 123); + jerry_release_value (res); + + res = jerry_exec_snapshot_at (merged_snapshot_buffer, merged_size, 1, false); + TEST_ASSERT (!jerry_value_has_error_flag (res)); + TEST_ASSERT (jerry_get_number_value (res) == 456); + jerry_release_value (res); + + jerry_cleanup (); + } + + /* Save literals */ + if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_SAVE)) + { + /* C format generation */ + jerry_init (JERRY_INIT_EMPTY); + + static uint32_t literal_buffer_c[SNAPSHOT_BUFFER_SIZE]; + static const char *code_for_c_format_p = "var object = { aa:'fo o', Bb:'max', aaa:'xzy0' };"; + + size_t literal_sizes_c_format = jerry_parse_and_save_literals ((jerry_char_t *) code_for_c_format_p, + strlen (code_for_c_format_p), + false, + literal_buffer_c, + SNAPSHOT_BUFFER_SIZE, + true); + TEST_ASSERT (literal_sizes_c_format == 203); + + static const char *expected_c_format = ( + "jerry_length_t literal_count = 4;\n\n" + "jerry_char_ptr_t literals[4] =\n" + "{\n" + " \"Bb\",\n" + " \"aa\",\n" + " \"aaa\",\n" + " \"xzy0\"\n" + "};\n\n" + "jerry_length_t literal_sizes[4] =\n" + "{\n" + " 2 /* Bb */,\n" + " 2 /* aa */,\n" + " 3 /* aaa */,\n" + " 4 /* xzy0 */\n" + "};\n" + ); + + TEST_ASSERT (!strncmp ((char *) literal_buffer_c, expected_c_format, literal_sizes_c_format)); + jerry_cleanup (); + + /* List format generation */ + jerry_init (JERRY_INIT_EMPTY); + + static uint32_t literal_buffer_list[SNAPSHOT_BUFFER_SIZE]; + static const char *code_for_list_format_p = "var obj = { a:'aa', bb:'Bb' };"; + + size_t literal_sizes_list_format = jerry_parse_and_save_literals ((jerry_char_t *) code_for_list_format_p, + strlen (code_for_list_format_p), + false, + literal_buffer_list, + SNAPSHOT_BUFFER_SIZE, + false); + + TEST_ASSERT (literal_sizes_list_format == 25); + TEST_ASSERT (!strncmp ((char *) literal_buffer_list, "1 a\n2 Bb\n2 aa\n2 bb\n3 obj\n", literal_sizes_list_format)); + + jerry_cleanup (); + } + + return 0; +} /* main */ diff --git a/tools/build.py b/tools/build.py index 2db7d22c..872cea12 100755 --- a/tools/build.py +++ b/tools/build.py @@ -74,6 +74,8 @@ def get_arguments(): help='build jerry command line tool (%(choices)s; default: %(default)s)') parser.add_argument('--jerry-cmdline-minimal', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper, help='build minimal version of the jerry command line tool (%(choices)s; default: %(default)s)') + parser.add_argument('--jerry-cmdline-snapshot', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper, + help='build snapshot command line tool (%(choices)s; default: %(default)s)') parser.add_argument('--jerry-debugger', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper, help='enable the jerry debugger (%(choices)s; default: %(default)s)') parser.add_argument('--jerry-ext', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper, @@ -150,6 +152,7 @@ def generate_build_options(arguments): build_options.append('-DFEATURE_ERROR_MESSAGES=%s' % arguments.error_messages) build_options.append('-DJERRY_CMDLINE=%s' % arguments.jerry_cmdline) build_options.append('-DJERRY_CMDLINE_MINIMAL=%s' % arguments.jerry_cmdline_minimal) + build_options.append('-DJERRY_CMDLINE_SNAPSHOT=%s' % arguments.jerry_cmdline_snapshot) build_options.append('-DJERRY_PORT_DEFAULT=%s' % arguments.jerry_port_default) build_options.append('-DJERRY_EXT=%s' % arguments.jerry_ext) build_options.append('-DJERRY_LIBC=%s' % arguments.jerry_libc) diff --git a/tools/run-tests.py b/tools/run-tests.py index f55f496b..516c32bc 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -128,6 +128,8 @@ JERRY_BUILDOPTIONS = [ ['--jerry-libc=off', '--compile-flag=-m32', '--cpointer-32bit=on', '--system-allocator=on']), Options('buildoption_test-external_context', ['--jerry-libc=off', '--external-context=on']), + Options('buildoption_test-snapshot_tool', + ['--jerry-cmdline-snapshot=on']), ] def get_arguments(): |