Skip to content

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

Merged
merged 1 commit into from
Apr 28, 2017

Conversation

zherczeg
Copy link
Member

No description provided.

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,
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Sorry.

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
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me!


int countdown = 6;

jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
Copy link
Contributor

@jiangzidong jiangzidong Apr 21, 2017

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?

Copy link
Member Author

@zherczeg zherczeg Apr 21, 2017

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).

Copy link
Contributor

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.

Copy link
Member Author

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) {
  }
}

Copy link
Member Author

@zherczeg zherczeg Apr 21, 2017

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your explanation :)

static jerry_value_t
vm_exec_stop_callback (void *user_p)
{
static countdown = 10;
Copy link
Contributor

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;

Copy link
Member Author

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.

@zherczeg
Copy link
Member Author

Patch updated, all comments addressed.

@LaszloLango
Copy link
Contributor

Please rebase to the current master.

@zherczeg zherczeg force-pushed the stop_js_exec branch 2 times, most recently from f9cf94a to 3f9b94e Compare April 24, 2017 09:42
@zherczeg
Copy link
Member Author

Rebased.

@zherczeg zherczeg force-pushed the stop_js_exec branch 2 times, most recently from 77be87f to 270791f Compare April 25, 2017 12:29
@@ -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
Copy link
Member

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.

@@ -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
Copy link
Member

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 */
Copy link
Member

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?

Copy link
Member Author

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

@@ -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 */
Copy link
Member

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

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);
}
Copy link
Contributor

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)


Copy link
Contributor

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_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);
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

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.

@zherczeg
Copy link
Member Author

Patch updated, please check.

Copy link
Contributor

@LaszloLango LaszloLango left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ECMAScript (note the 'c')

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

threw

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
Copy link
Member

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)

Copy link
Member Author

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.

Copy link
Member

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?

while (countdown > 0)
{
countdown--;
return jerry_create_undefined();
Copy link
Member

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)

}

// The error flag is added automatically.
return jerry_create_string((const jerry_char_t *) "Abort script");
Copy link
Member

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,
Copy link
Member

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.

#ifdef JERRY_VM_EXEC_STOP
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL)
{
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge the two ifs?

if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL && --JERRY_CONTEXT (vm_exec_stop_counter) == 0)

Copy link
Member Author

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.

Copy link
Member

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)

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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.

#ifdef JERRY_VM_EXEC_STOP
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL)
{
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise, merge the two ifs?

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)')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nitpicking) VM?

* 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);
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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).

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member

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.

#ifdef JERRY_VM_EXEC_STOP
if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL)
{
if (--JERRY_CONTEXT (vm_exec_stop_counter) == 0)
Copy link
Member

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.

* 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);
Copy link
Member

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]
@zherczeg
Copy link
Member Author

Follow-up: #1791

Copy link
Member

@akosthekiss akosthekiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zherczeg zherczeg merged commit 894aa6d into jerryscript-project:master Apr 28, 2017
@zherczeg zherczeg deleted the stop_js_exec branch April 28, 2017 12:19
jiangzidong pushed a commit to jiangzidong/jerryscript that referenced this pull request May 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants