Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created December 27, 2018 13:58
Show Gist options
  • Save xeioex/0e36f4f105ca7b8c410f5cc651c10746 to your computer and use it in GitHub Desktop.
Save xeioex/0e36f4f105ca7b8c410f5cc651c10746 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1545914285 -10800
# Thu Dec 27 15:38:05 2018 +0300
# Node ID 52c69512c5d6b8f7466e75f8d7179b8146d91150
# Parent 9ab8d11c151d4af4e634fa9bb8ef0bfef662c064
njs_vm_run() is rectified.
Previously, both njs_vm_call() and njs_vm_run() can be used to run njs
code. njs_vm_call() was used to invoke a single function, while
njs_vm_run() was used to run global code as well to process the events.
At first invocation njs_vm_run() executed global code, all the next
invocations it processed pending events.
The solution is splitting njs_vm_run() into two functions. One for
events processing and another for running the global code.
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c
+++ b/nginx/ngx_http_js_module.c
@@ -913,7 +913,7 @@ ngx_http_js_init_vm(ngx_http_request_t *
cln->handler = ngx_http_js_cleanup_ctx;
cln->data = ctx;
- if (njs_vm_run(ctx->vm) == NJS_ERROR) {
+ if (njs_vm_start(ctx->vm) == NJS_ERROR) {
njs_vm_retval_to_ext_string(ctx->vm, &exception);
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c
+++ b/nginx/ngx_stream_js_module.c
@@ -727,7 +727,7 @@ ngx_stream_js_init_vm(ngx_stream_session
cln->handler = ngx_stream_js_cleanup_ctx;
cln->data = ctx;
- if (njs_vm_run(ctx->vm) == NJS_ERROR) {
+ if (njs_vm_start(ctx->vm) == NJS_ERROR) {
njs_vm_retval_to_ext_string(ctx->vm, &exception);
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -595,27 +595,26 @@ 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;
-
if (nxt_slow_path(vm->backtrace != NULL)) {
nxt_array_reset(vm->backtrace);
}
+ return njs_vm_handle_events(vm);
+}
+
+
+nxt_int_t
+njs_vm_start(njs_vm_t *vm)
+{
+ njs_ret_t ret;
+
ret = njs_vmcode_interpreter(vm);
if (ret == NJS_STOP) {
- ret = njs_vm_handle_events(vm);
+ ret = NJS_OK;
}
- switch (ret) {
- case NJS_STOP:
- return NJS_OK;
-
- case NXT_AGAIN:
- case NXT_ERROR:
- default:
- return ret;
- }
+ return ret;
}
@@ -653,7 +652,7 @@ njs_vm_handle_events(njs_vm_t *vm)
}
}
- return njs_is_pending_events(vm) ? NJS_AGAIN : NJS_STOP;
+ return njs_is_pending_events(vm) ? NJS_AGAIN : NJS_OK;
}
diff --git a/njs/njs.h b/njs/njs.h
--- a/njs/njs.h
+++ b/njs/njs.h
@@ -162,8 +162,6 @@ NXT_EXPORT void njs_vm_destroy(njs_vm_t
NXT_EXPORT nxt_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end);
NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external);
-NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
- const njs_value_t *args, nxt_uint_t nargs);
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,
@@ -173,8 +171,35 @@ NXT_EXPORT nxt_int_t njs_vm_pending(njs_
NXT_EXPORT nxt_int_t njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
const njs_value_t *args, nxt_uint_t nargs);
+/*
+ * Runs the specified function with provided arguments.
+ * NJS_OK successful run.
+ * NJS_ERROR some exception or internal error happens.
+ *
+ * njs_vm_retval(vm) can be used to get the retval or exception value.
+ */
+NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
+ const njs_value_t *args, nxt_uint_t nargs);
+
+/*
+ * Runs posted events.
+ * NJS_OK successfully processed all posted events, no more events.
+ * NJS_AGAIN successfully processed all events, some posted events are
+ * still pending.
+ * NJS_ERROR some exception or internal error happens.
+ * njs_vm_retval(vm) can be used to get the retval or exception value.
+ */
NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t *vm);
+/*
+ * Runs the global code.
+ * NJS_OK successful run.
+ * NJS_ERROR some exception or internal error happens.
+ *
+ * njs_vm_retval(vm) can be used to get the retval or exception value.
+ */
+NXT_EXPORT nxt_int_t njs_vm_start(njs_vm_t *vm);
+
NXT_EXPORT const njs_extern_t *njs_vm_external_prototype(njs_vm_t *vm,
njs_external_t *external);
NXT_EXPORT nxt_int_t njs_vm_external_create(njs_vm_t *vm,
diff --git a/njs/njs_shell.c b/njs/njs_shell.c
--- a/njs/njs_shell.c
+++ b/njs/njs_shell.c
@@ -490,7 +490,7 @@ njs_process_script(njs_vm_t *vm, njs_opt
printf("\n");
}
- ret = njs_vm_run(vm);
+ ret = njs_vm_start(vm);
}
if (njs_vm_retval_dump(vm, out, 1) != NXT_OK) {
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
@@ -285,7 +285,7 @@ njs_interactive_test(nxt_bool_t verbose)
ret = njs_vm_compile(vm, &start, end);
if (ret == NXT_OK) {
- ret = njs_vm_run(vm);
+ ret = njs_vm_start(vm);
}
}
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
@@ -11801,7 +11801,7 @@ njs_unit_test(njs_unit_test_t tests[], s
goto done;
}
- ret = njs_vm_run(nvm);
+ ret = njs_vm_start(nvm);
if (njs_vm_retval_to_ext_string(nvm, &s) != NXT_OK) {
printf("njs_vm_retval_to_ext_string() failed\n");
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1545914357 -10800
# Thu Dec 27 15:39:17 2018 +0300
# Node ID 60d07e60d7fad21501cfb38da993466314221b73
# Parent 52c69512c5d6b8f7466e75f8d7179b8146d91150
njs_vm_pending() is split in njs_vm_posted() and njs_vm_waiting().
There are two types of events in njs:
Posted - are ready to be executed by njs_vm_run().
Waiting - await external async events.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -196,7 +196,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_waiting_events(vm)) {
nxt_lvlhsh_each_init(&lhe, &njs_event_hash_proto);
for ( ;; ) {
@@ -558,9 +558,16 @@ njs_vm_del_event(njs_vm_t *vm, njs_vm_ev
nxt_int_t
-njs_vm_pending(njs_vm_t *vm)
+njs_vm_waiting(njs_vm_t *vm)
{
- return njs_is_pending_events(vm);
+ return njs_waiting_events(vm);
+}
+
+
+nxt_int_t
+njs_vm_posted(njs_vm_t *vm)
+{
+ return njs_posted_events(vm);
}
@@ -652,7 +659,7 @@ njs_vm_handle_events(njs_vm_t *vm)
}
}
- return njs_is_pending_events(vm) ? NJS_AGAIN : NJS_OK;
+ return njs_posted_events(vm) ? NJS_AGAIN : NJS_OK;
}
diff --git a/njs/njs.h b/njs/njs.h
--- a/njs/njs.h
+++ b/njs/njs.h
@@ -167,11 +167,23 @@ NXT_EXPORT njs_vm_event_t njs_vm_add_eve
njs_function_t *function, nxt_uint_t once, njs_host_event_t host_ev,
njs_event_destructor 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,
const njs_value_t *args, nxt_uint_t nargs);
/*
+ * Returns 1 if async events are present.
+ */
+NXT_EXPORT nxt_int_t njs_vm_waiting(njs_vm_t *vm);
+
+/*
+ * Returns 1 if posted events are ready to be executed.
+ */
+NXT_EXPORT nxt_int_t njs_vm_posted(njs_vm_t *vm);
+
+#define njs_vm_pending(vm) (njs_vm_waiting(vm) || njs_vm_posted(vm))
+
+
+/*
* Runs the specified function with provided arguments.
* NJS_OK successful run.
* NJS_ERROR some exception or internal error happens.
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_waiting_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 1545917281 -10800
# Thu Dec 27 16:28:01 2018 +0300
# Node ID 0f63a2fa6f2f5c2e388218d8ed9d915afca70f8c
# Parent 60d07e60d7fad21501cfb38da993466314221b73
Interactive shell: initial support of posted events.
diff --git a/njs/njs_shell.c b/njs/njs_shell.c
--- a/njs/njs_shell.c
+++ b/njs/njs_shell.c
@@ -54,7 +54,7 @@ static nxt_int_t njs_interactive_shell(n
njs_vm_opt_t *vm_options);
static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options);
static nxt_int_t njs_process_script(njs_vm_t *vm, njs_opts_t *opts,
- const nxt_str_t *script, nxt_str_t *out);
+ const nxt_str_t *script);
static nxt_int_t njs_editline_init(njs_vm_t *vm);
static char **njs_completion_handler(const char *text, int start, int end);
static char *njs_completion_generator(const char *text, int state);
@@ -297,7 +297,7 @@ static nxt_int_t
njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options)
{
njs_vm_t *vm;
- nxt_str_t line, out;
+ nxt_str_t line;
vm = njs_vm_create(vm_options);
if (vm == NULL) {
@@ -335,9 +335,7 @@ njs_interactive_shell(njs_opts_t *opts,
add_history((char *) line.start);
- njs_process_script(vm, opts, &line, &out);
-
- printf("%.*s\n", (int) out.length, out.start);
+ njs_process_script(vm, opts, &line);
/* editline allocs a new buffer every time. */
free(line.start);
@@ -357,7 +355,7 @@ njs_process_file(njs_opts_t *opts, njs_v
ssize_t n;
njs_vm_t *vm;
nxt_int_t ret;
- nxt_str_t out, script;
+ nxt_str_t script;
struct stat sb;
file = opts->file;
@@ -448,9 +446,8 @@ njs_process_file(njs_opts_t *opts, njs_v
goto done;
}
- ret = njs_process_script(vm, opts, &script, &out);
+ ret = njs_process_script(vm, opts, &script);
if (ret != NXT_OK) {
- fprintf(stderr, "%.*s\n", (int) out.length, out.start);
ret = NXT_ERROR;
goto done;
}
@@ -473,9 +470,27 @@ close_fd:
}
+static void
+njs_output(njs_vm_t *vm, njs_opts_t *opts, njs_ret_t ret)
+{
+ nxt_str_t out;
+
+ if (njs_vm_retval_dump(vm, &out, 1) != NXT_OK) {
+ out = nxt_string_value("failed to get retval from VM");
+ ret = NJS_ERROR;
+ }
+
+ if (ret != NJS_OK) {
+ fprintf(stderr, "%.*s\n", (int) out.length, out.start);
+
+ } else if (opts->interactive) {
+ printf("%.*s\n", (int) out.length, out.start);
+ }
+}
+
+
static nxt_int_t
-njs_process_script(njs_vm_t *vm, njs_opts_t *opts, const nxt_str_t *script,
- nxt_str_t *out)
+njs_process_script(njs_vm_t *vm, njs_opts_t *opts, const nxt_str_t *script)
{
u_char *start;
nxt_int_t ret;
@@ -493,9 +508,17 @@ njs_process_script(njs_vm_t *vm, njs_opt
ret = njs_vm_start(vm);
}
- if (njs_vm_retval_dump(vm, out, 1) != NXT_OK) {
- *out = nxt_string_value("failed to get retval from VM");
- return NXT_ERROR;
+ njs_output(vm, opts, ret);
+
+ if (ret == NJS_OK) {
+ while (njs_vm_posted(vm)) {
+ ret = njs_vm_run(vm);
+
+ if (ret == NJS_ERROR) {
+ njs_output(vm, opts, ret);
+ return ret;
+ }
+ }
}
return ret;
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1545917348 -10800
# Thu Dec 27 16:29:08 2018 +0300
# Node ID a711ef15fe0cda47516a48a45ec3b79c9eabac16
# Parent 0f63a2fa6f2f5c2e388218d8ed9d915afca70f8c
Added setImmediate().
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -521,7 +521,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;
@@ -165,7 +165,7 @@ NXT_EXPORT njs_vm_t *njs_vm_clone(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_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
const njs_value_t *args, nxt_uint_t nargs);
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
@@ -406,6 +406,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
@@ -2075,6 +2075,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
@@ -199,6 +199,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,11 @@
#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)
{
+ nxt_uint_t n;
uint64_t delay;
njs_event_t *event;
njs_vm_ops_t *ops;
@@ -36,7 +37,7 @@ njs_set_timeout(njs_vm_t *vm, njs_value_
delay = 0;
- if (nargs >= 3 && njs_is_number(&args[2])) {
+ if (!immediate && nargs >= 3 && njs_is_number(&args[2])) {
delay = args[2].data.u.number;
}
@@ -45,9 +46,11 @@ njs_set_timeout(njs_vm_t *vm, njs_value_
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,11 +61,11 @@ 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) {
+ if (nxt_slow_path(event->host_event == NULL)) {
njs_internal_error(vm, "set_timer() failed");
return NJS_ERROR;
}
@@ -78,6 +81,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 +135,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
@@ -950,6 +950,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)
};
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1545917349 -10800
# Thu Dec 27 16:29:09 2018 +0300
# Node ID 651dec636dbf55ab7883eda05f7a77615bb7e581
# Parent a711ef15fe0cda47516a48a45ec3b79c9eabac16
Interactive shell: immediate events support.
This closes #66 issue on Github.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -645,7 +645,7 @@ njs_vm_handle_events(njs_vm_t *vm)
ev = nxt_queue_link_data(link, njs_event_t, link);
if (ev->once) {
- njs_del_event(vm, ev, NJS_EVENT_DELETE);
+ njs_del_event(vm, ev, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
} else {
ev->posted = 0;
diff --git a/njs/njs_shell.c b/njs/njs_shell.c
--- a/njs/njs_shell.c
+++ b/njs/njs_shell.c
@@ -20,13 +20,6 @@
#include <readline.h>
-typedef enum {
- NJS_COMPLETION_VAR = 0,
- NJS_COMPLETION_SUFFIX,
- NJS_COMPLETION_GLOBAL
-} njs_completion_phase_t;
-
-
typedef struct {
char *file;
nxt_int_t version;
@@ -40,22 +33,45 @@ typedef struct {
typedef struct {
size_t index;
size_t length;
- njs_vm_t *vm;
nxt_array_t *completions;
nxt_array_t *suffix_completions;
nxt_lvlhsh_each_t lhe;
- njs_completion_phase_t phase;
+
+ enum {
+ NJS_COMPLETION_VAR = 0,
+ NJS_COMPLETION_SUFFIX,
+ NJS_COMPLETION_GLOBAL
+ } phase;
} njs_completion_t;
+typedef struct {
+ njs_vm_event_t vm_event;
+ nxt_queue_link_t link;
+} njs_ev_t;
+
+
+typedef struct {
+ njs_vm_t *vm;
+
+ nxt_lvlhsh_t events; /* njs_ev_t * */
+ nxt_queue_t posted_events;
+
+ uint64_t time;
+
+ njs_completion_t completion;
+} njs_console_t;
+
+
static nxt_int_t njs_get_options(njs_opts_t *opts, int argc, char **argv);
-static nxt_int_t njs_externals_init(njs_vm_t *vm);
+static nxt_int_t njs_console_init(njs_console_t *console, njs_vm_t *vm);
+static nxt_int_t njs_externals_init(njs_console_t *console, njs_vm_t *vm);
static nxt_int_t njs_interactive_shell(njs_opts_t *opts,
njs_vm_opt_t *vm_options);
static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options);
-static nxt_int_t njs_process_script(njs_vm_t *vm, njs_opts_t *opts,
+static nxt_int_t njs_process_script(njs_console_t *console, njs_opts_t *opts,
const nxt_str_t *script);
-static nxt_int_t njs_editline_init(njs_vm_t *vm);
+static nxt_int_t njs_editline_init(void);
static char **njs_completion_handler(const char *text, int start, int end);
static char *njs_completion_generator(const char *text, int state);
@@ -70,6 +86,15 @@ static njs_ret_t njs_ext_console_time(nj
static njs_ret_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args,
nxt_uint_t nargs, njs_index_t unused);
+static njs_host_event_t njs_console_set_timer(njs_external_ptr_t external,
+ uint64_t delay, njs_vm_event_t vm_event);
+static void njs_console_clear_timer(njs_external_ptr_t external,
+ njs_host_event_t event);
+
+static nxt_int_t lvlhsh_key_test(nxt_lvlhsh_query_t *lhq, void *data);
+static void *lvlhsh_pool_alloc(void *pool, size_t size, nxt_uint_t nalloc);
+static void lvlhsh_pool_free(void *pool, void *p, size_t size);
+
static njs_external_t njs_ext_console[] = {
@@ -150,10 +175,22 @@ static njs_external_t njs_externals[] =
};
-static njs_completion_t njs_completion;
+static const nxt_lvlhsh_proto_t lvlhsh_proto nxt_aligned(64) = {
+ NXT_LVLHSH_LARGE_SLAB,
+ 0,
+ lvlhsh_key_test,
+ lvlhsh_pool_alloc,
+ lvlhsh_pool_free,
+};
-static uint64_t njs_console_time = UINT64_MAX;
+static njs_vm_ops_t njs_console_ops = {
+ njs_console_set_timer,
+ njs_console_clear_timer
+};
+
+
+static njs_console_t njs_console;
int
@@ -182,6 +219,8 @@ main(int argc, char **argv)
vm_options.accumulative = opts.interactive;
vm_options.backtrace = 1;
vm_options.sandbox = opts.sandbox;
+ vm_options.ops = &njs_console_ops;
+ vm_options.external = &njs_console;
if (opts.interactive) {
ret = njs_interactive_shell(&opts, &vm_options);
@@ -259,7 +298,27 @@ njs_get_options(njs_opts_t *opts, int ar
static nxt_int_t
-njs_externals_init(njs_vm_t *vm)
+njs_console_init(njs_console_t *console, njs_vm_t *vm)
+{
+ console->vm = vm;
+
+ nxt_lvlhsh_init(&console->events);
+ nxt_queue_init(&console->posted_events);
+
+ console->time = UINT64_MAX;
+
+ console->completion.completions = njs_vm_completions(vm, NULL);
+ if (console->completion.completions == NULL) {
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+
+static nxt_int_t
+njs_externals_init(njs_console_t *console, njs_vm_t *vm)
{
nxt_uint_t ret;
njs_value_t *value;
@@ -279,7 +338,7 @@ njs_externals_init(njs_vm_t *vm)
return NXT_ERROR;
}
- ret = njs_vm_external_create(vm, value, proto, NULL);
+ ret = njs_vm_external_create(vm, value, proto, &njs_console);
if (ret != NXT_OK) {
return NXT_ERROR;
}
@@ -289,6 +348,11 @@ njs_externals_init(njs_vm_t *vm)
return NXT_ERROR;
}
+ ret = njs_console_init(console, vm);
+ if (ret != NXT_OK) {
+ return NXT_ERROR;
+ }
+
return NXT_OK;
}
@@ -299,22 +363,22 @@ njs_interactive_shell(njs_opts_t *opts,
njs_vm_t *vm;
nxt_str_t line;
+ if (njs_editline_init() != NXT_OK) {
+ fprintf(stderr, "failed to init completions\n");
+ return NXT_ERROR;
+ }
+
vm = njs_vm_create(vm_options);
if (vm == NULL) {
fprintf(stderr, "failed to create vm\n");
return NXT_ERROR;
}
- if (njs_externals_init(vm) != NXT_OK) {
+ if (njs_externals_init(&njs_console, vm) != NXT_OK) {
fprintf(stderr, "failed to add external protos\n");
return NXT_ERROR;
}
- if (njs_editline_init(vm) != NXT_OK) {
- fprintf(stderr, "failed to init completions\n");
- return NXT_ERROR;
- }
-
if (!opts->quiet) {
printf("interactive njs %s\n\n", NJS_VERSION);
@@ -335,7 +399,7 @@ njs_interactive_shell(njs_opts_t *opts,
add_history((char *) line.start);
- njs_process_script(vm, opts, &line);
+ njs_process_script(&njs_console, opts, &line);
/* editline allocs a new buffer every time. */
free(line.start);
@@ -439,14 +503,14 @@ njs_process_file(njs_opts_t *opts, njs_v
goto done;
}
- ret = njs_externals_init(vm);
+ ret = njs_externals_init(&njs_console, vm);
if (ret != NXT_OK) {
fprintf(stderr, "failed to add external protos\n");
ret = NXT_ERROR;
goto done;
}
- ret = njs_process_script(vm, opts, &script);
+ ret = njs_process_script(&njs_console, opts, &script);
if (ret != NXT_OK) {
ret = NXT_ERROR;
goto done;
@@ -490,11 +554,43 @@ njs_output(njs_vm_t *vm, njs_opts_t *opt
static nxt_int_t
-njs_process_script(njs_vm_t *vm, njs_opts_t *opts, const nxt_str_t *script)
+njs_process_events(njs_console_t *console, njs_opts_t *opts)
+{
+ njs_ev_t *ev;
+ nxt_queue_t *events;
+ nxt_queue_link_t *link;
+
+ events = &console->posted_events;
+
+ for ( ;; ) {
+ link = nxt_queue_first(events);
+
+ if (link == nxt_queue_tail(events)) {
+ break;
+ }
+
+ ev = nxt_queue_link_data(link, njs_ev_t, link);
+
+ nxt_queue_remove(&ev->link);
+ ev->link.prev = NULL;
+ ev->link.next = NULL;
+
+ njs_vm_post_event(console->vm, ev->vm_event, NULL, 0);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_process_script(njs_console_t *console, njs_opts_t *opts,
+ const nxt_str_t *script)
{
u_char *start;
+ njs_vm_t *vm;
nxt_int_t ret;
+ vm = console->vm;
start = script->start;
ret = njs_vm_compile(vm, &start, start + script->length);
@@ -510,14 +606,29 @@ njs_process_script(njs_vm_t *vm, njs_opt
njs_output(vm, opts, ret);
- if (ret == NJS_OK) {
- while (njs_vm_posted(vm)) {
- ret = njs_vm_run(vm);
+ for ( ;; ) {
+ if (!njs_vm_pending(vm)) {
+ break;
+ }
- if (ret == NJS_ERROR) {
- njs_output(vm, opts, ret);
- return ret;
- }
+ ret = njs_process_events(console, opts);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ fprintf(stderr, "njs_process_events() failed\n");
+ ret = NJS_ERROR;
+ }
+
+ if (njs_vm_waiting(vm) && !njs_vm_posted(vm)) {
+ /*TODO: async events. */
+
+ fprintf(stderr, "njs_process_script(): "
+ "async events unsupported\n");
+ ret = NJS_ERROR;
+ }
+
+ ret = njs_vm_run(vm);
+
+ if (ret == NJS_ERROR) {
+ njs_output(vm, opts, ret);
}
}
@@ -526,7 +637,7 @@ njs_process_script(njs_vm_t *vm, njs_opt
static nxt_int_t
-njs_editline_init(njs_vm_t *vm)
+njs_editline_init(void)
{
rl_completion_append_character = '\0';
rl_attempted_completion_function = njs_completion_handler;
@@ -534,13 +645,6 @@ njs_editline_init(njs_vm_t *vm)
setlocale(LC_ALL, "");
- njs_completion.completions = njs_vm_completions(vm, NULL);
- if (njs_completion.completions == NULL) {
- return NXT_ERROR;
- }
-
- njs_completion.vm = vm;
-
return NXT_OK;
}
@@ -571,10 +675,12 @@ njs_completion_generator(const char *tex
size_t len;
nxt_str_t expression, *suffix;
const char *p;
+ njs_vm_t *vm;
njs_variable_t *var;
njs_completion_t *cmpl;
- cmpl = &njs_completion;
+ vm = njs_console.vm;
+ cmpl = &njs_console.completion;
if (state == 0) {
cmpl->phase = 0;
@@ -589,12 +695,12 @@ next:
switch (cmpl->phase) {
case NJS_COMPLETION_VAR:
- if (cmpl->vm->parser == NULL) {
+ if (vm->parser == NULL) {
njs_next_phase(cmpl);
}
for ( ;; ) {
- var = nxt_lvlhsh_each(&cmpl->vm->parser->scope->variables,
+ var = nxt_lvlhsh_each(&vm->parser->scope->variables,
&cmpl->lhe);
if (var == NULL) {
@@ -630,8 +736,7 @@ next:
expression.start = (u_char *) text;
expression.length = p - text;
- cmpl->suffix_completions = njs_vm_completions(cmpl->vm,
- &expression);
+ cmpl->suffix_completions = njs_vm_completions(vm, &expression);
if (cmpl->suffix_completions == NULL) {
njs_next_phase(cmpl);
}
@@ -797,15 +902,22 @@ static njs_ret_t
njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
{
+ njs_console_t *console;
+
if (!njs_value_is_void(njs_arg(args, nargs, 1))) {
njs_vm_error(vm, "labels not implemented");
return NJS_ERROR;
}
+ console = njs_vm_external(vm, njs_arg(args, nargs, 0));
+ if (nxt_slow_path(console == NULL)) {
+ return NJS_ERROR;
+ }
+
+ console->time = nxt_time();
+
vm->retval = njs_value_void;
- njs_console_time = nxt_time();
-
return NJS_OK;
}
@@ -814,7 +926,8 @@ static njs_ret_t
njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
njs_index_t unused)
{
- uint64_t ns, ms;
+ uint64_t ns, ms;
+ njs_console_t *console;
ns = nxt_time();
@@ -823,16 +936,21 @@ njs_ext_console_time_end(njs_vm_t *vm, n
return NJS_ERROR;
}
- if (nxt_fast_path(njs_console_time != UINT64_MAX)) {
+ console = njs_vm_external(vm, njs_arg(args, nargs, 0));
+ if (nxt_slow_path(console == NULL)) {
+ return NJS_ERROR;
+ }
- ns = ns - njs_console_time;
+ if (nxt_fast_path(console->time != UINT64_MAX)) {
+
+ ns = ns - console->time;
ms = ns / 1000000;
ns = ns % 1000000;
printf("default: %" PRIu64 ".%06" PRIu64 "ms\n", ms, ns);
- njs_console_time = UINT64_MAX;
+ console->time = UINT64_MAX;
} else {
printf("Timer \"default\" doesn’t exist.\n");
@@ -842,3 +960,107 @@ njs_ext_console_time_end(njs_vm_t *vm, n
return NJS_OK;
}
+
+
+static njs_host_event_t
+njs_console_set_timer(njs_external_ptr_t external, uint64_t delay,
+ njs_vm_event_t vm_event)
+{
+ njs_ev_t *ev;
+ njs_vm_t *vm;
+ nxt_int_t ret;
+ njs_console_t *console;
+ nxt_lvlhsh_query_t lhq;
+
+ if (delay != 0) {
+ fprintf(stderr, "njs_console_set_timer(): async timers unsupported\n");
+ return NULL;
+ }
+
+ console = external;
+ vm = console->vm;
+
+ ev = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_ev_t));
+ if (nxt_slow_path(ev == NULL)) {
+ return NULL;
+ }
+
+ ev->vm_event = vm_event;
+
+ lhq.key.start = (u_char *) &ev->vm_event;
+ lhq.key.length = sizeof(njs_vm_event_t);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.replace = 0;
+ lhq.value = ev;
+ lhq.proto = &lvlhsh_proto;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&console->events, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
+ }
+
+ nxt_queue_insert_tail(&console->posted_events, &ev->link);
+
+ return (njs_host_event_t) ev;
+}
+
+
+static void
+njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event)
+{
+ njs_ev_t *ev;
+ nxt_int_t ret;
+ njs_console_t *console;
+ nxt_lvlhsh_query_t lhq;
+
+ ev = event;
+ console = external;
+
+ lhq.key.start = (u_char *) &ev->vm_event;
+ lhq.key.length = sizeof(njs_vm_event_t);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.proto = &lvlhsh_proto;
+ lhq.pool = console->vm->mem_cache_pool;
+
+ if (ev->link.prev != NULL) {
+ nxt_queue_remove(&ev->link);
+ }
+
+ ret = nxt_lvlhsh_delete(&console->events, &lhq);
+ if (ret != NXT_OK) {
+ fprintf(stderr, "nxt_lvlhsh_delete() failed\n");
+ }
+}
+
+
+static nxt_int_t
+lvlhsh_key_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_ev_t *ev;
+
+ ev = data;
+
+ if (memcmp(&ev->vm_event, lhq->key.start, sizeof(njs_vm_event_t)) == 0) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static void *
+lvlhsh_pool_alloc(void *pool, size_t size, nxt_uint_t nalloc)
+{
+ return nxt_mem_cache_align(pool, size, size);
+}
+
+
+static void
+lvlhsh_pool_free(void *pool, void *p, size_t size)
+{
+ nxt_mem_cache_free(pool, p);
+}
+
diff --git a/njs/test/njs_expect_test.exp b/njs/test/njs_expect_test.exp
--- a/njs/test/njs_expect_test.exp
+++ b/njs/test/njs_expect_test.exp
@@ -292,6 +292,67 @@ njs_test {
"'й'"}
}
+# Immediate events
+
+njs_test {
+ {"var t = setImmediate(console.log, 'a', 'aa')\r\n"
+ "undefined\r\n'a' 'aa'"}
+}
+
+njs_test {
+ {"var a = 1 + 1; setTimeout(function (x) {a = x}, 0, 'a'); a\r\n"
+ "2"}
+ {"a\r\n"
+ "a\r\n'a'"}
+}
+
+njs_test {
+ {"setTimeout(function () {}, 1, 'a')\r\n"
+ "njs_console_set_timer(): async timers unsupported"}
+}
+
+njs_test {
+ {"var a = 1 + 1; setTimeout(function (x) { setTimeout(function (y) {a = y}, 0, x)}, 0, 'a'); a\r\n"
+ "2"}
+ {"a\r\n"
+ "a\r\n'a'"}
+}
+
+njs_test {
+ {"var a = 1 + 1; setImmediate(function (x) { setImmediate(function (y) {a = y}, x)}, 'a'); a\r\n"
+ "2"}
+ {"a\r\n"
+ "a\r\n'a'"}
+}
+
+njs_test {
+ {"var i = 0; (function x() { if (i < 10) setImmediate(x); i++; })()\r\n"
+ "undefined"}
+ {"i\r\n"
+ "i\r\n11"}
+}
+
+njs_test {
+ {"var a = 0, t = setImmediate(function() {a = 1}); clearTimeout(t)\r\n"
+ "undefined"}
+ {"a\r\n"
+ "a\r\n0"}
+}
+
+njs_test {
+ {"var i = 0; (function x() { if (i < 3) setImmediate(x); i++; throw 'Oops';})()\r\n"
+ "Oops"}
+ {"i\r\n"
+ "i\r\n4"}
+}
+
+njs_test {
+ {"var i = 0, queue = []; (function x() { if (i < 5) setImmediate(x); queue.push(i++); })()\r\n"
+ "undefined"}
+ {"queue.toString()\r\n"
+ "queue.toString()\r\n'0,1,2,3,4,5'"}
+}
+
# require('fs')
set file [open njs_test_file w]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment