[njs] Shell: CLIs is rewritten using public API.

Dmitry Volyntsev xeioex at nginx.com
Wed May 10 02:00:06 UTC 2023


details:   https://hg.nginx.org/njs/rev/fc8d1b125cef
branches:  
changeset: 2111:fc8d1b125cef
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue May 09 18:18:33 2023 -0700
description:
Shell: CLIs is rewritten using public API.

diffstat:

 auto/make            |     8 +-
 external/njs_shell.c |  1591 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/njs.h            |     6 +-
 src/njs_builtin.c    |     2 +-
 src/njs_shell.c      |  1578 -------------------------------------------------
 src/njs_vm.c         |    10 +
 src/njs_vm.h         |     2 -
 7 files changed, 1609 insertions(+), 1588 deletions(-)

diffs (truncated from 3268 to 1000 lines):

diff -r d610d744bbd2 -r fc8d1b125cef auto/make
--- a/auto/make	Mon May 08 22:03:32 2023 -0700
+++ b/auto/make	Tue May 09 18:18:33 2023 -0700
@@ -104,10 +104,10 @@ cat << END >> $NJS_MAKEFILE
 
 $NJS_BUILD_DIR/njs: \\
 	$NJS_BUILD_DIR/libnjs.a \\
-	src/njs_shell.c
+	external/njs_shell.c
 	\$(NJS_LINK) -o $NJS_BUILD_DIR/njs \$(NJS_CFLAGS) \\
 		$NJS_LIB_AUX_CFLAGS \$(NJS_LIB_INCS) -Injs \\
-		src/njs_shell.c \\
+		external/njs_shell.c \\
 		$NJS_BUILD_DIR/libnjs.a \\
 		$NJS_LD_OPT -lm $NJS_LIBS $NJS_LIB_AUX_LIBS $NJS_READLINE_LIB
 
@@ -118,12 +118,12 @@ END
 cat << END >> $NJS_MAKEFILE
 
 $NJS_BUILD_DIR/njs_process_script_fuzzer.o: \\
-	src/njs_shell.c
+	external/njs_shell.c
 	\$(NJS_CC) -c \$(CFLAGS) $NJS_LIB_AUX_CFLAGS \\
 		\$(NJS_LIB_INCS) -Injs \\
 		-DNJS_FUZZER_TARGET \\
 		-o $NJS_BUILD_DIR/njs_process_script_fuzzer.o \\
-		src/njs_shell.c
+		external/njs_shell.c
 
 $NJS_BUILD_DIR/njs_process_script_fuzzer: \\
 	$NJS_BUILD_DIR/libnjs.a \\
