[njs] Shell: added QuickJS engine support.

Dmitry Volyntsev xeioex at nginx.com
Fri Feb 23 05:19:47 UTC 2024


details:   https://hg.nginx.org/njs/rev/cb3e068a511c
branches:  
changeset: 2290:cb3e068a511c
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Feb 22 20:25:43 2024 -0800
description:
Shell: added QuickJS engine support.

diffstat:

 auto/expect              |    22 +-
 auto/make                |    23 +-
 auto/options             |     2 +
 auto/quickjs             |    55 +
 auto/summary             |     4 +
 configure                |     1 +
 external/njs_shell.c     |  2225 ++++++++++++++++++++++++++++++++++++++++-----
 src/njs_builtin.c        |     3 +
 src/test/njs_unit_test.c |     4 +-
 test/setup               |     5 +
 test/shell_test.exp      |   361 +-------
 test/shell_test_njs.exp  |   418 ++++++++
 test/test262             |     5 +
 13 files changed, 2531 insertions(+), 597 deletions(-)

diffs (truncated from 3866 to 1000 lines):

diff -r 272af619b821 -r cb3e068a511c auto/expect
--- a/auto/expect	Thu Feb 22 17:38:58 2024 -0800
+++ b/auto/expect	Thu Feb 22 20:25:43 2024 -0800
@@ -20,11 +20,31 @@ fi
 if [ $njs_found = yes -a $NJS_HAVE_READLINE = YES ]; then
     cat << END >> $NJS_MAKEFILE
 
-shell_test:	njs test/shell_test.exp
+shell_test_njs:	njs test/shell_test.exp
 	PATH=$NJS_BUILD_DIR:\$(PATH) LANG=C.UTF-8 TERM=screen \
     expect -f test/shell_test.exp
+	PATH=$NJS_BUILD_DIR:\$(PATH) LANG=C.UTF-8 TERM=screen \
+    expect -f test/shell_test_njs.exp
 END
 
+if [ $NJS_HAVE_QUICKJS = YES ]; then
+    cat << END >> $NJS_MAKEFILE
+
+shell_test:	shell_test_njs shell_test_quickjs
+
+shell_test_quickjs:	njs test/shell_test.exp
+	PATH=$NJS_BUILD_DIR:\$(PATH) LANG=C.UTF-8 TERM=screen NJS_ENGINE=QuickJS \
+    expect -f test/shell_test.exp
+END
+
+else
+    cat << END >> $NJS_MAKEFILE
+
+shell_test:	shell_test_njs
+END
+
+fi
+
 else
     echo " - expect tests are disabled"
 
diff -r 272af619b821 -r cb3e068a511c auto/make
--- a/auto/make	Thu Feb 22 17:38:58 2024 -0800
+++ b/auto/make	Thu Feb 22 20:25:43 2024 -0800
@@ -241,8 +241,7 @@ lib_test: $NJS_BUILD_DIR/njs_auto_config
 	$NJS_BUILD_DIR/lvlhsh_unit_test
 	$NJS_BUILD_DIR/unicode_unit_test
 
-test262: njs
-
+test262_njs: njs
 	test/test262 --binary=$NJS_BUILD_DIR/njs
 
 unit_test: $NJS_BUILD_DIR/njs_auto_config.h \\
@@ -265,6 +264,26 @@ dist:
 	&& echo njs-\$(NJS_VER).tar.gz done
 END
 
+if [ $NJS_HAVE_QUICKJS = YES ]; then
+    cat << END >> $NJS_MAKEFILE
+
+test262: njs test262_njs test262_quickjs
+
+test262_quickjs: njs
+	NJS_SKIP_LIST="test/js/promise_rejection_tracker_recursive.t.js \\
+test/js/async_exception_in_await.t.js" \\
+	test/test262 --binary='$NJS_BUILD_DIR/njs -n QuickJS -m'
+END
+
+else
+    cat << END >> $NJS_MAKEFILE
+
+test262: njs test262_njs
+END
+
+fi
+
+
 njs_ts_deps=`echo $NJS_TS_SRCS \
         | sed -e "s# *\([^ ][^ ]*\)#\1$njs_regex_cont#g"`
 
diff -r 272af619b821 -r cb3e068a511c auto/options
--- a/auto/options	Thu Feb 22 17:38:58 2024 -0800
+++ b/auto/options	Thu Feb 22 20:25:43 2024 -0800
@@ -14,6 +14,7 @@ NJS_DEBUG_GENERATOR=NO
 NJS_ADDRESS_SANITIZER=NO
 NJS_ADDR2LINE=NO
 
