mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Support ECMAScript stopping in JerryScript. (#1753)
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
parent
0e38356e5b
commit
894aa6d036
@ -36,6 +36,7 @@ Possible compile time enabled feature types:
|
||||
- JERRY_FEATURE_SNAPSHOT_SAVE - saving snapshot files
|
||||
- JERRY_FEATURE_SNAPSHOT_EXEC - executing snapshot files
|
||||
- JERRY_FEATURE_DEBUGGER - debugging
|
||||
- JERRY_FEATURE_VM_EXEC_STOP - stopping ECMAScript execution
|
||||
|
||||
## jerry_char_t
|
||||
|
||||
@ -234,6 +235,28 @@ typedef bool (*jerry_object_property_foreach_t) (const jerry_value_t property_na
|
||||
void *user_data_p);
|
||||
```
|
||||
|
||||
## jerry_vm_exec_stop_callback_t
|
||||
|
||||
**Summary**
|
||||
|
||||
Callback which tells whether the ECMAScript execution should be stopped.
|
||||
If it returns with undefined value the ECMAScript execution continues.
|
||||
Otherwise the result is thrown by the engine (if the error flag is not
|
||||
set for the returned value the engine automatically sets it). The
|
||||
callback function might be called again even if it threw an error.
|
||||
In this case the function must throw the same error again.
|
||||
|
||||
**Prototype**
|
||||
|
||||
```c
|
||||
typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p);
|
||||
```
|
||||
|
||||
**See also**
|
||||
|
||||
- [jerry_set_vm_exec_stop_callback](#jerry_set_vm_exec_stop_callback)
|
||||
|
||||
|
||||
# General engine functions
|
||||
|
||||
## jerry_init
|
||||
@ -3896,3 +3919,81 @@ jerry_parse_and_save_literals (const jerry_char_t *source_p,
|
||||
- [jerry_init](#jerry_init)
|
||||
- [jerry_cleanup](#jerry_cleanup)
|
||||
- [jerry_register_magic_strings](#jerry_register_magic_strings)
|
||||
|
||||
|
||||
# Miscellaneous functions
|
||||
|
||||
## jerry_set_vm_exec_stop_callback
|
||||
|
||||
**Summary**
|
||||
|
||||
When JERRY_FEATURE_VM_EXEC_STOP is enabled a callback function can be
|
||||
specified by this function. This callback is periodically called when
|
||||
JerryScript executes an ECMAScript program.
|
||||
|
||||
If the callback returns with undefined value the ECMAScript execution
|
||||
continues. Otherwise the result is thrown by the engine (if the error
|
||||
flag is not set for the returned value the engine automatically sets
|
||||
it). The callback function might be called again even if it threw
|
||||
an error. In this case the function must throw the same error again.
|
||||
|
||||
To reduce the CPU overhead of constantly checking the termination
|
||||
condition the callback is called when a backward jump is executed
|
||||
or an exception is caught. Setting the `frequency` to a greater
|
||||
than `1` value reduces this overhead further. If its value is N
|
||||
only every Nth event (backward jump, etc.) trigger the next check.
|
||||
|
||||
|
||||
**Prototype**
|
||||
|
||||
```c
|
||||
void
|
||||
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb,
|
||||
void *user_p,
|
||||
uint32_t frequency);
|
||||
```
|
||||
|
||||
- `stop_cb` - periodically called callback (passing NULL disables this feature)
|
||||
- `user_p` - user pointer passed to the `stop_cb` function
|
||||
- `frequency` - frequency of calling the `stop_cb` function
|
||||
|
||||
**Example**
|
||||
|
||||
```c
|
||||
static jerry_value_t
|
||||
vm_exec_stop_callback (void *user_p)
|
||||
{
|
||||
static int countdown = 10;
|
||||
|
||||
while (countdown > 0)
|
||||
{
|
||||
countdown--;
|
||||
return jerry_create_undefined ();
|
||||
}
|
||||
|
||||
// The error flag is added automatically.
|
||||
return jerry_create_string ((const jerry_char_t *) "Abort script");
|
||||
}
|
||||
|
||||
{
|
||||
jerry_init (JERRY_INIT_EMPTY);
|
||||
|
||||
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
|
||||
|
||||
// Inifinte loop.
|
||||
const char *src_p = "while(true) {}";
|
||||
|
||||
jerry_value_t src = jerry_parse ((jerry_char_t *) src_p, strlen (src_p), false);
|
||||
jerry_release_value (jerry_run (src));
|
||||
jerry_release_value (src);
|
||||
jerry_cleanup ();
|
||||
}
|
||||
```
|
||||
|
||||
**See also**
|
||||
|
||||
- [jerry_init](#jerry_init)
|
||||
- [jerry_cleanup](#jerry_cleanup)
|
||||
- [jerry_parse](#jerry_parse)
|
||||
- [jerry_run](#jerry_run)
|
||||
- [jerry_vm_exec_stop_callback_t](#jerry_vm_exec_stop_callback_t)
|
||||
|
||||
@ -32,6 +32,7 @@ set(FEATURE_SNAPSHOT_SAVE OFF CACHE BOOL "Enable saving snapshot files?
|
||||
set(FEATURE_SYSTEM_ALLOCATOR OFF CACHE BOOL "Enable system allocator?")
|
||||
set(FEATURE_VALGRIND OFF CACHE BOOL "Enable Valgrind support?")
|
||||
set(FEATURE_VALGRIND_FREYA OFF CACHE BOOL "Enable Valgrind-Freya support?")
|
||||
set(FEATURE_VM_EXEC_STOP OFF CACHE BOOL "Enable VM execution stopping?")
|
||||
set(MEM_HEAP_SIZE_KB "512" CACHE STRING "Size of memory heap, in kilobytes")
|
||||
|
||||
if(FEATURE_SYSTEM_ALLOCATOR)
|
||||
@ -54,6 +55,7 @@ message(STATUS "FEATURE_SNAPSHOT_SAVE " ${FEATURE_SNAPSHOT_SAVE})
|
||||
message(STATUS "FEATURE_SYSTEM_ALLOCATOR " ${FEATURE_SYSTEM_ALLOCATOR})
|
||||
message(STATUS "FEATURE_VALGRIND " ${FEATURE_VALGRIND})
|
||||
message(STATUS "FEATURE_VALGRIND_FREYA " ${FEATURE_VALGRIND_FREYA})
|
||||
message(STATUS "FEATURE_VM_EXEC_STOP " ${FEATURE_VM_EXEC_STOP})
|
||||
message(STATUS "MEM_HEAP_SIZE_KB " ${MEM_HEAP_SIZE_KB})
|
||||
|
||||
# Include directories
|
||||
@ -238,6 +240,11 @@ if(FEATURE_VALGRIND_FREYA)
|
||||
set(INCLUDE_CORE ${INCLUDE_CORE} ${INCLUDE_THIRD_PARTY_VALGRIND})
|
||||
endif()
|
||||
|
||||
# Enable VM execution stopping
|
||||
if (FEATURE_VM_EXEC_STOP)
|
||||
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_VM_EXEC_STOP)
|
||||
endif()
|
||||
|
||||
# Size of heap
|
||||
math(EXPR MEM_HEAP_AREA_SIZE "${MEM_HEAP_SIZE_KB} * 1024")
|
||||
set(DEFINES_JERRY ${DEFINES_JERRY} CONFIG_MEM_HEAP_AREA_SIZE=${MEM_HEAP_AREA_SIZE})
|
||||
|
||||
@ -84,6 +84,14 @@ typedef struct
|
||||
uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) */
|
||||
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
|
||||
|
||||
#ifdef JERRY_VM_EXEC_STOP
|
||||
uint32_t vm_exec_stop_frequency; /**< reset value for vm_exec_stop_counter */
|
||||
uint32_t vm_exec_stop_counter; /**< down counter for reducing the calls of vm_exec_stop_cb */
|
||||
void *vm_exec_stop_user_p; /**< user pointer for vm_exec_stop_cb */
|
||||
jerry_vm_exec_stop_callback_t vm_exec_stop_cb; /**< user function which returns whether the
|
||||
* ECMAScript execution should be stopped */
|
||||
#endif /* JERRY_VM_EXEC_STOP */
|
||||
|
||||
#ifdef JERRY_DEBUGGER
|
||||
uint8_t debugger_send_buffer[JERRY_DEBUGGER_MAX_BUFFER_SIZE]; /**< buffer for sending messages */
|
||||
uint8_t debugger_receive_buffer[JERRY_DEBUGGER_MAX_BUFFER_SIZE]; /**< buffer for receiving messages */
|
||||
|
||||
@ -618,6 +618,9 @@ bool jerry_is_feature_enabled (const jerry_feature_t feature)
|
||||
#ifdef JERRY_DEBUGGER
|
||||
|| feature == JERRY_FEATURE_DEBUGGER
|
||||
#endif /* JERRY_DEBUGGER */
|
||||
#ifdef JERRY_VM_EXEC_STOP
|
||||
|| feature == JERRY_FEATURE_VM_EXEC_STOP
|
||||
#endif /* JERRY_VM_EXEC_STOP */
|
||||
);
|
||||
} /* jerry_is_feature_enabled */
|
||||
|
||||
@ -2145,6 +2148,33 @@ jerry_is_valid_cesu8_string (const jerry_char_t *cesu8_buf_p, /**< CESU-8 string
|
||||
(lit_utf8_size_t) buf_size);
|
||||
} /* jerry_is_valid_cesu8_string */
|
||||
|
||||
/**
|
||||
* If JERRY_VM_EXEC_STOP is defined the callback passed to this function is
|
||||
* periodically called with the user_p argument. If frequency is greater
|
||||
* than 1, the callback is only called at every frequency ticks.
|
||||
*/
|
||||
void
|
||||
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, /**< periodically called user function */
|
||||
void *user_p, /**< pointer passed to the function */
|
||||
uint32_t frequency) /**< frequency of the function call */
|
||||
{
|
||||
#ifdef JERRY_VM_EXEC_STOP
|
||||
if (frequency == 0)
|
||||
{
|
||||
frequency = 1;
|
||||
}
|
||||
|
||||
JERRY_CONTEXT (vm_exec_stop_frequency) = frequency;
|
||||
JERRY_CONTEXT (vm_exec_stop_counter) = frequency;
|
||||
JERRY_CONTEXT (vm_exec_stop_user_p) = user_p;
|
||||
JERRY_CONTEXT (vm_exec_stop_cb) = stop_cb;
|
||||
#else /* !JERRY_VM_EXEC_STOP */
|
||||
JERRY_UNUSED (stop_cb);
|
||||
JERRY_UNUSED (user_p);
|
||||
JERRY_UNUSED (frequency);
|
||||
#endif /* JERRY_VM_EXEC_STOP */
|
||||
} /* jerry_set_vm_exec_stop_callback */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@ -86,6 +86,7 @@ typedef enum
|
||||
JERRY_FEATURE_SNAPSHOT_SAVE, /**< saving snapshot files */
|
||||
JERRY_FEATURE_SNAPSHOT_EXEC, /**< executing snapshot files */
|
||||
JERRY_FEATURE_DEBUGGER, /**< debugging */
|
||||
JERRY_FEATURE_VM_EXEC_STOP, /**< stopping ECMAScript execution */
|
||||
JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */
|
||||
} jerry_feature_t;
|
||||
|
||||
@ -174,6 +175,19 @@ typedef void (*jerry_object_free_callback_t) (const uintptr_t native_p);
|
||||
*/
|
||||
typedef void (*jerry_object_native_free_callback_t) (void *native_p);
|
||||
|
||||
/**
|
||||
* Callback which tells whether the ECMAScript execution should be stopped.
|
||||
*
|
||||
* As long as the function returns with undefined the execution continues.
|
||||
* When a non-undefined value is returned the execution stops and the value
|
||||
* is thrown by the engine (if the error flag is not set for the returned
|
||||
* value the engine automatically sets it).
|
||||
*
|
||||
* Note: if the function returns with a non-undefined value it
|
||||
* must return with the same value for future calls.
|
||||
*/
|
||||
typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p);
|
||||
|
||||
/**
|
||||
* Function type applied for each data property of an object.
|
||||
*/
|
||||
@ -388,6 +402,11 @@ jerry_value_t jerry_exec_snapshot (const uint32_t *snapshot_p, size_t snapshot_s
|
||||
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);
|
||||
|
||||
/**
|
||||
* Miscellaneous functions.
|
||||
*/
|
||||
void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, void *user_p, uint32_t frequency);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@ -847,6 +847,29 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
|
||||
if (opcode_data & VM_OC_BACKWARD_BRANCH)
|
||||
{
|
||||
#ifdef JERRY_VM_EXEC_STOP
|
||||
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL
|
||||
&& --JERRY_CONTEXT (vm_exec_stop_counter) == 0)
|
||||
{
|
||||
result = JERRY_CONTEXT (vm_exec_stop_cb) (JERRY_CONTEXT (vm_exec_stop_user_p));
|
||||
|
||||
if (ecma_is_value_undefined (result))
|
||||
{
|
||||
JERRY_CONTEXT (vm_exec_stop_counter) = JERRY_CONTEXT (vm_exec_stop_frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_CONTEXT (vm_exec_stop_counter) = 1;
|
||||
|
||||
if (!ECMA_IS_VALUE_ERROR (result))
|
||||
{
|
||||
result = ecma_make_error_value (result);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif /* JERRY_VM_EXEC_STOP */
|
||||
|
||||
branch_offset = -branch_offset;
|
||||
}
|
||||
}
|
||||
@ -2595,6 +2618,33 @@ error:
|
||||
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW);
|
||||
stack_top_p[-2] = result;
|
||||
}
|
||||
|
||||
#ifdef JERRY_VM_EXEC_STOP
|
||||
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL
|
||||
&& --JERRY_CONTEXT (vm_exec_stop_counter) == 0)
|
||||
{
|
||||
result = JERRY_CONTEXT (vm_exec_stop_cb) (JERRY_CONTEXT (vm_exec_stop_user_p));
|
||||
|
||||
if (ecma_is_value_undefined (result))
|
||||
{
|
||||
JERRY_CONTEXT (vm_exec_stop_counter) = JERRY_CONTEXT (vm_exec_stop_frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_CONTEXT (vm_exec_stop_counter) = 1;
|
||||
|
||||
left_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
||||
right_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
|
||||
|
||||
if (!ECMA_IS_VALUE_ERROR (result))
|
||||
{
|
||||
result = ecma_make_error_value (result);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif /* JERRY_VM_EXEC_STOP */
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,6 +171,21 @@ handler_construct (const jerry_value_t func_obj_val, /**< function object */
|
||||
return jerry_create_boolean (true);
|
||||
} /* handler_construct */
|
||||
|
||||
static jerry_value_t
|
||||
vm_exec_stop_callback (void *user_p)
|
||||
{
|
||||
int *int_p = (int *) user_p;
|
||||
|
||||
if (*int_p > 0)
|
||||
{
|
||||
(*int_p)--;
|
||||
|
||||
return jerry_create_undefined ();
|
||||
}
|
||||
|
||||
return jerry_create_string ((const jerry_char_t *) "Abort script");
|
||||
} /* vm_exec_stop_callback */
|
||||
|
||||
/**
|
||||
* Extended Magic Strings
|
||||
*/
|
||||
@ -1016,6 +1031,50 @@ main (void)
|
||||
jerry_cleanup ();
|
||||
}
|
||||
|
||||
/* Test stopping an infinite loop. */
|
||||
if (jerry_is_feature_enabled (JERRY_FEATURE_VM_EXEC_STOP))
|
||||
{
|
||||
jerry_init (JERRY_INIT_EMPTY);
|
||||
|
||||
int countdown = 6;
|
||||
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
|
||||
|
||||
const char *inf_loop_code_src_p = "while(true) {}";
|
||||
parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, strlen (inf_loop_code_src_p), false);
|
||||
TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val));
|
||||
res = jerry_run (parsed_code_val);
|
||||
TEST_ASSERT (countdown == 0);
|
||||
|
||||
TEST_ASSERT (jerry_value_has_error_flag (res));
|
||||
|
||||
jerry_release_value (res);
|
||||
jerry_release_value (parsed_code_val);
|
||||
|
||||
/* A more complex example. Although the callback error is captured
|
||||
* by the catch block, it is automatically thrown again. */
|
||||
|
||||
/* We keep the callback function, only the countdown is reset. */
|
||||
countdown = 6;
|
||||
|
||||
inf_loop_code_src_p = ("function f() { while (true) ; }\n"
|
||||
"try { f(); } catch(e) {}");
|
||||
|
||||
parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, strlen (inf_loop_code_src_p), false);
|
||||
|
||||
TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val));
|
||||
res = jerry_run (parsed_code_val);
|
||||
TEST_ASSERT (countdown == 0);
|
||||
|
||||
/* The result must have an error flag which proves that
|
||||
* the error is thrown again inside the catch block. */
|
||||
TEST_ASSERT (jerry_value_has_error_flag (res));
|
||||
|
||||
jerry_release_value (res);
|
||||
jerry_release_value (parsed_code_val);
|
||||
|
||||
jerry_cleanup ();
|
||||
}
|
||||
|
||||
/* External Magic String */
|
||||
jerry_init (JERRY_INIT_SHOW_OPCODES);
|
||||
|
||||
|
||||
@ -108,6 +108,8 @@ def get_arguments():
|
||||
help='build unittests')
|
||||
parser.add_argument('-v', '--verbose', action='store_const', const='ON', default='OFF',
|
||||
help='increase verbosity')
|
||||
parser.add_argument('--vm-exec-stop', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper,
|
||||
help='enable VM execution stopping (%(choices)s; default: %(default)s)')
|
||||
|
||||
devgroup = parser.add_argument_group('developer options')
|
||||
devgroup.add_argument('--link-map', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper,
|
||||
@ -161,6 +163,7 @@ def generate_build_options(arguments):
|
||||
build_options.append('-DFEATURE_SYSTEM_ALLOCATOR=%s' % arguments.system_allocator)
|
||||
build_options.append('-DENABLE_STATIC_LINK=%s' % arguments.static_link)
|
||||
build_options.append('-DENABLE_STRIP=%s' % arguments.strip)
|
||||
build_options.append('-DFEATURE_VM_EXEC_STOP=%s' % arguments.vm_exec_stop)
|
||||
|
||||
if arguments.toolchain:
|
||||
build_options.append('-DCMAKE_TOOLCHAIN_FILE=%s' % arguments.toolchain)
|
||||
|
||||
@ -42,9 +42,9 @@ def get_binary_path(bin_dir_path):
|
||||
# Test options for unittests
|
||||
JERRY_UNITTESTS_OPTIONS = [
|
||||
Options('unittests',
|
||||
['--unittests', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on']),
|
||||
['--unittests', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on', '--vm-exec-stop=on']),
|
||||
Options('unittests-debug',
|
||||
['--unittests', '--debug', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on'])
|
||||
['--unittests', '--debug', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on', '--vm-exec-stop=on'])
|
||||
]
|
||||
|
||||
# Test options for jerry-tests
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user