diff -r d610d744bbd2 -r fc8d1b125cef external/njs_shell.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/external/njs_shell.c	Tue May 09 18:18:33 2023 -0700
@@ -0,0 +1,1591 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs.h>
+#include <njs_unix.h>
+#include <njs_time.h>
+#include <njs_arr.h>
+#include <njs_queue.h>
+#include <njs_rbtree.h>
+#include <njs_lvlhsh.h>
+#include <njs_djb_hash.h>
+
+#if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE)
+
+#include <locale.h>
+#include <signal.h>
+#include <sys/select.h>
+#if (NJS_HAVE_EDITLINE)
+#include <editline/readline.h>
+#elif (NJS_HAVE_EDIT_READLINE)
+#include <edit/readline/readline.h>
+#else
+#include <readline/readline.h>
+#if (NJS_HAVE_GNU_READLINE)
+#include <readline/history.h>
+#endif
+#endif
+
+#endif
+
+
+typedef void (*njs_console_output_pt)(njs_vm_t *vm, njs_value_t *value,
+    njs_int_t ret);
+
+
+typedef struct {
+    uint8_t                 disassemble;
+    uint8_t                 denormals;
+    uint8_t                 interactive;
+    uint8_t                 module;
+    uint8_t                 quiet;
+    uint8_t                 sandbox;
+    uint8_t                 safe;
+    uint8_t                 version;
+    uint8_t                 ast;
+    uint8_t                 unhandled_rejection;
+    uint8_t                 opcode_debug;
+    uint8_t                 generator_debug;
+    int                     exit_code;
+    int                     stack_size;
+
+    char                    *file;
+    char                    *command;
+    size_t                  n_paths;
+    char                    **paths;
+    char                    **argv;
+    njs_uint_t              argc;
+} njs_opts_t;
+
+
+typedef struct {
+    size_t                  index;
+    size_t                  length;
+    njs_arr_t               *completions;
+    njs_arr_t               *suffix_completions;
+    njs_rbtree_node_t       *node;
+
+    enum {
+       NJS_COMPLETION_SUFFIX = 0,
+       NJS_COMPLETION_GLOBAL
+    }                       phase;
+} njs_completion_t;
+
+
+typedef struct {
+    njs_vm_event_t          vm_event;
+    njs_queue_link_t        link;
+} njs_ev_t;
+
+
+typedef struct {
+    njs_opaque_value_t      name;
+    uint64_t                time;
+} njs_timelabel_t;
+
+
+typedef struct {
+    njs_vm_t                *vm;
+
+    njs_lvlhsh_t            events;  /* njs_ev_t * */
+    njs_queue_t             posted_events;
+
+    njs_lvlhsh_t            labels;  /* njs_timelabel_t */
+
+    njs_completion_t        completion;
+} njs_console_t;
+
+
+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_externals_init(njs_vm_t *vm);
+static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options);
+static void njs_process_output(njs_vm_t *vm, njs_value_t *value, njs_int_t ret);
+static njs_int_t njs_process_script(njs_vm_t *vm, void *runtime,
+    const njs_str_t *script);
+
+#ifndef NJS_FUZZER_TARGET
+
+static njs_int_t njs_options_parse(njs_opts_t *opts, int argc, char **argv);
+static void njs_options_free(njs_opts_t *opts);
+static njs_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options);
+
+#ifdef NJS_HAVE_READLINE
+static njs_int_t njs_interactive_shell(njs_opts_t *opts,
+    njs_vm_opt_t *vm_options);
+static njs_int_t njs_editline_init(void);
+static char *njs_completion_generator(const char *text, int state);
+#endif
+
+#endif
+
+static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t indent, njs_value_t *retval);
+static njs_int_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
+
+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 void njs_console_log(njs_vm_t *vm, njs_external_ptr_t external,
+    njs_log_level_t level, const u_char *start, size_t length);
+
+static njs_int_t njs_timelabel_hash_test(njs_lvlhsh_query_t *lhq, void *data);
+
+static njs_int_t lvlhsh_key_test(njs_lvlhsh_query_t *lhq, void *data);
+static void *lvlhsh_pool_alloc(void *pool, size_t size);
+static void lvlhsh_pool_free(void *pool, void *p, size_t size);
+
+
+static njs_external_t  njs_ext_console[] = {
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("dump"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_log,
+            .magic8 = 1,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("log"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_log,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Console",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("time"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_time,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("timeEnd"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_ext_console_time_end,
+        }
+    },
+
+};
+
+
+static const njs_lvlhsh_proto_t  lvlhsh_proto  njs_aligned(64) = {
+    NJS_LVLHSH_LARGE_SLAB,
+    lvlhsh_key_test,
+    lvlhsh_pool_alloc,
+    lvlhsh_pool_free,
+};
+
+
+static const njs_lvlhsh_proto_t  njs_timelabel_hash_proto njs_aligned(64) = {
+    NJS_LVLHSH_DEFAULT,
+    njs_timelabel_hash_test,
+    lvlhsh_pool_alloc,
+    lvlhsh_pool_free,
+};
+
+
+static njs_vm_ops_t njs_console_ops = {
+    njs_console_set_timer,
+    njs_console_clear_timer,
+    NULL,
+    njs_console_log,
+};
+
+
+njs_module_t  njs_console_module = {
+    .name = njs_str("console"),
+    .init = njs_externals_init,
+};
+
+
+static njs_module_t *njs_console_addon_modules[] = {
+    &njs_console_module,
+    NULL,
+};
+
+
+static njs_int_t      njs_console_proto_id;
+
+
+static njs_console_t  njs_console;
+
+
+#ifndef NJS_FUZZER_TARGET
+
+int
+main(int argc, char **argv)
+{
+    njs_vm_t      *vm;
+    njs_int_t     ret;
+    njs_opts_t    opts;
+    njs_str_t     command;
+    njs_vm_opt_t  vm_options;
+
+    static uintptr_t uptr[] = {
+        (uintptr_t) njs_console_output,
+    };
+
+    static njs_vm_meta_t metas = {
+        .size = njs_nitems(uptr),
+        .values = uptr
+    };
+
+    njs_memzero(&opts, sizeof(njs_opts_t));
+    opts.interactive = 1;
+
+    ret = njs_options_parse(&opts, argc, argv);
+    if (ret != NJS_OK) {
+        ret = (ret == NJS_DONE) ? NJS_OK : NJS_ERROR;
+        goto done;
+    }
+
+    if (opts.version != 0) {
+        njs_printf("%s\n", NJS_VERSION);
+        ret = NJS_OK;
+        goto done;
+    }
+
+    njs_mm_denormals(opts.denormals);
+
+    njs_vm_opt_init(&vm_options);
+
+    if (opts.file == NULL) {
+        opts.file = (opts.command == NULL) ? (char *) "shell"
+                                           : (char *) "string";
+    }
+
+    vm_options.file.start = (u_char *) opts.file;
+    vm_options.file.length = njs_strlen(opts.file);
+
+    vm_options.init = 1;
+    vm_options.interactive = opts.interactive;
+    vm_options.disassemble = opts.disassemble;
+    vm_options.backtrace = 1;
+    vm_options.quiet = opts.quiet;
+    vm_options.sandbox = opts.sandbox;
+    vm_options.unsafe = !opts.safe;
+    vm_options.module = opts.module;
+#ifdef NJS_DEBUG_GENERATOR
+    vm_options.generator_debug = opts.generator_debug;
+#endif
+#ifdef NJS_DEBUG_OPCODE
+    vm_options.opcode_debug = opts.opcode_debug;
+#endif
+
+    vm_options.ops = &njs_console_ops;
+    vm_options.addons = njs_console_addon_modules;
+    vm_options.metas = &metas;
+    vm_options.external = &njs_console;
+    vm_options.argv = opts.argv;
+    vm_options.argc = opts.argc;
+    vm_options.ast = opts.ast;
+    vm_options.unhandled_rejection = opts.unhandled_rejection;
+
+    if (opts.stack_size != 0) {
+        vm_options.max_stack_size = opts.stack_size;
+    }
+
+#ifdef NJS_HAVE_READLINE
+
+    if (opts.interactive) {
+        ret = njs_interactive_shell(&opts, &vm_options);
+
+    } else
+
+#endif
+
+    if (opts.command) {
+        vm = njs_create_vm(&opts, &vm_options);
+        if (vm != NULL) {
+            command.start = (u_char *) opts.command;
+            command.length = njs_strlen(opts.command);
+            ret = njs_process_script(vm, njs_vm_external_ptr(vm), &command);
+            njs_vm_destroy(vm);
+        }
+
+    } else {
+        ret = njs_process_file(&opts, &vm_options);
+    }
+
+done:
+
+    njs_options_free(&opts);
+
+    return (ret == NJS_OK) ? EXIT_SUCCESS : opts.exit_code;
+}
+
+
+static njs_int_t
+njs_options_parse(njs_opts_t *opts, int argc, char **argv)
+{
+    char        *p, **paths;
+    njs_int_t   i, ret;
+    njs_uint_t  n;
+
+    static const char  help[] =
+        "njs [options] [-c string | script.js | -] [script args]\n"
+        "\n"
+        "Interactive shell: "
+#ifdef NJS_HAVE_READLINE
+        "enabled\n"
+#else
+        "disabled\n"
+#endif
+        "\n"
+        "Options:\n"
+        "  -a                print AST.\n"
+        "  -c                specify the command to execute.\n"
+        "  -d                print disassembled code.\n"
+        "  -e <code>         set failure exit code.\n"
+        "  -f                disabled denormals mode.\n"
+#ifdef NJS_DEBUG_GENERATOR
+        "  -g                enable generator debug.\n"
+#endif
+        "  -j <size>         set the maximum stack size in bytes.\n"
+#ifdef NJS_DEBUG_OPCODE
+        "  -o                enable opcode debug.\n"
+#endif
+        "  -p <path>         set path prefix for modules.\n"
+        "  -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->exit_code = EXIT_FAILURE;
+    opts->unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW;
+
+    p = getenv("NJS_EXIT_CODE");
+    if (p != NULL) {
+        opts->exit_code = atoi(p);
+    }
+
+    for (i = 1; i < argc; i++) {
+
+        p = argv[i];
+
+        if (p[0] != '-' || (p[0] == '-' && p[1] == '\0')) {
+            opts->interactive = 0;
+            opts->file = argv[i];
+            goto done;
+        }
+
+        p++;
+
+        switch (*p) {
+        case '?':
+        case 'h':
+            njs_printf("%*s", njs_length(help), help);
+            return ret;
+
+        case 'a':
+            opts->ast = 1;
+            break;
+
+        case 'c':
+            opts->interactive = 0;
+
+            if (++i < argc) {
+                opts->command = argv[i];
+                goto done;
+            }
+
+            njs_stderror("option \"-c\" requires argument\n");
+            return NJS_ERROR;
+
+        case 'd':
+            opts->disassemble = 1;
+            break;
+
+        case 'e':
+            if (++i < argc) {
+                opts->exit_code = atoi(argv[i]);
+                break;
+            }
+
+            njs_stderror("option \"-e\" requires argument\n");
+            return NJS_ERROR;
+
+        case 'f':
+
+#if !(NJS_HAVE_DENORMALS_CONTROL)
+            njs_stderror("option \"-f\" is not supported\n");
+            return NJS_ERROR;
+#endif
+
+            opts->denormals = 0;
+            break;
+
+#ifdef NJS_DEBUG_GENERATOR
+        case 'g':
+            opts->generator_debug = 1;
+            break;
+#endif
+        case 'j':
+            if (++i < argc) {
+                opts->stack_size = atoi(argv[i]);
+                break;
+            }
+
+            njs_stderror("option \"-j\" requires argument\n");
+            return NJS_ERROR;
+
+#ifdef NJS_DEBUG_OPCODE
+        case 'o':
+            opts->opcode_debug = 1;
+            break;
+#endif
+
+        case 'p':
+            if (++i < argc) {
+                opts->n_paths++;
+                paths = realloc(opts->paths, opts->n_paths * sizeof(char *));
+                if (paths == NULL) {
+                    njs_stderror("failed to add path\n");
+                    return NJS_ERROR;
+                }
+
+                opts->paths = paths;
+                opts->paths[opts->n_paths - 1] = argv[i];
+                break;
+            }
+
+            njs_stderror("option \"-p\" requires directory name\n");
+            return NJS_ERROR;
+
+        case 'q':
+            opts->quiet = 1;
+            break;
+
+        case 'r':
+            opts->unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE;
+            break;
+
+        case 's':
+            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;
+            break;
+
+        case 'u':
+            opts->safe = 1;
+            break;
+
+        default:
+            njs_stderror("Unknown argument: \"%s\" "
+                         "try \"%s -h\" for available options\n", argv[i],
+                         argv[0]);
+            return NJS_ERROR;
+        }
+    }
+
+done:
+
+    opts->argc = njs_max(argc - i + 1, 2);
+    opts->argv = malloc(sizeof(char*) * opts->argc);
+    if (opts->argv == NULL) {
+        njs_stderror("failed to alloc argv\n");
+        return NJS_ERROR;
+    }
+
+    opts->argv[0] = argv[0];
+    opts->argv[1] = (opts->file != NULL) ? opts->file : (char *) "";
+    for (n = 2; n < opts->argc; n++) {
+        opts->argv[n] = argv[i + n - 1];
+    }
+
+    return NJS_OK;
+}
+
+
+static void
+njs_options_free(njs_opts_t *opts)
+{
+    if (opts->paths != NULL) {
+        free(opts->paths);
+    }
+
+    if (opts->argv != NULL) {
+        free(opts->argv);
+    }
+}
+
+
+static njs_int_t
+njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options)
+{
+    int          fd;
+    char         *file;
+    u_char       *p, *end, *start;
+    size_t       size;
+    ssize_t      n;
+    njs_vm_t     *vm;
+    njs_int_t    ret;
+    njs_str_t    source, script;
+    struct stat  sb;
+    u_char       buf[4096];
+
+    file = opts->file;
+
+    if (file[0] == '-' && file[1] == '\0') {
+        fd = STDIN_FILENO;
+
+    } else {
+        fd = open(file, O_RDONLY);
+        if (fd == -1) {
+            njs_stderror("failed to open file: '%s' (%s)\n",
+                         file, strerror(errno));
+            return NJS_ERROR;
+        }
+    }
+
+    if (fstat(fd, &sb) == -1) {
+        njs_stderror("fstat(%d) failed while reading '%s' (%s)\n",
+                     fd, file, strerror(errno));
+        ret = NJS_ERROR;
+        goto close_fd;
+    }
+
+    size = sizeof(buf);
+
+    if (S_ISREG(sb.st_mode) && sb.st_size) {
+        size = sb.st_size;
+    }
+
+    vm = NULL;
+
+    source.length = 0;
+    source.start = realloc(NULL, size);
+    if (source.start == NULL) {
+        njs_stderror("alloc failed while reading '%s'\n", file);
+        ret = NJS_ERROR;
+        goto done;
+    }
+
+    p = source.start;
+    end = p + size;
+
+    for ( ;; ) {
+        n = read(fd, buf, sizeof(buf));
+
+        if (n == 0) {
+            break;
+        }
+
+        if (n < 0) {
+            njs_stderror("failed to read file: '%s' (%s)\n",
+                      file, strerror(errno));
+            ret = NJS_ERROR;
+            goto done;
+        }
+
+        if (p + n > end) {
+            size *= 2;
+
+            start = realloc(source.start, size);
+            if (start == NULL) {
+                njs_stderror("alloc failed while reading '%s'\n", file);
+                ret = NJS_ERROR;
+                goto done;
+            }
+
+            source.start = start;
+
+            p = source.start + source.length;
+            end = source.start + size;
+        }
+
+        memcpy(p, buf, n);
+
+        p += n;
+        source.length += n;
+    }
+
+    vm = njs_create_vm(opts, vm_options);
+    if (vm == NULL) {
+        ret = NJS_ERROR;
+        goto done;
+    }
+
+    script = source;
+
+    /* shebang */
+
+    if (script.length > 2 && memcmp(script.start, "#!", 2) == 0) {
+        p = njs_strlchr(script.start, script.start + script.length, '\n');
+
+        if (p != NULL) {
+            script.length -= (p + 1 - script.start);
+            script.start = p + 1;
+
+        } else {
+            script.length = 0;
+        }
+    }
+
+    ret = njs_process_script(vm, vm_options->external, &script);
+    if (ret != NJS_OK) {
+        ret = NJS_ERROR;
+        goto done;
+    }
+
+    ret = NJS_OK;
+
+done:
+
+    if (vm != NULL) {
+        njs_vm_destroy(vm);
+    }
+
+    if (source.start != NULL) {
+        free(source.start);
+    }
+
+close_fd:
+
+    if (fd != STDIN_FILENO) {
+        (void) close(fd);
+    }
+
+    return ret;
+}
+
+#else
+
+int
+LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{
+    njs_vm_t      *vm;
+    njs_opts_t    opts;
+    njs_str_t     script;
+    njs_vm_opt_t  vm_options;
+
+    static uintptr_t uptr[] = {
+        (uintptr_t) NULL,
+    };
+
+    static njs_vm_meta_t metas = {
+        .size = njs_nitems(uptr),
+        .values = uptr
+    };
+
+    if (size == 0) {
+        return 0;
+    }
+
+    njs_memzero(&opts, sizeof(njs_opts_t));
+
+    njs_vm_opt_init(&vm_options);
+
+    vm_options.init = 1;
+    vm_options.backtrace = 0;
+    vm_options.metas = &metas;
+    vm_options.ops = &njs_console_ops;
+
+    vm = njs_create_vm(&opts, &vm_options);
+
+    if (njs_fast_path(vm != NULL)) {
+        script.length = size;
+        script.start = (u_char *) data;
+
+        (void) njs_process_script(vm, NULL, &script);
+        njs_vm_destroy(vm);
+    }
+
+    return 0;
+}
+
+#endif
+
+static njs_int_t
+njs_console_init(njs_vm_t *vm, njs_console_t *console)
+{
+    console->vm = vm;
+
+    njs_lvlhsh_init(&console->events);
+    njs_queue_init(&console->posted_events);
+
+    njs_lvlhsh_init(&console->labels);
+
+    console->completion.completions = njs_vm_completions(vm, NULL);
+    if (console->completion.completions == NULL) {
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_externals_init(njs_vm_t *vm)
+{
+    njs_int_t           ret;
+    njs_value_t         *value;
+    njs_console_t       *console;
+    njs_opaque_value_t  method;
+
+    static const njs_str_t  console_name = njs_str("console");
+    static const njs_str_t  print_name = njs_str("print");
+    static const njs_str_t  console_log = njs_str("console.log");
+
+    console = njs_vm_options(vm)->external;
+
+    njs_console_proto_id = njs_vm_external_prototype(vm, njs_ext_console,
+                                         njs_nitems(njs_ext_console));
+    if (njs_slow_path(njs_console_proto_id < 0)) {
+        njs_stderror("failed to add \"console\" proto\n");
+        return NJS_ERROR;
+    }
+
+    value = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_opaque_value_t));
+    if (njs_slow_path(value == NULL)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_vm_external_create(vm, value, njs_console_proto_id, console, 0);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_vm_bind(vm, &console_name, value, 0);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_vm_value(vm, &console_log, njs_value_arg(&method));
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_vm_bind(vm, &print_name, njs_value_arg(&method), 0);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_console_init(vm, console);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_vm_t *
+njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options)
+{
+    u_char      *p, *start;
+    njs_vm_t    *vm;
+    njs_int_t   ret;
+    njs_str_t   path;
+    njs_uint_t  i;
+
+    vm = njs_vm_create(vm_options);
+    if (vm == NULL) {
+        njs_stderror("failed to create vm\n");
+        return NULL;
+    }
+
+    for (i = 0; i < opts->n_paths; i++) {
+        path.start = (u_char *) opts->paths[i];
+        path.length = njs_strlen(opts->paths[i]);
+
+        ret = njs_vm_add_path(vm, &path);
+        if (ret != NJS_OK) {
+            njs_stderror("failed to add path\n");
+            return NULL;
+        }
+    }
+
+    start = (u_char *) getenv("NJS_PATH");
+    if (start == NULL) {
+        return vm;
+    }
+
+    for ( ;; ) {
+        p = njs_strchr(start, ':');
+
+        path.start = start;
+        path.length = (p != NULL) ? (size_t) (p - start) : njs_strlen(start);
+
+        ret = njs_vm_add_path(vm, &path);
+        if (ret != NJS_OK) {
+            njs_stderror("failed to add path\n");
+            return NULL;
+        }
+
+        if (p == NULL) {
+            break;
+        }
+
+        start = p + 1;
+    }
+
+    return vm;
+}
+
+
+static void
+njs_console_output(njs_vm_t *vm, njs_value_t *value, njs_int_t ret)
+{
+    njs_str_t  out;
+
+    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) {
+            njs_print(out.start, out.length);
+            njs_print("\n", 1);
+        }
+
+    } else {
+        njs_vm_exception_string(vm, &out);
+        njs_stderror("Thrown:\n%V\n", &out);
+    }
+}
+
+
+static njs_int_t
+njs_process_events(void *runtime)
+{
+    njs_ev_t          *ev;
+    njs_queue_t       *events;
+    njs_console_t     *console;
+    njs_queue_link_t  *link;
+
+    if (runtime == NULL) {
+        njs_stderror("njs_process_events(): no runtime\n");
+        return NJS_ERROR;
+    }
+
+    console = runtime;
+
+    events = &console->posted_events;
+
+    for ( ;; ) {
+        link = njs_queue_first(events);
+
+        if (link == njs_queue_tail(events)) {
+            break;
+        }
+
+        ev = njs_queue_link_data(link, njs_ev_t, link);
+
+        njs_queue_remove(&ev->link);
+        ev->link.prev = NULL;
+        ev->link.next = NULL;
+
+        njs_vm_post_event(console->vm, ev->vm_event, NULL, 0);
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_process_script(njs_vm_t *vm, void *runtime, const njs_str_t *script)
+{
+    u_char              *start, *end;
+    njs_int_t           ret;
+    njs_opaque_value_t  retval;
+
+    start = script->start;
+    end = start + script->length;
+
+    ret = njs_vm_compile(vm, &start, end);
+
+    if (ret == NJS_OK) {
+        if (start == end) {
+            ret = njs_vm_start(vm, njs_value_arg(&retval));
+
+        } else {
+            njs_vm_error(vm, "Extra characters at the end of the script");


More information about the nginx-devel mailing list