Created
December 7, 2018 18:08
-
-
Save xeioex/88e4361649fb4c8d82950cbe499c7a70 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1544204674 -10800 | |
| # Fri Dec 07 20:44:34 2018 +0300 | |
| # Node ID 6b84f91045f6c35810ff619c2434e8e7ffa9d3da | |
| # Parent 0709c3d38212df011229fccb2e7a9e7f23263cce | |
| Fixed handling of immediate posted events. | |
| diff --git a/njs/njs.c b/njs/njs.c | |
| --- a/njs/njs.c | |
| +++ b/njs/njs.c | |
| @@ -10,6 +10,8 @@ | |
| static nxt_int_t njs_vm_init(njs_vm_t *vm); | |
| +static nxt_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, | |
| + const njs_value_t *args, nxt_uint_t nargs, nxt_bool_t process_events); | |
| static nxt_int_t njs_vm_handle_events(njs_vm_t *vm); | |
| @@ -196,7 +198,7 @@ njs_vm_destroy(njs_vm_t *vm) | |
| njs_event_t *event; | |
| nxt_lvlhsh_each_t lhe; | |
| - if (njs_is_pending_events(vm)) { | |
| + if (njs_pending_events(vm)) { | |
| nxt_lvlhsh_each_init(&lhe, &njs_event_hash_proto); | |
| for ( ;; ) { | |
| @@ -457,13 +459,13 @@ njs_vm_init(njs_vm_t *vm) | |
| } | |
| -nxt_int_t | |
| -njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, | |
| - nxt_uint_t nargs) | |
| +static nxt_int_t | |
| +njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, | |
| + nxt_uint_t nargs, nxt_bool_t process_events) | |
| { | |
| u_char *current; | |
| njs_ret_t ret; | |
| - njs_value_t *this; | |
| + njs_value_t *this, retval; | |
| static const njs_vmcode_stop_t stop[] = { | |
| { .code = { .operation = njs_vmcode_stop, | |
| @@ -491,11 +493,32 @@ njs_vm_call(njs_vm_t *vm, njs_function_t | |
| vm->current = current; | |
| - if (ret == NJS_STOP) { | |
| - ret = NXT_OK; | |
| + if (process_events && ret == NJS_STOP) { | |
| + retval = vm->retval; | |
| + ret = njs_vm_handle_events(vm); | |
| + | |
| + if (ret != NJS_ERROR) { | |
| + vm->retval = retval; | |
| + } | |
| } | |
| - return ret; | |
| + switch (ret) { | |
| + case NJS_STOP: | |
| + case NXT_AGAIN: | |
| + return NJS_OK; | |
| + | |
| + case NXT_ERROR: | |
| + default: | |
| + return ret; | |
| + } | |
| +} | |
| + | |
| + | |
| +nxt_int_t | |
| +njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, | |
| + nxt_uint_t nargs) | |
| +{ | |
| + return njs_vm_invoke(vm, function, args, nargs, 1); | |
| } | |
| @@ -540,7 +563,7 @@ njs_vm_del_event(njs_vm_t *vm, njs_vm_ev | |
| nxt_int_t | |
| njs_vm_pending(njs_vm_t *vm) | |
| { | |
| - return njs_is_pending_events(vm); | |
| + return njs_pending_events(vm) || njs_posted_events(vm); | |
| } | |
| @@ -575,7 +598,8 @@ njs_vm_post_event(njs_vm_t *vm, njs_vm_e | |
| nxt_int_t | |
| njs_vm_run(njs_vm_t *vm) | |
| { | |
| - nxt_int_t ret; | |
| + nxt_int_t ret; | |
| + njs_value_t retval; | |
| if (nxt_slow_path(vm->backtrace != NULL)) { | |
| nxt_array_reset(vm->backtrace); | |
| @@ -584,14 +608,19 @@ njs_vm_run(njs_vm_t *vm) | |
| ret = njs_vmcode_interpreter(vm); | |
| if (ret == NJS_STOP) { | |
| + retval = vm->retval; | |
| ret = njs_vm_handle_events(vm); | |
| + | |
| + if (ret != NJS_ERROR) { | |
| + vm->retval = retval; | |
| + } | |
| } | |
| switch (ret) { | |
| case NJS_STOP: | |
| + case NXT_AGAIN: | |
| return NJS_OK; | |
| - case NXT_AGAIN: | |
| case NXT_ERROR: | |
| default: | |
| return ret; | |
| @@ -626,14 +655,14 @@ njs_vm_handle_events(njs_vm_t *vm) | |
| nxt_queue_remove(&ev->link); | |
| } | |
| - ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs); | |
| + ret = njs_vm_invoke(vm, ev->function, ev->args, ev->nargs, 0); | |
| if (ret == NJS_ERROR) { | |
| return ret; | |
| } | |
| } | |
| - return njs_is_pending_events(vm) ? NJS_AGAIN : NJS_STOP; | |
| + return njs_pending_events(vm) ? NJS_AGAIN : NJS_STOP; | |
| } | |
| diff --git a/njs/njs_event.h b/njs/njs_event.h | |
| --- a/njs/njs_event.h | |
| +++ b/njs/njs_event.h | |
| @@ -12,7 +12,9 @@ | |
| #define NJS_EVENT_DELETE 2 | |
| -#define njs_is_pending_events(vm) (!nxt_lvlhsh_is_empty(&(vm)->events_hash)) | |
| +#define njs_pending_events(vm) (!nxt_lvlhsh_is_empty(&(vm)->events_hash)) | |
| + | |
| +#define njs_posted_events(vm) (!nxt_queue_is_empty(&(vm)->posted_events)) | |
| typedef struct { | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1544204675 -10800 | |
| # Fri Dec 07 20:44:35 2018 +0300 | |
| # Node ID 5aeb70f46d6a285394bb9b6c37e6693b063f3fe6 | |
| # Parent 6b84f91045f6c35810ff619c2434e8e7ffa9d3da | |
| Added setImmediate(). | |
| In addition, it is possible now to use both setTimeout(fun, 0) and | |
| setImmediate() without timers support from the host environment. | |
| This closes #66 issue on Github. | |
| diff --git a/njs/njs.c b/njs/njs.c | |
| --- a/njs/njs.c | |
| +++ b/njs/njs.c | |
| @@ -524,7 +524,7 @@ njs_vm_call(njs_vm_t *vm, njs_function_t | |
| njs_vm_event_t | |
| njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, nxt_uint_t once, | |
| - njs_host_event_t host_ev, njs_event_destructor destructor) | |
| + njs_host_event_t host_ev, njs_event_destructor_t destructor) | |
| { | |
| njs_event_t *event; | |
| diff --git a/njs/njs.h b/njs/njs.h | |
| --- a/njs/njs.h | |
| +++ b/njs/njs.h | |
| @@ -125,15 +125,15 @@ typedef void * njs_ | |
| typedef void * njs_host_event_t; | |
| typedef void * njs_external_ptr_t; | |
| -typedef njs_host_event_t (*njs_set_timer)(njs_external_ptr_t external, | |
| +typedef njs_host_event_t (*njs_set_timer_t)(njs_external_ptr_t external, | |
| uint64_t delay, njs_vm_event_t vm_event); | |
| -typedef void (*njs_event_destructor)(njs_external_ptr_t external, | |
| +typedef void (*njs_event_destructor_t)(njs_external_ptr_t external, | |
| njs_host_event_t event); | |
| typedef struct { | |
| - njs_set_timer set_timer; | |
| - njs_event_destructor clear_timer; | |
| + njs_set_timer_t set_timer; | |
| + njs_event_destructor_t clear_timer; | |
| } njs_vm_ops_t; | |
| @@ -167,7 +167,7 @@ NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_ | |
| NXT_EXPORT njs_vm_event_t njs_vm_add_event(njs_vm_t *vm, | |
| njs_function_t *function, nxt_uint_t once, njs_host_event_t host_ev, | |
| - njs_event_destructor destructor); | |
| + njs_event_destructor_t destructor); | |
| NXT_EXPORT void njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event); | |
| NXT_EXPORT nxt_int_t njs_vm_pending(njs_vm_t *vm); | |
| NXT_EXPORT nxt_int_t njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event, | |
| diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c | |
| --- a/njs/njs_builtin.c | |
| +++ b/njs/njs_builtin.c | |
| @@ -111,6 +111,7 @@ const njs_object_init_t *njs_function | |
| &njs_decode_uri_component_function_init, | |
| &njs_require_function_init, | |
| &njs_set_timeout_function_init, | |
| + &njs_set_immediate_function_init, | |
| &njs_clear_timeout_function_init, | |
| NULL | |
| }; | |
| @@ -132,6 +133,8 @@ const njs_function_init_t njs_native_fu | |
| { njs_module_require, { NJS_SKIP_ARG, NJS_STRING_ARG } }, | |
| { njs_set_timeout, | |
| { NJS_SKIP_ARG, NJS_FUNCTION_ARG, NJS_NUMBER_ARG } }, | |
| + { njs_set_immediate, | |
| + { NJS_SKIP_ARG, NJS_FUNCTION_ARG } }, | |
| { njs_clear_timeout, { NJS_SKIP_ARG, NJS_NUMBER_ARG } }, | |
| }; | |
| diff --git a/njs/njs_event.h b/njs/njs_event.h | |
| --- a/njs/njs_event.h | |
| +++ b/njs/njs_event.h | |
| @@ -18,17 +18,17 @@ | |
| typedef struct { | |
| - njs_function_t *function; | |
| - njs_value_t *args; | |
| - nxt_uint_t nargs; | |
| - njs_host_event_t host_event; | |
| - njs_event_destructor destructor; | |
| + njs_function_t *function; | |
| + njs_value_t *args; | |
| + nxt_uint_t nargs; | |
| + njs_host_event_t host_event; | |
| + njs_event_destructor_t destructor; | |
| - njs_value_t id; | |
| - nxt_queue_link_t link; | |
| + njs_value_t id; | |
| + nxt_queue_link_t link; | |
| - unsigned posted:1; | |
| - unsigned once:1; | |
| + unsigned posted:1; | |
| + unsigned once:1; | |
| } njs_event_t; | |
| diff --git a/njs/njs_generator.c b/njs/njs_generator.c | |
| --- a/njs/njs_generator.c | |
| +++ b/njs/njs_generator.c | |
| @@ -387,6 +387,7 @@ njs_generator(njs_vm_t *vm, njs_generato | |
| case NJS_TOKEN_DECODE_URI_COMPONENT: | |
| case NJS_TOKEN_REQUIRE: | |
| case NJS_TOKEN_SET_TIMEOUT: | |
| + case NJS_TOKEN_SET_IMMEDIATE: | |
| case NJS_TOKEN_CLEAR_TIMEOUT: | |
| return njs_generate_builtin_object(vm, generator, node); | |
| diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c | |
| --- a/njs/njs_lexer_keyword.c | |
| +++ b/njs/njs_lexer_keyword.c | |
| @@ -90,6 +90,7 @@ static const njs_keyword_t njs_keywords | |
| { nxt_string("decodeURIComponent"), NJS_TOKEN_DECODE_URI_COMPONENT, 0 }, | |
| { nxt_string("require"), NJS_TOKEN_REQUIRE, 0 }, | |
| { nxt_string("setTimeout"), NJS_TOKEN_SET_TIMEOUT, 0 }, | |
| + { nxt_string("setImmediate"), NJS_TOKEN_SET_IMMEDIATE, 0 }, | |
| { nxt_string("clearTimeout"), NJS_TOKEN_CLEAR_TIMEOUT, 0 }, | |
| /* Reserved words. */ | |
| diff --git a/njs/njs_parser.c b/njs/njs_parser.c | |
| --- a/njs/njs_parser.c | |
| +++ b/njs/njs_parser.c | |
| @@ -2045,6 +2045,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa | |
| case NJS_TOKEN_DECODE_URI_COMPONENT: | |
| case NJS_TOKEN_REQUIRE: | |
| case NJS_TOKEN_SET_TIMEOUT: | |
| + case NJS_TOKEN_SET_IMMEDIATE: | |
| case NJS_TOKEN_CLEAR_TIMEOUT: | |
| return njs_parser_builtin_function(vm, parser, node); | |
| diff --git a/njs/njs_parser.h b/njs/njs_parser.h | |
| --- a/njs/njs_parser.h | |
| +++ b/njs/njs_parser.h | |
| @@ -198,6 +198,7 @@ typedef enum { | |
| NJS_TOKEN_DECODE_URI_COMPONENT, | |
| NJS_TOKEN_REQUIRE, | |
| NJS_TOKEN_SET_TIMEOUT, | |
| + NJS_TOKEN_SET_IMMEDIATE, | |
| NJS_TOKEN_CLEAR_TIMEOUT, | |
| NJS_TOKEN_RESERVED, | |
| diff --git a/njs/njs_time.c b/njs/njs_time.c | |
| --- a/njs/njs_time.c | |
| +++ b/njs/njs_time.c | |
| @@ -10,10 +10,12 @@ | |
| #include <stdio.h> | |
| -njs_ret_t | |
| -njs_set_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
| - njs_index_t unused) | |
| +static njs_ret_t | |
| +njs_set_timer(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
| + njs_index_t unused, nxt_bool_t immediate) | |
| { | |
| + njs_ret_t ret; | |
| + nxt_uint_t n; | |
| uint64_t delay; | |
| njs_event_t *event; | |
| njs_vm_ops_t *ops; | |
| @@ -28,26 +30,29 @@ njs_set_timeout(njs_vm_t *vm, njs_value_ | |
| return NJS_ERROR; | |
| } | |
| + delay = 0; | |
| + | |
| + if (!immediate && nargs >= 3 && njs_is_number(&args[2])) { | |
| + delay = args[2].data.u.number; | |
| + } | |
| + | |
| ops = vm->options.ops; | |
| - if (nxt_slow_path(ops == NULL)) { | |
| + | |
| + if (nxt_slow_path(ops == NULL && delay != 0)) { | |
| njs_internal_error(vm, "not supported by host environment"); | |
| return NJS_ERROR; | |
| } | |
| - delay = 0; | |
| - | |
| - if (nargs >= 3 && njs_is_number(&args[2])) { | |
| - delay = args[2].data.u.number; | |
| - } | |
| - | |
| event = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_event_t)); | |
| if (nxt_slow_path(event == NULL)) { | |
| goto memory_error; | |
| } | |
| - event->destructor = ops->clear_timer; | |
| + n = immediate ? 2 : 3; | |
| + | |
| + event->destructor = (ops != NULL) ? ops->clear_timer : NULL; | |
| event->function = args[1].data.u.function; | |
| - event->nargs = (nargs >= 3) ? nargs - 3 : 0; | |
| + event->nargs = (nargs >= n) ? nargs - n : 0; | |
| event->once = 1; | |
| event->posted = 0; | |
| @@ -58,16 +63,28 @@ njs_set_timeout(njs_vm_t *vm, njs_value_ | |
| goto memory_error; | |
| } | |
| - memcpy(event->args, &args[3], sizeof(njs_value_t) * event->nargs); | |
| + memcpy(event->args, &args[n], sizeof(njs_value_t) * event->nargs); | |
| } | |
| - event->host_event = ops->set_timer(vm->external, delay, event); | |
| - if (event->host_event == NULL) { | |
| - njs_internal_error(vm, "set_timer() failed"); | |
| + if (delay != 0) { | |
| + event->host_event = ops->set_timer(vm->external, delay, event); | |
| + if (nxt_slow_path(event->host_event == NULL)) { | |
| + njs_internal_error(vm, "set_timer() failed"); | |
| + return NJS_ERROR; | |
| + } | |
| + } | |
| + | |
| + ret = njs_add_event(vm, event); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| return NJS_ERROR; | |
| } | |
| - return njs_add_event(vm, event); | |
| + if (delay == 0) { | |
| + event->posted = 1; | |
| + nxt_queue_insert_tail(&vm->posted_events, &event->link); | |
| + } | |
| + | |
| + return NJS_OK; | |
| memory_error: | |
| @@ -78,6 +95,22 @@ memory_error: | |
| njs_ret_t | |
| +njs_set_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
| + njs_index_t unused) | |
| +{ | |
| + return njs_set_timer(vm, args, nargs, unused, 0); | |
| +} | |
| + | |
| + | |
| +njs_ret_t | |
| +njs_set_immediate(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
| + njs_index_t unused) | |
| +{ | |
| + return njs_set_timer(vm, args, nargs, unused, 1); | |
| +} | |
| + | |
| + | |
| +njs_ret_t | |
| njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
| njs_index_t unused) | |
| { | |
| @@ -116,6 +149,12 @@ const njs_object_init_t njs_set_timeout | |
| 0, | |
| }; | |
| +const njs_object_init_t njs_set_immediate_function_init = { | |
| + nxt_string("setImmediate"), | |
| + NULL, | |
| + 0, | |
| +}; | |
| + | |
| const njs_object_init_t njs_clear_timeout_function_init = { | |
| nxt_string("clearTimeout"), | |
| diff --git a/njs/njs_time.h b/njs/njs_time.h | |
| --- a/njs/njs_time.h | |
| +++ b/njs/njs_time.h | |
| @@ -10,11 +10,14 @@ | |
| njs_ret_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args, | |
| nxt_uint_t nargs, njs_index_t unused); | |
| +njs_ret_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args, | |
| + nxt_uint_t nargs, njs_index_t unused); | |
| njs_ret_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, | |
| nxt_uint_t nargs, njs_index_t unused); | |
| extern const njs_object_init_t njs_set_timeout_function_init; | |
| +extern const njs_object_init_t njs_set_immediate_function_init; | |
| extern const njs_object_init_t njs_clear_timeout_function_init; | |
| #endif /* _NJS_TIMEOUT_H_INCLUDED_ */ | |
| diff --git a/njs/njs_vm.h b/njs/njs_vm.h | |
| --- a/njs/njs_vm.h | |
| +++ b/njs/njs_vm.h | |
| @@ -930,6 +930,7 @@ enum njs_function_e { | |
| NJS_FUNCTION_STRING_DECODE_URI_COMPONENT, | |
| NJS_FUNCTION_REQUIRE, | |
| NJS_FUNCTION_SET_TIMEOUT, | |
| + NJS_FUNCTION_SET_IMMEDIATE, | |
| NJS_FUNCTION_CLEAR_TIMEOUT, | |
| #define NJS_FUNCTION_MAX (NJS_FUNCTION_CLEAR_TIMEOUT + 1) | |
| }; | |
| diff --git a/njs/test/njs_interactive_test.c b/njs/test/njs_interactive_test.c | |
| --- a/njs/test/njs_interactive_test.c | |
| +++ b/njs/test/njs_interactive_test.c | |
| @@ -110,6 +110,50 @@ static njs_interactive_test_t njs_test[ | |
| "function(){}()" ENTER), | |
| nxt_string("SyntaxError: Unexpected token \"(\" in 1") }, | |
| + /* Immediate events */ | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "setTimeout(function (x) {a = x}, 0, 'a'); a" ENTER), | |
| + nxt_string("0") }, | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "setTimeout(function (x) {a = x}, 0, 'a')" ENTER | |
| + "a" ENTER), | |
| + nxt_string("a") }, | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "setImmediate(function (x) {a = x}, 'a')" ENTER | |
| + "a" ENTER), | |
| + nxt_string("a") }, | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "setTimeout(function (x) {" | |
| + " setTimeout(function (y) {a = y}, 0, x)" | |
| + "}, 0, 5); a" ENTER | |
| + "a" ENTER), | |
| + nxt_string("5") }, | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "setImmediate(function (x) {" | |
| + " setImmediate(function (y) {a = y}, x)" | |
| + "}, 5); a" ENTER | |
| + "a" ENTER), | |
| + nxt_string("5") }, | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "setTimeout(function (x) {a = x}, 0, 'a'); a" ENTER), | |
| + nxt_string("0") }, | |
| + | |
| + { nxt_string("var i = 0; " | |
| + "(function x() { if (i < 10) setImmediate(x); i++; })()" ENTER | |
| + "i" ENTER), | |
| + nxt_string("11") }, | |
| + | |
| + { nxt_string("var a = 0; " | |
| + "var t = setImmediate(function() {a = 1}); clearTimeout(t)" ENTER | |
| + "a" ENTER), | |
| + nxt_string("0") }, | |
| + | |
| /* Backtraces */ | |
| { nxt_string("function ff(o) {return o.a.a}" ENTER | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -10635,8 +10635,11 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("setTimeout()"), | |
| nxt_string("TypeError: too few arguments") }, | |
| - { nxt_string("setTimeout(function(){})"), | |
| - nxt_string("InternalError: not supported by host environment") }, | |
| + { nxt_string("var a = 0; setTimeout(function () {a = 1;}); a"), | |
| + nxt_string("0") }, | |
| + | |
| + { nxt_string("var a = 0; setTimeout(function () {setImmediate(function () {a = 1})}); a"), | |
| + nxt_string("0") }, | |
| { nxt_string("setTimeout(function(){}, 12)"), | |
| nxt_string("InternalError: not supported by host environment") }, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment