Introduce a way to directly save snapshot functions and load them back (#2043)

Added two new api functions:
* jerry_parse_and_save_function_snapshot
* jerry_load_function_snapshot_at

The jerry_parse_and_save_function_snapshot function allows
creating snapshots from snapshot arguments and body source.

The jerry_load_function_snapshot_at function enables loading
back functions from a given snapshot as a JS function object.

JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com
This commit is contained in:
Péter Gál 2017-10-18 15:03:54 +02:00 committed by László Langó
parent d0143adc82
commit 6d988afbf4
4 changed files with 375 additions and 26 deletions

View File

@ -4170,6 +4170,76 @@ main (void)
- [jerry_exec_snapshot](#jerry_exec_snapshot) - [jerry_exec_snapshot](#jerry_exec_snapshot)
## jerry_parse_and_save_function_snapshot
**Summary**
Generate function snapshot from the specified source code
with the given arguments.
The function arguments and function body are
passed as separated arguments.
**Prototype**
```c
size_t
jerry_parse_and_save_function_snapshot (const jerry_char_t *source_p,
size_t source_size,
const jerry_char_t *args_p,
size_t args_size,
bool is_strict,
uint32_t *buffer_p,
size_t buffer_size)
- `source_p` - script source, it must be a valid utf8 string.
- `source_size` - script source size, in bytes.
- `args_p` - function arguments, it must be a valid utf8 string.
- `args_size` - function argument size, in bytes.
- `is_strict` - strict mode
- `buffer_p` - buffer to save snapshot to.
- `buffer_size` - the buffer's size.
- return value
- the 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_SAVE)
- 0 otherwise.
**Example**
[doctest]: # ()
```c
#include <string.h>
#include "jerryscript.h"
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
static uint32_t func_snapshot_buffer[256];
const jerry_char_t *args_p = (const jerry_char_t *) "a, b";
const jerry_char_t *src_p = (const jerry_char_t *) "return a + b;";
size_t func_snapshot_size = jerry_parse_and_save_function_snapshot (src_p,
strlen ((const char *) src_p),
args_p,
strlen ((const char *) args_p),
false,
func_snapshot_buffer,
sizeof (func_snapshot_buffer) / sizeof (uint32_t));
jerry_cleanup ();
}
```
**See also**
- [jerry_init](#jerry_init)
- [jerry_cleanup](#jerry_cleanup)
- [jerry_load_function_snapshot_at](#jerry_load_function_snapshot_at)
## jerry_exec_snapshot ## jerry_exec_snapshot
**Summary** **Summary**
@ -4318,6 +4388,97 @@ main (void)
- [jerry_parse_and_save_snapshot](#jerry_parse_and_save_snapshot) - [jerry_parse_and_save_snapshot](#jerry_parse_and_save_snapshot)
## jerry_load_function_snapshot_at
**Summary**
Load the selected snapshot function from the specified buffer as a function object.
The lexical environment of the loaded function is always the global lexical environment.
*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_load_function_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 function to load
- `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
- function object built from the snapshot
- thrown error, otherwise
**Example**
[doctest]: # ()
```c
#include <string.h>
#include "jerryscript.h"
int
main (void)
{
static uint32_t snapshot_buffer[256];
const jerry_char_t *args_p = (const jerry_char_t *)"a, b";
const jerry_char_t *src_p = (const jerry_char_t *) "return a + b;";
jerry_init (JERRY_INIT_EMPTY);
size_t snapshot_size = jerry_parse_and_save_function_snapshot (src_p,
strlen ((const char *) src_p),
args_p,
strlen ((const char *) args_p),
false,
snapshot_buffer,
sizeof (snapshot_buffer) / sizeof (uint32_t));
jerry_cleanup ();
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t func = jerry_load_function_snapshot_at (snapshot_buffer,
snapshot_size,
0,
false);
/* 'func' can be used now as a function object */
jerry_value_t this_value = jerry_create_undefined ();
jerry_value_t args[2];
args[0] = jerry_create_number (1.0);
args[1] = jerry_create_number (2.0);
jerry_value_t res = jerry_call_function (func, this_value, args, 2);
/* 'res' now contains the value 3 as a jerry_value_t */
jerry_release_value (args[0]);
jerry_release_value (args[1]);
jerry_release_value (this_value);
jerry_release_value (func);
jerry_cleanup ();
return 0;
}
```
**See also**
- [jerry_init](#jerry_init)
- [jerry_cleanup](#jerry_cleanup)
- [jerry_parse_and_save_function_snapshot](#jerry_parse_and_save_function_snapshot)
## jerry_parse_and_save_literals ## jerry_parse_and_save_literals
**Summary** **Summary**

View File

@ -21,6 +21,7 @@
#include "jerryscript.h" #include "jerryscript.h"
#include "jerry-snapshot.h" #include "jerry-snapshot.h"
#include "js-parser.h" #include "js-parser.h"
#include "ecma-lex-env.h"
#include "lit-char-helpers.h" #include "lit-char-helpers.h"
#include "re-compiler.h" #include "re-compiler.h"
@ -494,24 +495,26 @@ snapshot_load_compiled_code (const uint8_t *base_addr_p, /**< base address of th
#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */ #endif /* JERRY_ENABLE_SNAPSHOT_EXEC */
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
/** /**
* Generate snapshot from specified source * Generate snapshot from specified source and arguments
* *
* @return size of snapshot, if it was generated succesfully * @return size of snapshot, if it was generated succesfully
* (i.e. there are no syntax errors in source code, buffer size is sufficient, * (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_SAVE), * and snapshot support is enabled in current configuration through JERRY_ENABLE_SNAPSHOT_SAVE),
* 0 - otherwise. * 0 - otherwise.
*/ */
size_t static size_t
jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source */ jerry_parse_and_save_snapshot_with_args (const jerry_char_t *source_p, /**< script source */
size_t source_size, /**< script source size */ size_t source_size, /**< script source size */
const jerry_char_t *args_p, /**< arguments string */
size_t args_size, /**< arguments string size */
bool is_for_global, /**< snapshot would be executed as global (true) bool is_for_global, /**< snapshot would be executed as global (true)
* or eval (false) */ * or eval (false) */
bool is_strict, /**< strict mode */ bool is_strict, /**< strict mode */
uint32_t *buffer_p, /**< buffer to save snapshot to */ uint32_t *buffer_p, /**< buffer to save snapshot to */
size_t buffer_size) /**< the buffer's size */ size_t buffer_size) /**< the buffer's size */
{ {
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
snapshot_globals_t globals; snapshot_globals_t globals;
ecma_value_t parse_status; ecma_value_t parse_status;
ecma_compiled_code_t *bytecode_data_p; ecma_compiled_code_t *bytecode_data_p;
@ -522,8 +525,8 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source
globals.snapshot_error_occured = false; globals.snapshot_error_occured = false;
globals.regex_found = false; globals.regex_found = false;
parse_status = parser_parse_script (NULL, parse_status = parser_parse_script (args_p,
0, args_size,
source_p, source_p,
source_size, source_size,
is_strict, is_strict,
@ -588,6 +591,35 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source
ecma_bytecode_deref (bytecode_data_p); ecma_bytecode_deref (bytecode_data_p);
return globals.snapshot_buffer_write_offset; return globals.snapshot_buffer_write_offset;
} /* jerry_parse_and_save_snapshot_with_args */
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
/**
* 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_SAVE),
* 0 - otherwise.
*/
size_t
jerry_parse_and_save_snapshot (const jerry_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) */
bool is_strict, /**< strict mode */
uint32_t *buffer_p, /**< buffer to save snapshot to */
size_t buffer_size) /**< the buffer's size */
{
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
return jerry_parse_and_save_snapshot_with_args (source_p,
source_size,
NULL,
0,
is_for_global,
is_strict,
buffer_p,
buffer_size);
#else /* !JERRY_ENABLE_SNAPSHOT_SAVE */ #else /* !JERRY_ENABLE_SNAPSHOT_SAVE */
JERRY_UNUSED (source_p); JERRY_UNUSED (source_p);
JERRY_UNUSED (source_size); JERRY_UNUSED (source_size);
@ -600,8 +632,9 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ #endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
} /* jerry_parse_and_save_snapshot */ } /* jerry_parse_and_save_snapshot */
#ifdef JERRY_ENABLE_SNAPSHOT_EXEC
/** /**
* Execute snapshot from specified buffer * Execute/load snapshot from specified buffer
* *
* Note: * Note:
* returned value must be freed with jerry_release_value, when it is no longer needed. * returned value must be freed with jerry_release_value, when it is no longer needed.
@ -609,19 +642,19 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source
* @return result of bytecode - if run was successful * @return result of bytecode - if run was successful
* thrown error - otherwise * thrown error - otherwise
*/ */
jerry_value_t static jerry_value_t
jerry_exec_snapshot_at (const uint32_t *snapshot_p, /**< snapshot */ jerry_snapshot_result_at (const uint32_t *snapshot_p, /**< snapshot */
size_t snapshot_size, /**< size of snapshot */ size_t snapshot_size, /**< size of snapshot */
size_t func_index, /**< index of primary function */ size_t func_index, /**< index of primary function */
bool copy_bytecode) /**< flag, indicating whether the passed snapshot bool copy_bytecode, /**< flag, indicating whether the passed snapshot
* buffer should be copied to the engine's memory. * buffer should be copied to the engine's memory.
* If set the engine should not reference the buffer * If set the engine should not reference the buffer
* after the function returns (in this case, the passed * after the function returns (in this case, the passed
* buffer could be freed after the call). * buffer could be freed after the call).
* Otherwise (if the flag is not set) - the buffer could only be * Otherwise (if the flag is not set) - the buffer could only be
* freed after the engine stops (i.e. after call to jerry_cleanup). */ * freed after the engine stops (i.e. after call to jerry_cleanup). */
bool as_function) /** < specify if the loaded snapshot should be returned as a function */
{ {
#ifdef JERRY_ENABLE_SNAPSHOT_EXEC
JERRY_ASSERT (snapshot_p != NULL); JERRY_ASSERT (snapshot_p != NULL);
static const char * const invalid_version_error_p = "Invalid snapshot version or unsupported features present"; static const char * const invalid_version_error_p = "Invalid snapshot version or unsupported features present";
@ -676,7 +709,16 @@ jerry_exec_snapshot_at (const uint32_t *snapshot_p, /**< snapshot */
ecma_value_t ret_val; ecma_value_t ret_val;
if (header_p->func_offsets[func_index] & JERRY_SNAPSHOT_EVAL_CONTEXT) if (as_function)
{
ecma_object_t *lex_env_p = ecma_get_global_environment ();
ecma_object_t *func_obj_p = ecma_op_create_function_object (lex_env_p,
bytecode_p);
ecma_bytecode_deref (bytecode_p);
ret_val = ecma_make_object_value (func_obj_p);
}
else if (header_p->func_offsets[func_index] & JERRY_SNAPSHOT_EVAL_CONTEXT)
{ {
ret_val = vm_run_eval (bytecode_p, false); ret_val = vm_run_eval (bytecode_p, false);
} }
@ -687,6 +729,32 @@ jerry_exec_snapshot_at (const uint32_t *snapshot_p, /**< snapshot */
} }
return ret_val; return ret_val;
} /* jerry_snapshot_result_at */
#endif /* JERRY_ENABLE_SNAPSHOT_EXEC */
/**
* 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_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
return jerry_snapshot_result_at (snapshot_p, snapshot_size, func_index, copy_bytecode, false);
#else /* !JERRY_ENABLE_SNAPSHOT_EXEC */ #else /* !JERRY_ENABLE_SNAPSHOT_EXEC */
JERRY_UNUSED (snapshot_p); JERRY_UNUSED (snapshot_p);
JERRY_UNUSED (snapshot_size); JERRY_UNUSED (snapshot_size);
@ -1411,3 +1479,58 @@ jerry_parse_and_save_literals (const jerry_char_t *source_p, /**< script source
return 0; return 0;
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */ #endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
} /* jerry_parse_and_save_literals */ } /* jerry_parse_and_save_literals */
size_t jerry_parse_and_save_function_snapshot (const jerry_char_t *source_p, /**< function body source */
size_t source_size, /**< function body size */
const jerry_char_t *args_p, /**< arguments string */
size_t args_size, /**< arguments string size */
bool is_strict, /**< strict mode */
uint32_t *buffer_p, /**< buffer to save snapshot to */
size_t buffer_size) /**< the buffer's size */
{
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
return jerry_parse_and_save_snapshot_with_args (source_p,
source_size,
args_p,
args_size,
true,
is_strict,
buffer_p,
buffer_size);
#else /* !JERRY_ENABLE_SNAPSHOT_SAVE */
JERRY_UNUSED (source_p);
JERRY_UNUSED (source_size);
JERRY_UNUSED (args_p);
JERRY_UNUSED (args_size);
JERRY_UNUSED (is_strict);
JERRY_UNUSED (buffer_p);
JERRY_UNUSED (buffer_size);
return 0;
#endif /* JERRY_ENABLE_SNAPSHOT_SAVE */
} /* jerry_parse_and_save_function_snapshot */
jerry_value_t jerry_load_function_snapshot_at (const uint32_t *function_snapshot_p, /**< snapshot of the function(s) */
const size_t function_snapshot_size, /**< size of the snapshot */
size_t func_index, /**< index of the function to load */
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_snapshot_result_at (function_snapshot_p, function_snapshot_size, func_index, copy_bytecode, true);
#else /* !JERRY_ENABLE_SNAPSHOT_EXEC */
JERRY_UNUSED (function_snapshot_p);
JERRY_UNUSED (function_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_load_function_snapshot_at */

View File

@ -40,6 +40,13 @@ size_t jerry_merge_snapshots (const uint32_t **inp_buffers_p, size_t *inp_buffer
size_t jerry_parse_and_save_literals (const jerry_char_t *source_p, size_t source_size, bool is_strict, 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); uint32_t *buffer_p, size_t buffer_size, bool is_c_format);
size_t jerry_parse_and_save_function_snapshot (const jerry_char_t *source_p, size_t source_size,
const jerry_char_t *args_p, size_t args_size,
bool is_strict, uint32_t *buffer_p, size_t buffer_size);
jerry_value_t jerry_load_function_snapshot_at (const uint32_t *function_snapshot_p,
const size_t function_snapshot_size,
size_t func_index,
bool copy_bytecode);
/** /**
* @} * @}
*/ */

View File

@ -23,6 +23,62 @@
*/ */
#define SNAPSHOT_BUFFER_SIZE (256) #define SNAPSHOT_BUFFER_SIZE (256)
static void test_function_snapshot (void)
{
/* function to snapshot */
if (!jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_SAVE)
|| !jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_EXEC))
{
return;
}
const jerry_init_flag_t flags = JERRY_INIT_EMPTY;
static uint32_t function_snapshot_buffer[SNAPSHOT_BUFFER_SIZE];
const char *args_p = "a, b";
const char *code_to_snapshot_p = "return a + b";
jerry_init (flags);
size_t function_snapshot_size = jerry_parse_and_save_function_snapshot ((jerry_char_t *) code_to_snapshot_p,
strlen (code_to_snapshot_p),
(jerry_char_t *) args_p,
strlen (args_p),
false,
function_snapshot_buffer,
SNAPSHOT_BUFFER_SIZE);
TEST_ASSERT (function_snapshot_size != 0);
jerry_cleanup ();
jerry_init (flags);
jerry_value_t function_obj = jerry_load_function_snapshot_at (function_snapshot_buffer,
function_snapshot_size,
0,
false);
TEST_ASSERT (!jerry_value_has_error_flag (function_obj));
TEST_ASSERT (jerry_value_is_function (function_obj));
jerry_value_t this_val = jerry_create_undefined ();
jerry_value_t args[2];
args[0] = jerry_create_number (1.0);
args[1] = jerry_create_number (2.0);
jerry_value_t res = jerry_call_function (function_obj, this_val, args, 2);
TEST_ASSERT (!jerry_value_has_error_flag (res));
TEST_ASSERT (jerry_value_is_number (res));
double num = jerry_get_number_value (res);
TEST_ASSERT (num == 3);
jerry_release_value (args[0]);
jerry_release_value (args[1]);
jerry_release_value (res);
jerry_release_value (function_obj);
jerry_cleanup ();
} /* test_function_snapshot */
int int
main (void) main (void)
{ {
@ -212,5 +268,7 @@ main (void)
jerry_cleanup (); jerry_cleanup ();
} }
test_function_snapshot ();
return 0; return 0;
} /* main */ } /* main */