+NJS_QUICKJS=YES
 NJS_OPENSSL=YES
 NJS_LIBXML2=YES
 NJS_ZLIB=YES
@@ -47,6 +48,7 @@ do
         --debug-opcode=*)                NJS_DEBUG_OPCODE="$value"           ;;
         --debug-generator=*)             NJS_DEBUG_GENERATOR="$value"        ;;
 
+        --no-quickjs)                    NJS_QUICKJS=NO                      ;;
         --no-openssl)                    NJS_OPENSSL=NO                      ;;
         --no-libxml2)                    NJS_LIBXML2=NO                      ;;
         --no-zlib)                       NJS_ZLIB=NO                         ;;
diff -r 272af619b821 -r cb3e068a511c auto/quickjs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auto/quickjs	Thu Feb 22 20:25:43 2024 -0800
@@ -0,0 +1,55 @@
+
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) NGINX, Inc.
+
+
+NJS_QUICKJS_LIB=
+NJS_HAVE_QUICKJS=NO
+
+if [ $NJS_QUICKJS = YES ]; then
+    njs_found=no
+
+    njs_feature="QuickJS library"
+    njs_feature_name=NJS_HAVE_QUICKJS
+    njs_feature_run=yes
+    njs_feature_incs=
+    njs_feature_libs=""
+    njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8)
+                      #pragma GCC diagnostic push
+                      #pragma GCC diagnostic ignored \"-Wcast-function-type\"
+                      #endif
+
+                      #include <quickjs.h>
+
+                      int main() {
+                          JSRuntime *rt;
+
+                          rt = JS_NewRuntime();
+                          JS_FreeRuntime(rt);
+                          return 0;
+                     }"
+    . auto/feature
+
+    if [ $njs_found = no ]; then
+        njs_feature="QuickJS library -lquickjs.lto"
+        njs_feature_incs="/usr/include/quickjs/"
+        njs_feature_libs="-L/usr/lib/quickjs/ -lquickjs.lto -lm -ldl -lpthread"
+
+        . auto/feature
+    fi
+
+    if [ $njs_found = no ]; then
+        njs_feature="QuickJS library -lquickjs"
+        njs_feature_libs="-L/usr/lib/quickjs/ -lquickjs -lm -ldl -lpthread"
+
+        . auto/feature
+    fi
+
+    if [ $njs_found = yes ]; then
+        NJS_HAVE_QUICKJS=YES
+        NJS_QUICKJS_LIB="$njs_feature_libs"
+        NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs"
+        NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs"
+    fi
+
+fi
diff -r 272af619b821 -r cb3e068a511c auto/summary
--- a/auto/summary	Thu Feb 22 17:38:58 2024 -0800
+++ b/auto/summary	Thu Feb 22 20:25:43 2024 -0800
@@ -18,6 +18,10 @@ if [ $NJS_HAVE_READLINE = YES ]; then
   echo " + using readline library: $NJS_READLINE_LIB"
 fi
 
+if [ $NJS_HAVE_QUICKJS = YES ]; then
+  echo " + using QuickJS library: $NJS_QUICKJS_LIB"
+fi
+
 if [ $NJS_HAVE_OPENSSL = YES ]; then
   echo " + using OpenSSL library: $NJS_OPENSSL_LIB"
 fi
diff -r 272af619b821 -r cb3e068a511c configure
--- a/configure	Thu Feb 22 17:38:58 2024 -0800
+++ b/configure	Thu Feb 22 20:25:43 2024 -0800
@@ -50,6 +50,7 @@ NJS_LIB_AUX_LIBS=
 . auto/explicit_bzero
 . auto/pcre
 . auto/readline
+. auto/quickjs
 . auto/openssl
 . auto/libxml2
 . auto/zlib
diff -r 272af619b821 -r cb3e068a511c external/njs_shell.c
--- a/external/njs_shell.c	Thu Feb 22 17:38:58 2024 -0800
+++ b/external/njs_shell.c	Thu Feb 22 20:25:43 2024 -0800
@@ -11,6 +11,21 @@
 #include <njs_queue.h>
 #include <njs_rbtree.h>
 
+#if (NJS_HAVE_QUICKJS)
+#if defined(__GNUC__) && (__GNUC__ >= 8)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+
+#include <quickjs.h>
+
+#if defined(__GNUC__) && (__GNUC__ >= 8)
+#pragma GCC diagnostic pop
+#endif
+#define NJS_QUICKJS_VERSION  "Unknown version"
+#include <pthread.h>
+#endif
+
 #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE)
 
 #include <locale.h>
