-
Notifications
You must be signed in to change notification settings - Fork 683
Support ECMAScript stopping in JerryScript. #1753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
tools/build.py
Outdated
@@ -109,6 +109,8 @@ def devhelp(helpstring): | |||
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='ON', type=str.upper, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you also need to disable the vm execution stopping
by default in here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Sorry.
docs/02.API-REFERENCE.md
Outdated
JerryScript executes an ECMASCript program. | ||
|
||
If the callback returns with undefined value the ECMAScript execution | ||
continues. Otherwise the result is converted to an error and thrown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't it make more sense to let the callback return an Error
as opposed to letting the engine convert it into an error?
That way the callback can decide what Error
(sub)type to use for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would make the return value processing a bit more difficult since we would need to free the returned value, but would make the code more robust. If this is an acceptable overhead, I can change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if I follow.. If the return value is a Jerry string you don't need to free it while if it's an object (Error
instance) you do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not error case, but the normal case. If we return with undefined, we don't need to do anything (it is a primitive value). But if we allow to return with anything in the normal case, we need to prepare for complex objects, and free them.
Alternative suggestion. Return with:
- undefined (with or without error flag): do nothing
- anything else: if the value does not have an error flag, automatically add it. Throw the value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me!
tests/unit/test-api.c
Outdated
|
||
int countdown = 6; | ||
|
||
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a question to verify:
if the JS code do contains useful loop, e.g.
for (i = 0; i<6*16; i++)
{
a[i] = process(i);
}
or multiple loops but the total VM_OC_BACKWARD_BRANCH bytecode exceeds 6*16, the case will also be stopped here, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand this question. In your case the loop will probably stop at 6*16-1
index (a function start check and 6*16-1
backward loop checks).
To reduce the overhead the check only happens at backward loops and stack rollbacks (includes function entries).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, it's a bad example.
I wanted to ask whether the current design only count the VM_OC_BACKWARD_BRANCH, and then if countdown to 0, the user provided stop_cb will be invoked. And now I know the answer is YES.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be more precise it counts the backward branches, function entries and stack rollbacks, but in your case there is only a single function entry (the global code is a function).
This code stops sooner:
for (i = 0; i<6*16; i++)
{
try {
throw 1
} catch(e) {
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before you ask: why it counts stack rollbacks? Because we throw an exception which cause a stack rollback and an active catch block catches this exception and restart executing the byte code. However, our termination check runs again just before the code is executed, and raise another exception. At some point the stack will fully rolled back and the actual function returns with the exception. Then stack roll back start for the callee function.
Hm, function entry checks might not be needed. This could further optimize this code. But I need to think over this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your explanation :)
docs/02.API-REFERENCE.md
Outdated
static jerry_value_t | ||
vm_exec_stop_callback (void *user_p) | ||
{ | ||
static countdown = 10; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably, static int countdown = 10;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! Thanks for catching it.
Patch updated, all comments addressed. |
Please rebase to the current master. |
f9cf94a
to
3f9b94e
Compare
Rebased. |
77be87f
to
270791f
Compare
docs/02.API-REFERENCE.md
Outdated
@@ -35,7 +35,8 @@ Possible compile time enabled feature types: | |||
- JERRY_FEATURE_REGEXP_DUMP - regexp byte-code dumps | |||
- JERRY_FEATURE_SNAPSHOT_SAVE - saving snapshot files | |||
- JERRY_FEATURE_SNAPSHOT_EXEC - executing snapshot files | |||
- JERRY_FEATURE_DEBUGGER - debugging | |||
- JERRY_FEATURE_DEBUGGER - debugger server is available |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? It is both unrelated and makes the typedef out of sync from the doc.
jerry-core/jerry.c
Outdated
@@ -2149,6 +2152,31 @@ jerry_is_valid_cesu8_string (const jerry_char_t *cesu8_buf_p, /**< CESU-8 string | |||
} /* jerry_is_valid_cesu8_string */ | |||
|
|||
/** | |||
* If JERRY_VM_EXEC_STOP is defined the periodically called callback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incomplete sentence?
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 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the unit of frequency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is described in the documentation. There is no real unit. If the callback could be called N
times, it is actually called floor(N/frequency)
times
jerry-core/jerryscript.h
Outdated
@@ -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, /**< vm execution stopping enabled */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is also not the same text as the one found in API docs
docs/02.API-REFERENCE.md
Outdated
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); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing jerry_cleanup ();
@@ -3896,3 +3897,76 @@ 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) | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on jerryscript.h
: add a new top level header here: # Miscellaneous functions.
jerry-core/vm/vm.c
Outdated
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left_value
and right_value
shouldn't be freed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is freed after the error:
label.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I wanted to write that the free happens after error:
label above, and when we reach this position, the variables hold garbage. Since we go back to error:
we would encounter a double free issue.
Patch updated, please check. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
docs/02.API-REFERENCE.md
Outdated
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ECMAScript (note the 'c')
docs/02.API-REFERENCE.md
Outdated
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 thrown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
threw
docs/02.API-REFERENCE.md
Outdated
Frequently calling a callback in the main executor loop reduces | ||
its performance. The `frequency` argument allows decreasing | ||
this overhead. If its value is `N`, the callback is called only | ||
every `N` times. The minimum value of `N` is `1` (`0` is converted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know I've asked that before but it's still not clear to me, at least from the doc, what this exactly means. "every N
times" of what? Only after every N cycles of the VM, i.e., after the execution of every N byte code? Or after the execution of every N JS statement? Or something else? (I should not have to read the code to understand it, IMHO)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please recommend me a good sentence then:
This is an extra layer for improving performance. Every time when the callback could be called, we decrease a counter, and only call the callback if it is 0. We also reset it back to frequency (that is why frequnce must be > 0, to avoid underflow). The dividers of CPU clock works this way. Basically if you set to to N then N-1 times the call is skipped, and only the Nth time it is executed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely understand what N is for. What's undocumented is what "Every time when the callback could be called" means. When could the callback be called? After/before every byte code? After/before every JS statement?
docs/02.API-REFERENCE.md
Outdated
while (countdown > 0) | ||
{ | ||
countdown--; | ||
return jerry_create_undefined(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add space before (
(our examples should also follow our style guide)
docs/02.API-REFERENCE.md
Outdated
} | ||
|
||
// The error flag is added automatically. | ||
return jerry_create_string((const jerry_char_t *) "Abort script"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add space before (
|
||
```c | ||
void | ||
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we document the signature of jerry_vm_exec_stop_callback_t
, too? Right now, it can only be deduced from the example.
jerry-core/vm/vm.c
Outdated
#ifdef JERRY_VM_EXEC_STOP | ||
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL) | ||
{ | ||
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merge the two if
s?
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL && --JERRY_CONTEXT (vm_exec_stop_counter) == 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how --
works in that case. The code is clearer this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is completely well-defined and standard-specified, there is no vagueness involved. &&
in C has short circuit evaluation. Please see ISO/IEC 9899 standard, section 6.5.13, "Logical AND operator" (http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf, http://www.open-std.org/JTC1/SC22/WG14/www/standards.html#9899)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had enough problems with --
before. It is mostly undefined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like the current code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really tempted to start a potentially long-running debate on "It is mostly undefined". (It is certainly not.) But I wont. Keep this as is.
jerry-core/vm/vm.c
Outdated
#ifdef JERRY_VM_EXEC_STOP | ||
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL) | ||
{ | ||
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
likewise, merge the two if
s?
tools/build.py
Outdated
@@ -108,6 +108,8 @@ def devhelp(helpstring): | |||
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)') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nitpicking) VM?
jerry-core/ecma/base/ecma-globals.h
Outdated
* Callback which returns with a non-undefined value | ||
* when the ECMAScript execution should be stopped. | ||
*/ | ||
typedef ecma_value_t (*jerry_vm_exec_stop_cb_t) (void *user_p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to figure out why this typedef duplication is needed (i.e., jerryscript.h
also has jerry_vm_exec_stop_callback_t
) but didn't succeed. We have other callback types in jerryscript.h
, too (e.g., jerry_external_handler_t
), but they have no duplicates internally. Couldn't we drop this also and use the single public typedef?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. jerryscript.h is not included inside jerry-core. I don't want to use external_pointer_t, because thet requires lot of type casting. This is easier to understand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is clearly not true. jerry-core/jrt/jrt.h
is including jerryscript.h
(line 22) and jerry-core/ecma/base/ecma-globals.h
is including jrt.h
(line 20).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very bad news, I am not aware of that. I don't think removing this dependency should be in this patch though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not bad news, this is how existing callback types are already used.
- jerry_external_handler_t in ecma-function-object.c
- jerry_object_free_callback_t in ecma-gc.c
- jerry_user_context_deinit_cb in jcontext.h
They are all defined in jerryscript.h and used in the engine. There is no "dependency to be removed". It's the callback typedef duplication that's to be removed from ecma-globals.h.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm strongly insisting on dropping this internal typedef.
jerry-core/vm/vm.c
Outdated
#ifdef JERRY_VM_EXEC_STOP | ||
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL) | ||
{ | ||
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really tempted to start a potentially long-running debate on "It is mostly undefined". (It is certainly not.) But I wont. Keep this as is.
jerry-core/ecma/base/ecma-globals.h
Outdated
* Callback which returns with a non-undefined value | ||
* when the ECMAScript execution should be stopped. | ||
*/ | ||
typedef ecma_value_t (*jerry_vm_exec_stop_cb_t) (void *user_p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm strongly insisting on dropping this internal typedef.
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
Follow-up: #1791 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
No description provided.