Skip to content

Commit 3f9b94e

Browse files
committed
Support ECMAScript stopping in JerryScript.
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
1 parent 849ea45 commit 3f9b94e

File tree

10 files changed

+265
-3
lines changed

10 files changed

+265
-3
lines changed

docs/02.API-REFERENCE.md

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ Possible compile time enabled feature types:
3535
- JERRY_FEATURE_REGEXP_DUMP - regexp byte-code dumps
3636
- JERRY_FEATURE_SNAPSHOT_SAVE - saving snapshot files
3737
- JERRY_FEATURE_SNAPSHOT_EXEC - executing snapshot files
38-
- JERRY_FEATURE_DEBUGGER - debugging
38+
- JERRY_FEATURE_DEBUGGER - debugger server is available
39+
- JERRY_FEATURE_VM_EXEC_STOP - stopping ECMAScript execution
3940

4041
## jerry_char_t
4142

@@ -3896,3 +3897,76 @@ jerry_parse_and_save_literals (const jerry_char_t *source_p,
38963897
- [jerry_init](#jerry_init)
38973898
- [jerry_cleanup](#jerry_cleanup)
38983899
- [jerry_register_magic_strings](#jerry_register_magic_strings)
3900+
3901+
3902+
## jerry_set_vm_exec_stop_callback
3903+
3904+
**Summary**
3905+
3906+
When JERRY_FEATURE_VM_EXEC_STOP is enabled a callback function can be
3907+
specified by this function. This callback is periodically called when
3908+
JerryScript executes an ECMASCript program.
3909+
3910+
If the callback returns with undefined value the ECMAScript execution
3911+
continues. Otherwise the result is thrown by the engine (if the error
3912+
flag is not set for the returned value the engine automatically sets
3913+
it). The callback function might be called again even if it thrown
3914+
an error. In this case the function must throw the same error again.
3915+
3916+
Frequently calling a callback in the main executor loop reduces
3917+
its performance. The `frequency` argument allows decreasing
3918+
this overhead. If its value is `N`, the callback is called only
3919+
every `N` times. The minimum value of `N` is `1` (`0` is converted
3920+
to `1` automatically).
3921+
3922+
**Prototype**
3923+
3924+
```c
3925+
void
3926+
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb,
3927+
void *user_p,
3928+
uint32_t frequency);
3929+
```
3930+
3931+
- `stop_cb` - periodically called callback (passing NULL disables this feature)
3932+
- `user_p` - user pointer passed to the `stop_cb` function
3933+
- `frequency` - frequency of calling the `stop_cb` function
3934+
3935+
**Example**
3936+
3937+
```c
3938+
static jerry_value_t
3939+
vm_exec_stop_callback (void *user_p)
3940+
{
3941+
static int countdown = 10;
3942+
3943+
while (countdown > 0)
3944+
{
3945+
countdown--;
3946+
return jerry_create_undefined();
3947+
}
3948+
3949+
// The error flag is added automatically.
3950+
return jerry_create_string((const jerry_char_t *) "Abort script");
3951+
}
3952+
3953+
{
3954+
jerry_init (JERRY_INIT_EMPTY);
3955+
3956+
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
3957+
3958+
// Inifinte loop.
3959+
const char *src_p = "while(true) {}";
3960+
3961+
jerry_value_t src = jerry_parse ((jerry_char_t *) src_p, strlen (src_p), false);
3962+
jerry_release_value (jerry_run (src));
3963+
jerry_release_value (src);
3964+
}
3965+
```
3966+
3967+
**See also**
3968+
3969+
- [jerry_init](#jerry_init)
3970+
- [jerry_cleanup](#jerry_cleanup)
3971+
- [jerry_parse](#jerry_parse)
3972+
- [jerry_run](#jerry_run)

jerry-core/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(FEATURE_SNAPSHOT_SAVE OFF CACHE BOOL "Enable saving snapshot files?
3232
set(FEATURE_SYSTEM_ALLOCATOR OFF CACHE BOOL "Enable system allocator?")
3333
set(FEATURE_VALGRIND OFF CACHE BOOL "Enable Valgrind support?")
3434
set(FEATURE_VALGRIND_FREYA OFF CACHE BOOL "Enable Valgrind-Freya support?")
35+
set(FEATURE_VM_EXEC_STOP OFF CACHE BOOL "Enable vm execution stopping?")
3536
set(MEM_HEAP_SIZE_KB "512" CACHE STRING "Size of memory heap, in kilobytes")
3637

3738
if(FEATURE_SYSTEM_ALLOCATOR)
@@ -54,6 +55,7 @@ message(STATUS "FEATURE_SNAPSHOT_SAVE " ${FEATURE_SNAPSHOT_SAVE})
5455
message(STATUS "FEATURE_SYSTEM_ALLOCATOR " ${FEATURE_SYSTEM_ALLOCATOR})
5556
message(STATUS "FEATURE_VALGRIND " ${FEATURE_VALGRIND})
5657
message(STATUS "FEATURE_VALGRIND_FREYA " ${FEATURE_VALGRIND_FREYA})
58+
message(STATUS "FEATURE_VM_EXEC_STOP " ${FEATURE_VM_EXEC_STOP})
5759
message(STATUS "MEM_HEAP_SIZE_KB " ${MEM_HEAP_SIZE_KB})
5860

5961
# Include directories
@@ -241,6 +243,11 @@ if(FEATURE_VALGRIND_FREYA)
241243
set(INCLUDE_CORE ${INCLUDE_CORE} ${INCLUDE_THIRD_PARTY_VALGRIND})
242244
endif()
243245

246+
# Enable VM execution stopping
247+
if (FEATURE_VM_EXEC_STOP)
248+
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_VM_EXEC_STOP)
249+
endif()
250+
244251
# Size of heap
245252
math(EXPR MEM_HEAP_AREA_SIZE "${MEM_HEAP_SIZE_KB} * 1024")
246253
set(DEFINES_JERRY ${DEFINES_JERRY} CONFIG_MEM_HEAP_AREA_SIZE=${MEM_HEAP_AREA_SIZE})

jerry-core/ecma/base/ecma-globals.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,16 @@ typedef struct
204204
void *info_p; /**< free info or callback */
205205
} ecma_native_pointer_t;
206206

207+
#ifdef JERRY_VM_EXEC_STOP
208+
209+
/**
210+
* Callback which returns with a non-undefined value
211+
* when the ECMAScript execution should be stopped.
212+
*/
213+
typedef ecma_value_t (*jerry_vm_exec_stop_cb_t) (void *user_p);
214+
215+
#endif /* JERRY_VM_EXEC_STOP */
216+
207217
/**
208218
* Special property identifiers.
209219
*/

jerry-core/jcontext/jcontext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ typedef struct
8484
uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) */
8585
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
8686

87+
#ifdef JERRY_VM_EXEC_STOP
88+
uint32_t vm_exec_stop_frequency; /**< reset value for vm_exec_stop_counter */
89+
uint32_t vm_exec_stop_counter; /**< down counter for reducing the calls of vm_exec_stop_cb */
90+
void *vm_exec_stop_user_p; /**< user pointer for vm_exec_stop_cb */
91+
jerry_vm_exec_stop_cb_t vm_exec_stop_cb; /**< user function which returns whether the
92+
* ECMAScript execution should be stopped */
93+
#endif /* JERRY_VM_EXEC_STOP */
94+
8795
#ifdef JERRY_DEBUGGER
8896
uint8_t debugger_send_buffer[JERRY_DEBUGGER_MAX_BUFFER_SIZE]; /**< buffer for sending messages */
8997
uint8_t debugger_receive_buffer[JERRY_DEBUGGER_MAX_BUFFER_SIZE]; /**< buffer for receiving messages */

jerry-core/jerry.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,9 @@ bool jerry_is_feature_enabled (const jerry_feature_t feature)
621621
#ifdef JERRY_DEBUGGER
622622
|| feature == JERRY_FEATURE_DEBUGGER
623623
#endif /* JERRY_DEBUGGER */
624+
#ifdef JERRY_VM_EXEC_STOP
625+
|| feature == JERRY_FEATURE_VM_EXEC_STOP
626+
#endif /* JERRY_VM_EXEC_STOP */
624627
);
625628
} /* jerry_is_feature_enabled */
626629

@@ -2143,6 +2146,31 @@ jerry_is_valid_cesu8_string (const jerry_char_t *cesu8_buf_p, /**< CESU-8 string
21432146
(lit_utf8_size_t) buf_size);
21442147
} /* jerry_is_valid_cesu8_string */
21452148

2149+
/**
2150+
* If JERRY_VM_EXEC_STOP is defined the periodically called callback
2151+
*/
2152+
void
2153+
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, /**< periodically called user function */
2154+
void *user_p, /**< pointer passed to the function */
2155+
uint32_t frequency) /**< frequency of the function call */
2156+
{
2157+
#ifdef JERRY_VM_EXEC_STOP
2158+
if (frequency == 0)
2159+
{
2160+
frequency = 1;
2161+
}
2162+
2163+
JERRY_CONTEXT (vm_exec_stop_frequency) = frequency;
2164+
JERRY_CONTEXT (vm_exec_stop_counter) = frequency;
2165+
JERRY_CONTEXT (vm_exec_stop_user_p) = user_p;
2166+
JERRY_CONTEXT (vm_exec_stop_cb) = stop_cb;
2167+
#else /* !JERRY_VM_EXEC_STOP */
2168+
JERRY_UNUSED (stop_cb);
2169+
JERRY_UNUSED (user_p);
2170+
JERRY_UNUSED (frequency);
2171+
#endif /* JERRY_VM_EXEC_STOP */
2172+
} /* jerry_set_vm_exec_stop_callback */
2173+
21462174
/**
21472175
* @}
21482176
*/

jerry-core/jerryscript.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ typedef enum
8686
JERRY_FEATURE_SNAPSHOT_SAVE, /**< saving snapshot files */
8787
JERRY_FEATURE_SNAPSHOT_EXEC, /**< executing snapshot files */
8888
JERRY_FEATURE_DEBUGGER, /**< debugging */
89+
JERRY_FEATURE_VM_EXEC_STOP, /**< vm execution stopping enabled */
8990
JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */
9091
} jerry_feature_t;
9192

@@ -174,6 +175,19 @@ typedef void (*jerry_object_free_callback_t) (const uintptr_t native_p);
174175
*/
175176
typedef void (*jerry_object_native_free_callback_t) (void *native_p);
176177

178+
/**
179+
* Callback which tells whether the ECMAScript execution should be stopped.
180+
*
181+
* As long as the function returns with undefined the execution continues.
182+
* When a non-undefined value is returned the execution stops and the value
183+
* is thrown by the engine (if the error flag is not set for the returned
184+
* value the engine automatically sets it).
185+
*
186+
* Note: if the function returns with a non-undefined value it
187+
* must return with the same value for future calls.
188+
*/
189+
typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p);
190+
177191
/**
178192
* Function type applied for each data property of an object.
179193
*/
@@ -388,6 +402,11 @@ jerry_value_t jerry_exec_snapshot (const uint32_t *snapshot_p, size_t snapshot_s
388402
size_t jerry_parse_and_save_literals (const jerry_char_t *source_p, size_t source_size, bool is_strict,
389403
uint32_t *buffer_p, size_t buffer_size, bool is_c_format);
390404

405+
/**
406+
* Miscellaneous functions.
407+
*/
408+
void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, void *user_p, uint32_t frequency);
409+
391410
/**
392411
* @}
393412
*/

jerry-core/vm/vm.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,31 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
847847

848848
if (opcode_data & VM_OC_BACKWARD_BRANCH)
849849
{
850+
#ifdef JERRY_VM_EXEC_STOP
851+
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL)
852+
{
853+
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0)
854+
{
855+
result = JERRY_CONTEXT (vm_exec_stop_cb) (JERRY_CONTEXT (vm_exec_stop_user_p));
856+
857+
if (ecma_is_value_undefined (result))
858+
{
859+
JERRY_CONTEXT (vm_exec_stop_counter) = JERRY_CONTEXT (vm_exec_stop_frequency);
860+
}
861+
else
862+
{
863+
JERRY_CONTEXT (vm_exec_stop_counter) = 1;
864+
865+
if (!ECMA_IS_VALUE_ERROR (result))
866+
{
867+
result = ecma_make_error_value (result);
868+
}
869+
goto error;
870+
}
871+
}
872+
}
873+
#endif /* JERRY_VM_EXEC_STOP */
874+
850875
branch_offset = -branch_offset;
851876
}
852877
}
@@ -2595,6 +2620,35 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
25952620
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW);
25962621
stack_top_p[-2] = result;
25972622
}
2623+
2624+
#ifdef JERRY_VM_EXEC_STOP
2625+
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL)
2626+
{
2627+
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0)
2628+
{
2629+
result = JERRY_CONTEXT (vm_exec_stop_cb) (JERRY_CONTEXT (vm_exec_stop_user_p));
2630+
2631+
if (ecma_is_value_undefined (result))
2632+
{
2633+
JERRY_CONTEXT (vm_exec_stop_counter) = JERRY_CONTEXT (vm_exec_stop_frequency);
2634+
}
2635+
else
2636+
{
2637+
JERRY_CONTEXT (vm_exec_stop_counter) = 1;
2638+
2639+
left_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
2640+
right_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
2641+
2642+
if (!ECMA_IS_VALUE_ERROR (result))
2643+
{
2644+
result = ecma_make_error_value (result);
2645+
}
2646+
goto error;
2647+
}
2648+
}
2649+
}
2650+
#endif /* JERRY_VM_EXEC_STOP */
2651+
25982652
continue;
25992653
}
26002654
}

