Skip to content

Instantly share code, notes, and snippets.

@xeioex
Last active December 12, 2018 12:38
Show Gist options
  • Save xeioex/b504bcadc8a9ba651155d7513c82710e to your computer and use it in GitHub Desktop.
Save xeioex/b504bcadc8a9ba651155d7513c82710e to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1544527173 -10800
# Tue Dec 11 14:19:33 2018 +0300
# Node ID 14801762b1eb5199453acc3c774b98ace7f9abb5
# Parent 0709c3d38212df011229fccb2e7a9e7f23263cce
Added setImmediate().
This closes #66 issue on Github.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -501,7 +501,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
@@ -16,17 +16,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,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
@@ -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)
};
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1544527174 -10800
# Tue Dec 11 14:19:34 2018 +0300
# Node ID 141759f986b49a4721c2541b1b836db25bf35621
# Parent 14801762b1eb5199453acc3c774b98ace7f9abb5
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_run_global(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_run_global(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
@@ -575,27 +575,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_run_global(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;
}
@@ -633,7 +632,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_run_global(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_run_global(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_run_global(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
@@ -11481,7 +11481,7 @@ njs_unit_test(njs_unit_test_t tests[], s
goto done;
}
- ret = njs_vm_run(nvm);
+ ret = njs_vm_run_global(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 1544527174 -10800
# Tue Dec 11 14:19:34 2018 +0300
# Node ID 0e05190227e09c39aeca91ccaf9099b91b63b222
# Parent 141759f986b49a4721c2541b1b836db25bf35621
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 ( ;; ) {
@@ -538,9 +538,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);
}
@@ -632,7 +639,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_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,
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 1544527175 -10800
# Tue Dec 11 14:19:35 2018 +0300
# Node ID 7c78f2d37e824f99ee0c7b202f002094cb66f3e7
# Parent 0e05190227e09c39aeca91ccaf9099b91b63b222
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_run_global(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 1544551224 -10800
# Tue Dec 11 21:00:24 2018 +0300
# Node ID fb6163c8a1a967dc7facfa3a826bafff61234d67
# Parent 7c78f2d37e824f99ee0c7b202f002094cb66f3e7
Interactive shell: immediate events support.
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -625,7 +625,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
@@ -40,7 +40,6 @@ 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;
@@ -48,14 +47,25 @@ typedef struct {
} njs_completion_t;
+typedef struct {
+ njs_vm_t *vm;
+
+ nxt_lvlhsh_t 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 +80,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 +169,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 +213,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 +292,26 @@ 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);
+
+ 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 +331,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 +341,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 +356,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 +392,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 +496,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 +547,36 @@ 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_vm_event_t event;
+ nxt_lvlhsh_each_t lhe;
+
+ nxt_lvlhsh_each_init(&lhe, &lvlhsh_proto);
+
+ for ( ;; ) {
+ event = nxt_lvlhsh_each(&console->events, &lhe);
+
+ if (event == NULL) {
+ break;
+ }
+
+ njs_vm_post_event(console->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 +592,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 +623,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 +631,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 +661,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 +681,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 +722,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 +888,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 +912,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 +922,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 +946,85 @@ 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)
+{
+ 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;
+
+ lhq.key.start = (u_char *) &vm_event;
+ lhq.key.length = sizeof(vm_event);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.replace = 0;
+ lhq.value = vm_event;
+ lhq.proto = &lvlhsh_proto;
+ lhq.pool = console->vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&console->events, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
+ }
+
+ return (njs_host_event_t) vm_event;
+}
+
+
+static void
+njs_console_clear_timer(njs_external_ptr_t external, njs_host_event_t event)
+{
+ nxt_int_t ret;
+ njs_console_t *console;
+ nxt_lvlhsh_query_t lhq;
+
+ console = external;
+
+ lhq.key.start = (u_char *) &event;
+ lhq.key.length = sizeof(event);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+ lhq.proto = &lvlhsh_proto;
+ lhq.pool = console->vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_delete(&console->events, &lhq);
+ if (ret != NXT_OK) {
+ printf("nxt_lvlhsh_delete() failed\n");
+ }
+}
+
+
+static nxt_int_t
+lvlhsh_key_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ if (memcmp(&data, 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,55 @@ njs_test {
"'й'"}
}
+# Immediate events
+
+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"}
+}
+
# require('fs')
set file [open njs_test_file w]
# HG changeset patch
# User Dmitry Volyntsev <[email protected]>
# Date 1544618207 -10800
# Wed Dec 12 15:36:47 2018 +0300
# Node ID 2db0a59ce692db14af778253d51def8a471e5a9a
# Parent fb6163c8a1a967dc7facfa3a826bafff61234d67
Added support of native functions call in njs_vm_call().
diff --git a/njs/njs.c b/njs/njs.c
--- a/njs/njs.c
+++ b/njs/njs.c
@@ -461,9 +461,10 @@ nxt_int_t
njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
nxt_uint_t nargs)
{
- u_char *current;
- njs_ret_t ret;
- njs_value_t *this;
+ u_char *current;
+ njs_ret_t ret;
+ njs_value_t *this;
+ njs_continuation_t *cont;
static const njs_vmcode_stop_t stop[] = {
{ .code = { .operation = njs_vmcode_stop,
@@ -474,17 +475,36 @@ njs_vm_call(njs_vm_t *vm, njs_function_t
this = (njs_value_t *) &njs_value_void;
- ret = njs_function_frame(vm, function, this, args, nargs, 0);
- if (nxt_slow_path(ret != NXT_OK)) {
- return ret;
- }
+ current = vm->current;
- current = vm->current;
- vm->current = (u_char *) stop;
+ if (function->native) {
+ ret = njs_function_native_frame(vm, function, this, &args[0],
+ nargs, NJS_CONTINUATION_SIZE, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
- ret = njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
- if (nxt_slow_path(ret == NXT_ERROR)) {
- return ret;
+ cont = njs_vm_continuation(vm);
+
+ cont->function = function->u.native;
+ cont->args_types = function->args_types;
+ cont->retval = NJS_INDEX_GLOBAL_RETVAL;
+
+ cont->return_address = (u_char *) stop;
+ vm->current = (u_char *) njs_continuation_nexus;
+
+ } else {
+ ret = njs_function_frame(vm, function, this, args, nargs, 0);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ vm->current = (u_char *) stop;
+
+ ret = njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
}
ret = njs_vmcode_interpreter(vm);
diff --git a/njs/njs_function.c b/njs/njs_function.c
--- a/njs/njs_function.c
+++ b/njs/njs_function.c
@@ -178,7 +178,7 @@ njs_function_arguments_thrower(njs_vm_t
njs_ret_t
njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
- const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
+ const njs_value_t *this, const njs_value_t *args, nxt_uint_t nargs,
size_t reserve, nxt_bool_t ctor)
{
size_t size;
diff --git a/njs/njs_function.h b/njs/njs_function.h
--- a/njs/njs_function.h
+++ b/njs/njs_function.h
@@ -162,7 +162,7 @@ njs_ret_t njs_function_constructor(njs_v
njs_ret_t njs_function_apply(njs_vm_t *vm, njs_function_t *function,
njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
njs_ret_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
- const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
+ const njs_value_t *this, const njs_value_t *args, nxt_uint_t nargs,
size_t reserve, nxt_bool_t ctor);
njs_ret_t njs_function_frame(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *this, const njs_value_t *args, nxt_uint_t nargs,
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
@@ -341,6 +341,11 @@ njs_test {
"i\r\n4"}
}
+njs_test {
+ {"var t = setImmediate(console.log, 'a', 'aa')\r\n"
+ "undefined\r\n'a' 'aa'"}
+}
+
# 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