@@ -38,8 +53,10 @@ typedef struct {
     uint8_t                 version;
     uint8_t                 ast;
     uint8_t                 unhandled_rejection;
+    uint8_t                 suppress_stdout;
     uint8_t                 opcode_debug;
     uint8_t                 generator_debug;
+    uint8_t                 can_block;
     int                     exit_code;
     int                     stack_size;
 
@@ -49,6 +66,11 @@ typedef struct {
     njs_str_t               *paths;
     char                    **argv;
     njs_uint_t              argc;
+
+    enum {
+        NJS_ENGINE_NJS = 0,
+        NJS_ENGINE_QUICKJS = 1,
+    }                       engine;
 } njs_opts_t;
 
 
@@ -75,8 +97,19 @@ typedef struct {
 
 typedef struct {
     NJS_RBTREE_NODE         (node);
-    njs_function_t          *function;
-    njs_value_t             *args;
+    union {
+        struct {
+            njs_function_t      *function;
+            njs_value_t         *args;
+        }                       njs;
+#if (NJS_HAVE_QUICKJS)
+        struct {
+            JSValue             function;
+            JSValue             *args;
+        }                       qjs;
+#endif
+    }                           u;
+
     njs_uint_t              nargs;
     uint32_t                id;
 
@@ -92,8 +125,18 @@ typedef struct {
 
 
 typedef struct {
-    void                    *promise;
-    njs_opaque_value_t      message;
+    union {
+        struct {
+            njs_opaque_value_t  promise;
+            njs_opaque_value_t  message;
+        }                       njs;
+#if (NJS_HAVE_QUICKJS)
+        struct {
+            JSValue             promise;
+            JSValue             message;
+        }                       qjs;
+#endif
+    }                           u;
 } njs_rejected_promise_t;
 
 
@@ -105,8 +148,39 @@ typedef struct {
 } njs_module_info_t;
 
 
+typedef struct  njs_engine_s     njs_engine_t;
+
+
+struct njs_engine_s {
+    union {
+        struct {
+            njs_vm_t            *vm;
+
+            njs_opaque_value_t  value;
+            njs_completion_t    completion;
+        }                       njs;
+#if (NJS_HAVE_QUICKJS)
+        struct {
+            JSRuntime           *rt;
+            JSContext           *ctx;
+            JSValue             value;
+        }                       qjs;
+#endif
+    }                           u;
+
+    njs_int_t                 (*eval)(njs_engine_t *engine, njs_str_t *script);
+    njs_int_t                 (*execute_pending_job)(njs_engine_t *engine);
+    njs_int_t                 (*unhandled_rejection)(njs_engine_t *engine);
+    njs_int_t                 (*process_events)(njs_engine_t *engine);
+    njs_int_t                 (*destroy)(njs_engine_t *engine);
+    njs_int_t                 (*output)(njs_engine_t *engine, njs_int_t ret);
+
+    unsigned                    type;
+    njs_mp_t                    *pool;
+};
+
 typedef struct {
-    njs_vm_t                *vm;
+    njs_engine_t            *engine;
 
     uint32_t                event_id;
     njs_rbtree_t            events;  /* njs_ev_t * */
@@ -119,25 +193,57 @@ typedef struct {
     njs_arr_t               *rejected_promises;
 
     njs_bool_t              suppress_stdout;
-
-    njs_completion_t        completion;
+    njs_bool_t              interactive;
+    njs_bool_t              module;
+    char                    **argv;
+    njs_uint_t              argc;
+
+#if (NJS_HAVE_QUICKJS)
+    JSValue                 process;
+
+    njs_queue_t             agents;
+    njs_queue_t             reports;
+    pthread_mutex_t         agent_mutex;
+    pthread_cond_t          agent_cond;
+    pthread_mutex_t         report_mutex;
+#endif
 } njs_console_t;
 
 
+#if (NJS_HAVE_QUICKJS)
+typedef struct {
+    njs_queue_link_t        link;
+    pthread_t               tid;
+    njs_console_t           *console;
+    char                    *script;
+    JSValue                 broadcast_func;
+    njs_bool_t              broadcast_pending;
+    JSValue                 broadcast_sab;
+    uint8_t                 *broadcast_sab_buf;
+    size_t                  broadcast_sab_size;
+    int32_t                 broadcast_val;
+} njs_262agent_t;
+
+
+typedef struct {
+    njs_queue_link_t        link;
+    char                    *str;
+} njs_agent_report_t;
+#endif
+
+
 static njs_int_t njs_main(njs_opts_t *opts);
-static njs_int_t njs_console_init(njs_vm_t *vm, njs_console_t *console);
-static void njs_console_output(njs_vm_t *vm, njs_value_t *value,
-    njs_int_t ret);
+static njs_int_t njs_console_init(njs_opts_t *opts, njs_console_t *console);
 static njs_int_t njs_externals_init(njs_vm_t *vm);
-static njs_vm_t *njs_create_vm(njs_opts_t *opts);
-static void njs_process_output(njs_vm_t *vm, njs_value_t *value, njs_int_t ret);
+static njs_engine_t *njs_create_engine(njs_opts_t *opts);
 static njs_int_t njs_process_file(njs_opts_t *opts);
-static njs_int_t njs_process_script(njs_vm_t *vm, void *runtime,
-    const njs_str_t *script);
+static njs_int_t njs_process_script(njs_engine_t *engine,
+    njs_console_t *console, njs_str_t *script);
 
 #ifndef NJS_FUZZER_TARGET
 
 static njs_int_t njs_options_parse(njs_opts_t *opts, int argc, char **argv);
+static njs_int_t njs_options_parse_engine(njs_opts_t *opts, const char *engine);
 static njs_int_t njs_options_add_path(njs_opts_t *opts, char *path, size_t len);
 static void njs_options_free(njs_opts_t *opts);
 
@@ -166,6 +272,9 @@ static void njs_console_log(njs_log_leve
 static void njs_console_logger(njs_log_level_t level, const u_char *start,
     size_t length);
 
+static njs_int_t njs_console_time(njs_console_t *console, njs_str_t *name);
+static void njs_console_time_end(njs_console_t *console, njs_str_t *name,
+    uint64_t time);
 static intptr_t njs_event_rbtree_compare(njs_rbtree_node_t *node1,
     njs_rbtree_node_t *node2);
 static uint64_t njs_time(void);
@@ -317,8 +426,8 @@ static njs_console_t  njs_console;
 static njs_int_t
 njs_main(njs_opts_t *opts)
 {
-    njs_vm_t   *vm;
-    njs_int_t  ret;
+    njs_int_t     ret;
+    njs_engine_t  *engine;
 
     njs_mm_denormals(opts->denormals);
 
@@ -339,6 +448,12 @@ njs_main(njs_opts_t *opts)
         }
     }
 
+    ret = njs_console_init(opts, &njs_console);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_stderror("njs_console_init() failed\n");
+        return NJS_ERROR;
+    }
+
 #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE)
 
     if (opts->interactive) {
@@ -349,13 +464,13 @@ njs_main(njs_opts_t *opts)
 #endif
 
     if (opts->command.length != 0) {
-        vm = njs_create_vm(opts);
-        if (vm == NULL) {
+        engine = njs_create_engine(opts);
+        if (engine == NULL) {
             return NJS_ERROR;
         }
 
-        ret = njs_process_script(vm, njs_vm_external_ptr(vm), &opts->command);
-        njs_vm_destroy(vm);
+        ret = njs_process_script(engine, &njs_console, &opts->command);
+        engine->destroy(engine);
 
     } else {
         ret = njs_process_file(opts);
@@ -426,6 +541,10 @@ njs_options_parse(njs_opts_t *opts, int 
         "  -g                enable generator debug.\n"
 #endif
         "  -j <size>         set the maximum stack size in bytes.\n"
+        "  -m                load as ES6 module (script is default).\n"
+#ifdef NJS_HAVE_QUICKJS
+        "  -n njs|QuickJS    set JS engine (njs is default)\n"
+#endif
 #ifdef NJS_DEBUG_OPCODE
         "  -o                enable opcode debug.\n"
 #endif
@@ -433,15 +552,14 @@ njs_options_parse(njs_opts_t *opts, int 
         "  -q                disable interactive introduction prompt.\n"
         "  -r                ignore unhandled promise rejection.\n"
         "  -s                sandbox mode.\n"
-        "  -t script|module  source code type (script is default).\n"
         "  -v                print njs version and exit.\n"
         "  -u                disable \"unsafe\" mode.\n"
         "  script.js | -     run code from a file or stdin.\n";
 
-    ret = NJS_DONE;
-
     opts->denormals = 1;
+    opts->can_block = 1;
     opts->exit_code = EXIT_FAILURE;
+    opts->engine = NJS_ENGINE_NJS;
     opts->unhandled_rejection = 1;
 
     p = getenv("NJS_EXIT_CODE");
@@ -449,6 +567,24 @@ njs_options_parse(njs_opts_t *opts, int 
         opts->exit_code = atoi(p);
     }
 
+    p = getenv("NJS_CAN_BLOCK");
+    if (p != NULL) {
+        opts->can_block = atoi(p);
+    }
+
+    p = getenv("NJS_LOAD_AS_MODULE");
+    if (p != NULL) {
+        opts->module = 1;
+    }
+
+    p = getenv("NJS_ENGINE");
+    if (p != NULL) {
+        ret = njs_options_parse_engine(opts, p);
+        if (ret != NJS_OK) {
+            return NJS_ERROR;
+        }
+    }
+
     start = getenv("NJS_PATH");
     if (start != NULL) {
         for ( ;; ) {
@@ -486,7 +622,7 @@ njs_options_parse(njs_opts_t *opts, int 
         case '?':
         case 'h':
             njs_printf("%*s", njs_length(help), help);
-            return ret;
+            return NJS_DONE;
 
         case 'a':
             opts->ast = 1;
@@ -541,6 +677,23 @@ njs_options_parse(njs_opts_t *opts, int 
             njs_stderror("option \"-j\" requires argument\n");
             return NJS_ERROR;
 
+        case 'm':
+            opts->module = 1;
+            break;
+
+        case 'n':
+            if (++i < argc) {
+                ret = njs_options_parse_engine(opts, argv[i]);
+                if (ret != NJS_OK) {
+                    return NJS_ERROR;
+                }
+
+                break;
+            }
+
+            njs_stderror("option \"-n\" requires argument\n");
+            return NJS_ERROR;
+
 #ifdef NJS_DEBUG_OPCODE
         case 'o':
             opts->opcode_debug = 1;
@@ -573,22 +726,6 @@ njs_options_parse(njs_opts_t *opts, int 
             opts->sandbox = 1;
             break;
 
-        case 't':
-            if (++i < argc) {
-                if (strcmp(argv[i], "module") == 0) {
-                    opts->module = 1;
-
-                } else if (strcmp(argv[i], "script") != 0) {
-                    njs_stderror("option \"-t\" unexpected source type: %s\n",
-                                 argv[i]);
-                    return NJS_ERROR;
-                }
-
-                break;
-            }
-
-            njs_stderror("option \"-t\" requires source type\n");
-            return NJS_ERROR;
         case 'v':
         case 'V':
             opts->version = 1;
@@ -608,6 +745,40 @@ njs_options_parse(njs_opts_t *opts, int 
 
 done:
 
+#ifdef NJS_HAVE_QUICKJS
+    if (opts->engine == NJS_ENGINE_QUICKJS) {
+        if (opts->ast) {
+            njs_stderror("option \"-a\" is not supported for quickjs\n");
+            return NJS_ERROR;
+        }
+
+        if (opts->disassemble) {
+            njs_stderror("option \"-d\" is not supported for quickjs\n");
+            return NJS_ERROR;
+        }
+
+        if (opts->generator_debug) {
+            njs_stderror("option \"-g\" is not supported for quickjs\n");
+            return NJS_ERROR;
+        }
+
+        if (opts->opcode_debug) {
+            njs_stderror("option \"-o\" is not supported for quickjs\n");
+            return NJS_ERROR;
+        }
+
+        if (opts->sandbox) {
+            njs_stderror("option \"-s\" is not supported for quickjs\n");
+            return NJS_ERROR;
+        }
+
+        if (opts->safe) {
+            njs_stderror("option \"-u\" is not supported for quickjs\n");
+            return NJS_ERROR;
+        }
+    }
+#endif
+
     opts->argc = njs_max(argc - i + 1, 2);
     opts->argv = malloc(sizeof(char*) * opts->argc);
     if (opts->argv == NULL) {
@@ -626,6 +797,26 @@ done:
 
 
 static njs_int_t
+njs_options_parse_engine(njs_opts_t *opts, const char *engine)
+{
+    if (strncasecmp(engine, "njs", 3) == 0) {
+        opts->engine = NJS_ENGINE_NJS;
+
+#ifdef NJS_HAVE_QUICKJS
+    } else if (strncasecmp(engine, "QuickJS", 7) == 0) {
+        opts->engine = NJS_ENGINE_QUICKJS;
+#endif
+
+    } else {
+        njs_stderror("unknown engine \"%s\"\n", engine);
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
 njs_options_add_path(njs_opts_t *opts, char *path, size_t len)
 {
     njs_str_t  *paths;
@@ -675,10 +866,7 @@ LLVMFuzzerTestOneInput(const uint8_t* da
     opts.file = (char *) "fuzzer";
     opts.command.start = (u_char *) data;
     opts.command.length = size;
-
-    njs_memzero(&njs_console, sizeof(njs_console_t));
-
-    njs_console.suppress_stdout = 1;
+    opts.suppress_stdout = 1;
 
     return njs_main(&opts);
 }
@@ -686,23 +874,31 @@ LLVMFuzzerTestOneInput(const uint8_t* da
 #endif
 
 static njs_int_t
-njs_console_init(njs_vm_t *vm, njs_console_t *console)
+njs_console_init(njs_opts_t *opts, njs_console_t *console)
 {
-    console->vm = vm;
-
-    console->event_id = 0;
+    njs_memzero(console, sizeof(njs_console_t));
+
     njs_rbtree_init(&console->events, njs_event_rbtree_compare);
     njs_queue_init(&console->posted_events);
     njs_queue_init(&console->labels);
 
-    njs_memzero(&console->cwd, sizeof(njs_str_t));
-
-    console->rejected_promises = NULL;
-
-    console->completion.completions = njs_vm_completions(vm, NULL);
-    if (console->completion.completions == NULL) {
-        return NJS_ERROR;
+    console->interactive = opts->interactive;
+    console->suppress_stdout = opts->suppress_stdout;
+    console->module = opts->module;
+    console->argv = opts->argv;
+    console->argc = opts->argc;
+
+#if (NJS_HAVE_QUICKJS)
+    if (opts->engine == NJS_ENGINE_QUICKJS) {
+        njs_queue_init(&console->agents);
+        njs_queue_init(&console->reports);
+        pthread_mutex_init(&console->report_mutex, NULL);
+        pthread_mutex_init(&console->agent_mutex, NULL);
+        pthread_cond_init(&console->agent_cond, NULL);
+
+        console->process = JS_UNDEFINED;
     }
+#endif
 
     return NJS_OK;
 }
@@ -741,7 +937,7 @@ njs_externals_init(njs_vm_t *vm)
     static const njs_str_t  set_immediate = njs_str("setImmediate");
     static const njs_str_t  clear_timeout = njs_str("clearTimeout");
 
-    console = njs_vm_options(vm)->external;
+    console = njs_vm_external_ptr(vm);
 
     njs_console_proto_id = njs_vm_external_prototype(vm, njs_ext_console,
                                          njs_nitems(njs_ext_console));
@@ -803,11 +999,6 @@ njs_externals_init(njs_vm_t *vm)
         return NJS_ERROR;
     }
 
-    ret = njs_console_init(vm, console);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-
     return NJS_OK;
 }
 
@@ -830,7 +1021,9 @@ njs_rejection_tracker(njs_vm_t *vm, njs_
         promise_obj = njs_value_ptr(promise);
 
         for (i = 0; i < length; i++) {
-            if (rejected_promise[i].promise == promise_obj) {
+            if (njs_value_ptr(njs_value_arg(&rejected_promise[i].u.njs.promise))
+                == promise_obj)
+            {
                 njs_arr_remove(console->rejected_promises,
                                &rejected_promise[i]);
 
@@ -842,7 +1035,7 @@ njs_rejection_tracker(njs_vm_t *vm, njs_
     }
 
     if (console->rejected_promises == NULL) {
-        console->rejected_promises = njs_arr_create(njs_vm_memory_pool(vm), 4,
+        console->rejected_promises = njs_arr_create(console->engine->pool, 4,
                                                 sizeof(njs_rejected_promise_t));
         if (njs_slow_path(console->rejected_promises == NULL)) {
             return;
@@ -854,8 +1047,8 @@ njs_rejection_tracker(njs_vm_t *vm, njs_
         return;
     }
 
-    rejected_promise->promise = njs_value_ptr(promise);
-    njs_value_assign(&rejected_promise->message, reason);
+    njs_value_assign(&rejected_promise->u.njs.promise, promise);
+    njs_value_assign(&rejected_promise->u.njs.message, reason);
 }
 
 
@@ -968,7 +1161,7 @@ njs_module_read(njs_mp_t *mp, int fd, nj
 
     text->length = sb.st_size;
 
-    text->start = njs_mp_alloc(mp, text->length);
+    text->start = njs_mp_alloc(mp, text->length + 1);
     if (text->start == NULL) {
         goto fail;
     }
@@ -979,6 +1172,8 @@ njs_module_read(njs_mp_t *mp, int fd, nj
         goto fail;
     }
 
+    text->start[text->length] = '\0';
+
     return NJS_OK;
 
 fail:
@@ -1034,13 +1229,13 @@ current_dir:
 
 
 static njs_int_t
-njs_console_set_cwd(njs_vm_t *vm, njs_console_t *console, njs_str_t *file)
+njs_console_set_cwd(njs_console_t *console, njs_str_t *file)
 {
     njs_str_t  cwd;
 
     njs_file_dirname(file, &cwd);
 
-    console->cwd.start = njs_mp_alloc(njs_vm_memory_pool(vm), cwd.length);
+    console->cwd.start = njs_mp_alloc(console->engine->pool, cwd.length);
     if (njs_slow_path(console->cwd.start == NULL)) {
         return NJS_ERROR;
     }
@@ -1086,7 +1281,7 @@ njs_module_loader(njs_vm_t *vm, njs_exte
 
     prev_cwd = console->cwd;
 
-    ret = njs_console_set_cwd(vm, console, &info.file);
+    ret = njs_console_set_cwd(console, &info.file);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_vm_internal_error(vm, "while setting cwd for \"%V\" module",
                               &info.file);
@@ -1107,8 +1302,8 @@ njs_module_loader(njs_vm_t *vm, njs_exte
 }
 
 
-static njs_vm_t *
-njs_create_vm(njs_opts_t *opts)
+static njs_int_t
+njs_engine_njs_init(njs_engine_t *engine, njs_opts_t *opts)
 {
     njs_vm_t      *vm;
     njs_int_t     ret;
@@ -1147,7 +1342,12 @@ njs_create_vm(njs_opts_t *opts)
     vm = njs_vm_create(&vm_options);
     if (vm == NULL) {
         njs_stderror("failed to create vm\n");
-        return NULL;
+        return NJS_ERROR;
+    }
+
+    engine->u.njs.completion.completions = njs_vm_completions(vm, NULL);
+    if (engine->u.njs.completion.completions == NULL) {
+        return NJS_ERROR;
     }
 
     if (opts->unhandled_rejection) {
@@ -1155,30 +1355,77 @@ njs_create_vm(njs_opts_t *opts)
                                      njs_vm_external_ptr(vm));
     }
 
-    ret = njs_console_set_cwd(vm, njs_vm_external_ptr(vm), &vm_options.file);
+    ret = njs_console_set_cwd(njs_vm_external_ptr(vm), &vm_options.file);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_stderror("failed to set cwd\n");
-        return NULL;
+        return NJS_ERROR;
     }
 
     njs_vm_set_module_loader(vm, njs_module_loader, opts);
 
-    return vm;
+    engine->u.njs.vm = vm;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_engine_njs_destroy(njs_engine_t *engine)
+{
+    njs_vm_destroy(engine->u.njs.vm);
+    njs_mp_destroy(engine->pool);
+
+    return NJS_OK;
 }
 
 
-static void
-njs_console_output(njs_vm_t *vm, njs_value_t *value, njs_int_t ret)
+static njs_int_t
+njs_engine_njs_eval(njs_engine_t *engine, njs_str_t *script)
 {
-    njs_str_t  out;
+     u_char     *start, *end;
+     njs_int_t  ret;
+
+     start = script->start;
+     end = start + script->length;
+
+     ret = njs_vm_compile(engine->u.njs.vm, &start, end);
+
+     if (ret == NJS_OK && start == end) {
+        return njs_vm_start(engine->u.njs.vm,
+                           njs_value_arg(&engine->u.njs.value));
+     }
+
+     return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_engine_njs_execute_pending_job(njs_engine_t *engine)
+{
+    return njs_vm_execute_pending_job(engine->u.njs.vm);
+}
+
+
+static njs_int_t
+njs_engine_njs_output(njs_engine_t *engine, njs_int_t ret)
+{
+    njs_vm_t       *vm;
+    njs_str_t      out;
+    njs_console_t  *console;
+
+    vm = engine->u.njs.vm;
+    console = njs_vm_external_ptr(vm);
 
     if (ret == NJS_OK) {
-        if (njs_vm_value_dump(vm, &out, value, 0, 1) != NJS_OK) {
-            njs_stderror("Shell:failed to get retval from VM\n");
-            return;
-        }
-
-        if (njs_vm_options(vm)->interactive) {
+        if (console->interactive) {
+            if (njs_vm_value_dump(vm, &out, njs_value_arg(&engine->u.njs.value),
+                                  0, 1)
+                != NJS_OK)
+            {
+                njs_stderror("Shell:failed to get retval from VM\n");
+                return NJS_ERROR;
+            }
+
             njs_print(out.start, out.length);
             njs_print("\n", 1);
         }
@@ -1187,11 +1434,13 @@ njs_console_output(njs_vm_t *vm, njs_val
         njs_vm_exception_string(vm, &out);
         njs_stderror("Thrown:\n%V\n", &out);
     }
+
+    return NJS_OK;
 }
 
 
 static njs_int_t
-njs_process_events(void *runtime)
+njs_engine_njs_process_events(njs_engine_t *engine)
 {
     njs_ev_t            *ev;
     njs_vm_t            *vm;
@@ -1201,14 +1450,8 @@ njs_process_events(void *runtime)
     njs_queue_link_t    *link;
     njs_opaque_value_t  retval;
 
-    if (runtime == NULL) {
-        njs_stderror("njs_process_events(): no runtime\n");
-        return NJS_ERROR;
-    }
-
-    console = runtime;
-    vm = console->vm;
-
+    vm = engine->u.njs.vm;
+    console = njs_vm_external_ptr(vm);
     events = &console->posted_events;
 
     for ( ;; ) {
@@ -1221,17 +1464,14 @@ njs_process_events(void *runtime)
         ev = njs_queue_link_data(link, njs_ev_t, link);
 
         njs_queue_remove(&ev->link);
-        ev->link.prev = NULL;
-        ev->link.next = NULL;
-
         njs_rbtree_delete(&console->events, &ev->node);
 
-        ret = njs_vm_invoke(vm, ev->function, ev->args, ev->nargs,
+        ret = njs_vm_invoke(vm, ev->u.njs.function, ev->u.njs.args, ev->nargs,
                             njs_value_arg(&retval));
         if (ret == NJS_ERROR) {
-            njs_process_output(vm, njs_value_arg(&retval), ret);
-
-            if (!njs_vm_options(vm)->interactive) {
+            njs_engine_njs_output(engine, ret);
+
+            if (!console->interactive) {
                 return NJS_ERROR;
             }
         }
@@ -1246,14 +1486,16 @@ njs_process_events(void *runtime)
 
 
 static njs_int_t
-njs_unhandled_rejection(void *runtime)
+njs_engine_njs_unhandled_rejection(njs_engine_t *engine)
 {
+    njs_vm_t                *vm;
     njs_int_t               ret;
     njs_str_t               message;
     njs_console_t           *console;
     njs_rejected_promise_t  *rejected_promise;
 
-    console = runtime;
+    vm = engine->u.njs.vm;
+    console = njs_vm_external_ptr(vm);
 
     if (console->rejected_promises == NULL
         || console->rejected_promises->items == 0)
@@ -1263,14 +1505,13 @@ njs_unhandled_rejection(void *runtime)
 
     rejected_promise = console->rejected_promises->start;
 
-    ret = njs_vm_value_to_string(console->vm, &message,
-                                 njs_value_arg(&rejected_promise->message));
+    ret = njs_vm_value_to_string(vm, &message,
+                               njs_value_arg(&rejected_promise->u.njs.message));
     if (njs_slow_path(ret != NJS_OK)) {
         return -1;
     }
 
-    njs_vm_error(console->vm, "unhandled promise rejection: %V",
-                 &message);
+    njs_vm_error(vm, "unhandled promise rejection: %V", &message);
 
     njs_arr_destroy(console->rejected_promises);
     console->rejected_promises = NULL;
@@ -1278,6 +1519,1483 @@ njs_unhandled_rejection(void *runtime)
     return 1;
 }
 
+#ifdef NJS_HAVE_QUICKJS
+
+static JSValue
+njs_qjs_console_log(JSContext *ctx, JSValueConst this_val, int argc,
+    JSValueConst *argv, int magic)
+{
+    int         i;
+    size_t      len;
+    const char  *str;
+
+    for (i = 0; i < argc; i++) {
+        str = JS_ToCStringLen(ctx, &len, argv[i]);
+        if (!str) {
+            return JS_EXCEPTION;
+        }
+
+        njs_console_logger(magic, (const u_char*) str, len);
+        JS_FreeCString(ctx, str);
+    }
+
+    return JS_UNDEFINED;
+}
+
+
+static JSValue
+njs_qjs_console_time(JSContext *ctx, JSValueConst this_val, int argc,
+    JSValueConst *argv)
+{
+    njs_str_t      name;
+    const char     *str;
+    njs_console_t  *console;
+


More information about the nginx-devel mailing list