tests/unit-core/test-api.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,21 @@ handler_construct (const jerry_value_t func_obj_val, /**< function object */
170170
return jerry_create_boolean (true);
171171
} /* handler_construct */
172172

173+
static jerry_value_t
174+
vm_exec_stop_callback (void *user_p)
175+
{
176+
int *int_p = (int *) user_p;
177+
178+
if (*int_p > 0)
179+
{
180+
(*int_p)--;
181+
182+
return jerry_create_undefined ();
183+
}
184+
185+
return jerry_create_string ((const jerry_char_t *) "Abort script");
186+
} /* vm_exec_stop_callback */
187+
173188
/**
174189
* Extended Magic Strings
175190
*/
@@ -1002,6 +1017,50 @@ main (void)
10021017
jerry_cleanup ();
10031018
}
10041019

1020+
/* Test stopping an infinite loop. */
1021+
if (jerry_is_feature_enabled (JERRY_FEATURE_VM_EXEC_STOP))
1022+
{
1023+
jerry_init (JERRY_INIT_EMPTY);
1024+
1025+
int countdown = 6;
1026+
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
1027+
1028+
const char *inf_loop_code_src_p = "while(true) {}";
1029+
parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, strlen (inf_loop_code_src_p), false);
1030+
TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val));
1031+
res = jerry_run (parsed_code_val);
1032+
TEST_ASSERT (countdown == 0);
1033+
1034+
TEST_ASSERT (jerry_value_has_error_flag (res));
1035+
1036+
jerry_release_value (res);
1037+
jerry_release_value (parsed_code_val);
1038+
1039+
/* A more complex example. Although the callback error is captured
1040+
* by the catch block, it is automatically thrown again. */
1041+
1042+
/* We keep the callback function, only the countdown is reset. */
1043+
countdown = 6;
1044+
1045+
inf_loop_code_src_p = "function f() { while (true) ; }\n"
1046+
"try { f(); } catch(e) {}";
1047+
1048+
parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, strlen (inf_loop_code_src_p), false);
1049+
1050+
TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val));
1051+
res = jerry_run (parsed_code_val);
1052+
TEST_ASSERT (countdown == 0);
1053+
1054+
/* The result must have an error flag which proves that
1055+
* the error is thrown again inside the catch block. */
1056+
TEST_ASSERT (jerry_value_has_error_flag (res));
1057+
1058+
jerry_release_value (res);
1059+
jerry_release_value (parsed_code_val);
1060+
1061+
jerry_cleanup ();
1062+
}
1063+
10051064
/* External Magic String */
10061065
jerry_init (JERRY_INIT_SHOW_OPCODES);
10071066

