[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