tools/build.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ def devhelp(helpstring):
109109
help='build unittests')
110110
parser.add_argument('-v', '--verbose', action='store_const', const='ON', default='OFF',
111111
help='increase verbosity')
112+
parser.add_argument('--vm-exec-stop', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper,
113+
help='enable vm execution stopping (%(choices)s; default: %(default)s)')
112114

113115
devgroup = parser.add_argument_group('developer options')
114116
devgroup.add_argument('--link-map', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper,
@@ -162,6 +164,7 @@ def generate_build_options(arguments):
162164
build_options.append('-DFEATURE_SYSTEM_ALLOCATOR=%s' % arguments.system_allocator)
163165
build_options.append('-DENABLE_STATIC_LINK=%s' % arguments.static_link)
164166
build_options.append('-DENABLE_STRIP=%s' % arguments.strip)
167+
build_options.append('-DFEATURE_VM_EXEC_STOP=%s' % arguments.vm_exec_stop)
165168

166169
if arguments.toolchain:
167170
build_options.append('-DCMAKE_TOOLCHAIN_FILE=%s' % arguments.toolchain)

tools/run-tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ def get_binary_path(bin_dir_path):
4242
# Test options for unittests
4343
JERRY_UNITTESTS_OPTIONS = [
4444
Options('unittests',
45-
['--unittests', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on']),
45+
['--unittests', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on', '--vm-exec-stop=on']),
4646
Options('unittests-debug',
47-
['--unittests', '--debug', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on'])
47+
['--unittests', '--debug', '--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on', '--vm-exec-stop=on'])
4848
]
4949

5050
# Test options for jerry-tests

0 commit comments

Comments
 (0)