From 28dd204a89c3ee2b094698e54e5f92d603af3b64 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Thu, 3 May 2018 18:29:26 +0300 Subject: [PATCH] Renamed njscript to njs. njscript.c -> njs.c njscript.h -> njs.h njs.c -> njs_shell.c --- Makefile | 74 +-- nginx/ngx_http_js_module.c | 2 +- nginx/ngx_stream_js_module.c | 2 +- njs/njs.c | 971 +++++++++++++++++--------------- njs/{njscript.h => njs.h} | 8 +- njs/njs_core.h | 2 +- njs/njs_shell.c | 651 +++++++++++++++++++++ njs/test/njs_benchmark.c | 2 +- njs/test/njs_interactive_test.c | 2 +- 9 files changed, 1210 insertions(+), 504 deletions(-) rename njs/{njscript.h => njs.h} (98%) create mode 100644 njs/njs_shell.c diff --git a/Makefile b/Makefile index 8946813f..26e7c13b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/libnjs.a: \ $(NXT_LIB)/nxt_auto_config.h \ - $(NXT_BUILDDIR)/njscript.o \ + $(NXT_BUILDDIR)/njs_shell.o \ $(NXT_BUILDDIR)/njs_vm.o \ $(NXT_BUILDDIR)/njs_boolean.o \ $(NXT_BUILDDIR)/njs_number.o \ @@ -49,7 +49,7 @@ $(NXT_BUILDDIR)/libnjs.a: \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ ar -r -c $(NXT_BUILDDIR)/libnjs.a \ - $(NXT_BUILDDIR)/njscript.o \ + $(NXT_BUILDDIR)/njs_shell.o \ $(NXT_BUILDDIR)/njs_vm.o \ $(NXT_BUILDDIR)/njs_boolean.o \ $(NXT_BUILDDIR)/njs_number.o \ @@ -110,7 +110,7 @@ clean: rm -f $(NXT_LIB)/Makefile.conf $(NXT_LIB)/nxt_auto_config.h dist: - NJS_VER=`grep NJS_VERSION njs/njscript.h | sed -e 's/.*"\(.*\)".*/\1/'`; \ + NJS_VER=`grep NJS_VERSION njs/njs.h | sed -e 's/.*"\(.*\)".*/\1/'`; \ rm -rf njs-$${NJS_VER} \ && hg archive njs-$${NJS_VER}.tar.gz \ -p njs-$${NJS_VER} \ @@ -123,25 +123,25 @@ $(NXT_LIB)/nxt_auto_config.h: @echo @exit 1 -$(NXT_BUILDDIR)/njscript.o: \ +$(NXT_BUILDDIR)/njs_shell.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_string.h \ njs/njs_object.h \ njs/njs_function.h \ njs/njs_parser.h \ - njs/njscript.h \ - njs/njscript.c \ + njs/njs.h \ + njs/njs.c \ - $(NXT_CC) -c -o $(NXT_BUILDDIR)/njscript.o $(NXT_CFLAGS) \ + $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_shell.o $(NXT_CFLAGS) \ -I$(NXT_LIB) -Injs \ - njs/njscript.c + njs/njs.c $(NXT_BUILDDIR)/njs_vm.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_number.h \ @@ -162,7 +162,7 @@ $(NXT_BUILDDIR)/njs_vm.o: \ $(NXT_BUILDDIR)/njs_boolean.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_vm.h \ njs/njs_boolean.h \ njs/njs_object.h \ @@ -175,7 +175,7 @@ $(NXT_BUILDDIR)/njs_boolean.o: \ $(NXT_BUILDDIR)/njs_number.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_number.h \ @@ -191,7 +191,7 @@ $(NXT_BUILDDIR)/njs_number.o: \ $(NXT_BUILDDIR)/njs_string.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_number.h \ @@ -210,7 +210,7 @@ $(NXT_BUILDDIR)/njs_string.o: \ $(NXT_BUILDDIR)/njs_object.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_object.h \ @@ -224,7 +224,7 @@ $(NXT_BUILDDIR)/njs_object.o: \ $(NXT_BUILDDIR)/njs_array.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_number.h \ @@ -241,7 +241,7 @@ $(NXT_BUILDDIR)/njs_array.o: \ $(NXT_BUILDDIR)/njs_json.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_object.h \ @@ -254,7 +254,7 @@ $(NXT_BUILDDIR)/njs_json.o: \ $(NXT_BUILDDIR)/njs_function.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_object.h \ @@ -268,7 +268,7 @@ $(NXT_BUILDDIR)/njs_function.o: \ $(NXT_BUILDDIR)/njs_regexp.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_string.h \ @@ -285,7 +285,7 @@ $(NXT_BUILDDIR)/njs_regexp.o: \ $(NXT_BUILDDIR)/njs_date.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_string.h \ @@ -300,7 +300,7 @@ $(NXT_BUILDDIR)/njs_date.o: \ $(NXT_BUILDDIR)/njs_error.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_string.h \ @@ -315,7 +315,7 @@ $(NXT_BUILDDIR)/njs_error.o: \ $(NXT_BUILDDIR)/njs_math.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_object.h \ @@ -328,7 +328,7 @@ $(NXT_BUILDDIR)/njs_math.o: \ $(NXT_BUILDDIR)/njs_time.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_object.h \ @@ -341,7 +341,7 @@ $(NXT_BUILDDIR)/njs_time.o: \ $(NXT_BUILDDIR)/njs_module.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_module.h \ @@ -353,7 +353,7 @@ $(NXT_BUILDDIR)/njs_module.o: \ $(NXT_BUILDDIR)/njs_event.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_event.h \ @@ -365,7 +365,7 @@ $(NXT_BUILDDIR)/njs_event.o: \ $(NXT_BUILDDIR)/njs_fs.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_vm.h \ njs/njs_fs.h \ njs/njs_fs.c \ @@ -376,7 +376,7 @@ $(NXT_BUILDDIR)/njs_fs.o: \ $(NXT_BUILDDIR)/njs_crypto.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_crypto.h \ @@ -388,7 +388,7 @@ $(NXT_BUILDDIR)/njs_crypto.o: \ $(NXT_BUILDDIR)/njs_extern.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_parser.h \ @@ -401,7 +401,7 @@ $(NXT_BUILDDIR)/njs_extern.o: \ $(NXT_BUILDDIR)/njs_variable.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_parser.h \ @@ -414,7 +414,7 @@ $(NXT_BUILDDIR)/njs_variable.o: \ $(NXT_BUILDDIR)/njs_builtin.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_boolean.h \ @@ -434,7 +434,7 @@ $(NXT_BUILDDIR)/njs_builtin.o: \ $(NXT_BUILDDIR)/njs_lexer.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_parser.h \ @@ -446,7 +446,7 @@ $(NXT_BUILDDIR)/njs_lexer.o: \ $(NXT_BUILDDIR)/njs_lexer_keyword.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_vm.h \ njs/njs_number.h \ njs/njs_object.h \ @@ -459,7 +459,7 @@ $(NXT_BUILDDIR)/njs_lexer_keyword.o: \ $(NXT_BUILDDIR)/njs_parser.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_string.h \ @@ -475,7 +475,7 @@ $(NXT_BUILDDIR)/njs_parser.o: \ $(NXT_BUILDDIR)/njs_parser_expression.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_number.h \ @@ -491,7 +491,7 @@ $(NXT_BUILDDIR)/njs_parser_expression.o: \ $(NXT_BUILDDIR)/njs_generator.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_number.h \ @@ -508,7 +508,7 @@ $(NXT_BUILDDIR)/njs_generator.o: \ $(NXT_BUILDDIR)/njs_disassembler.o: \ $(NXT_BUILDDIR)/libnxt.a \ - njs/njscript.h \ + njs/njs.h \ njs/njs_core.h \ njs/njs_vm.h \ njs/njs_object.h \ @@ -522,11 +522,11 @@ $(NXT_BUILDDIR)/njs_disassembler.o: \ $(NXT_BUILDDIR)/njs: \ $(NXT_BUILDDIR)/libnxt.a \ $(NXT_BUILDDIR)/libnjs.a \ - njs/njs.c \ + njs/njs_shell.c \ $(NXT_CC) -o $(NXT_BUILDDIR)/njs $(NXT_CFLAGS) \ -I$(NXT_LIB) $(NXT_EDITLINE_CFLAGS) -Injs \ - njs/njs.c \ + njs/njs_shell.c \ $(NXT_BUILDDIR)/libnjs.a \ -lm $(NXT_PCRE_LIB) $(NXT_EDITLINE_LIB) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 3dc12d46..5a5cb68f 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -9,7 +9,7 @@ #include #include -#include +#include typedef struct { diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 05b9b77d..80051fb4 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -9,7 +9,7 @@ #include #include -#include +#include typedef struct { diff --git a/njs/njs.c b/njs/njs.c index c83b1098..fc5de1b9 100644 --- a/njs/njs.c +++ b/njs/njs.c @@ -1,651 +1,706 @@ /* - * Copyright (C) Dmitry Volyntsev + * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ - #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include - -#include - - -typedef enum { - NJS_COMPLETION_GLOBAL = 0, - NJS_COMPLETION_SUFFIX, -} njs_completion_phase_t; - - -typedef struct { - char *file; - nxt_int_t version; - nxt_int_t disassemble; - nxt_int_t interactive; -} njs_opts_t; - - -typedef struct { - size_t index; - size_t length; - njs_vm_t *vm; - nxt_array_t *completions; - nxt_array_t *suffix_completions; - nxt_lvlhsh_each_t lhe; - njs_completion_phase_t phase; -} njs_completion_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_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, - const nxt_str_t *script, nxt_str_t *out); -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); - -static njs_ret_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, - nxt_uint_t nargs, njs_index_t unused); -static njs_ret_t njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, - nxt_uint_t nargs, njs_index_t unused); - - -static njs_external_t njs_ext_console[] = { - - { nxt_string("log"), - NJS_EXTERN_METHOD, - NULL, - 0, - NULL, - NULL, - NULL, - NULL, - NULL, - njs_ext_console_log, - 0 }, - - { nxt_string("help"), - NJS_EXTERN_METHOD, - NULL, - 0, - NULL, - NULL, - NULL, - NULL, - NULL, - njs_ext_console_help, - 0 }, -}; -static njs_external_t njs_externals[] = { - - { nxt_string("console"), - NJS_EXTERN_OBJECT, - njs_ext_console, - nxt_nitems(njs_ext_console), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - 0 }, -}; +static nxt_int_t njs_vm_init(njs_vm_t *vm); +static nxt_int_t njs_vm_handle_events(njs_vm_t *vm); -static njs_completion_t njs_completion; + +static void * +njs_alloc(void *mem, size_t size) +{ + return nxt_malloc(size); +} -int -main(int argc, char **argv) +static void * +njs_zalloc(void *mem, size_t size) { - nxt_int_t ret; - njs_opts_t opts; - njs_vm_opt_t vm_options; + void *p; - memset(&opts, 0, sizeof(njs_opts_t)); - opts.interactive = 1; + p = nxt_malloc(size); - ret = njs_get_options(&opts, argc, argv); - if (ret != NXT_OK) { - return (ret == NXT_DONE) ? EXIT_SUCCESS : EXIT_FAILURE; + if (p != NULL) { + memset(p, 0, size); } - if (opts.version != 0) { - printf("%s\n", NJS_VERSION); - return EXIT_SUCCESS; - } + return p; +} - memset(&vm_options, 0, sizeof(njs_vm_opt_t)); - vm_options.accumulative = 1; - vm_options.backtrace = 1; +static void * +njs_align(void *mem, size_t alignment, size_t size) +{ + return nxt_memalign(alignment, size); +} - if (opts.interactive) { - ret = njs_interactive_shell(&opts, &vm_options); - } else { - ret = njs_process_file(&opts, &vm_options); - } +static void +njs_free(void *mem, void *p) +{ + nxt_free(p); +} - return (ret == NXT_OK) ? EXIT_SUCCESS : EXIT_FAILURE; + +const nxt_mem_proto_t njs_vm_mem_cache_pool_proto = { + njs_alloc, + njs_zalloc, + njs_align, + NULL, + njs_free, + NULL, + NULL, +}; + + +static void * +njs_array_mem_alloc(void *mem, size_t size) +{ + return nxt_mem_cache_alloc(mem, size); } -static nxt_int_t -njs_get_options(njs_opts_t *opts, int argc, char** argv) +static void +njs_array_mem_free(void *mem, void *p) +{ + nxt_mem_cache_free(mem, p); +} + + +const nxt_mem_proto_t njs_array_mem_proto = { + njs_array_mem_alloc, + NULL, + NULL, + NULL, + njs_array_mem_free, + NULL, + NULL, +}; + + +njs_vm_t * +njs_vm_create(njs_vm_opt_t *options) { - char *p; - nxt_int_t i, ret; + njs_vm_t *vm; + nxt_int_t ret; + nxt_array_t *debug; + nxt_mem_cache_pool_t *mcp; + njs_regexp_pattern_t *pattern; - ret = NXT_DONE; + mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, + NULL, 2 * nxt_pagesize(), 128, 512, 16); + if (nxt_slow_path(mcp == NULL)) { + return NULL; + } - for (i = 1; i < argc; i++) { + vm = nxt_mem_cache_zalign(mcp, sizeof(njs_value_t), sizeof(njs_vm_t)); - p = argv[i]; + if (nxt_fast_path(vm != NULL)) { + vm->mem_cache_pool = mcp; - if (p[0] != '-' || (p[0] == '-' && p[1] == '\0')) { - opts->interactive = 0; - opts->file = argv[i]; - continue; + ret = njs_regexp_init(vm); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; } - p++; + if (options->shared != NULL) { + vm->shared = options->shared; - switch (*p) { - case 'd': - opts->disassemble = 1; - break; + } else { + vm->shared = nxt_mem_cache_zalloc(mcp, sizeof(njs_vm_shared_t)); + if (nxt_slow_path(vm->shared == NULL)) { + return NULL; + } - case 'V': - opts->version = 1; - break; + options->shared = vm->shared; - default: - fprintf(stderr, "Unknown argument: \"%s\"\n", argv[i]); - ret = NXT_ERROR; + nxt_lvlhsh_init(&vm->shared->keywords_hash); - /* Fall through. */ + ret = njs_lexer_keywords_init(mcp, &vm->shared->keywords_hash); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } - case 'h': - case '?': - printf("Usage: %s [|-] [-dV]\n", argv[0]); - return ret; + nxt_lvlhsh_init(&vm->shared->values_hash); + + pattern = njs_regexp_pattern_create(vm, (u_char *) "(?:)", + sizeof("(?:)") - 1, 0); + if (nxt_slow_path(pattern == NULL)) { + return NULL; + } + + vm->shared->empty_regexp_pattern = pattern; + + nxt_lvlhsh_init(&vm->modules_hash); + + ret = njs_builtin_objects_create(vm); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + } + + nxt_lvlhsh_init(&vm->values_hash); + + vm->external = options->external; + + vm->external_objects = nxt_array_create(4, sizeof(void *), + &njs_array_mem_proto, + vm->mem_cache_pool); + if (nxt_slow_path(vm->external_objects == NULL)) { + return NULL; + } + + nxt_lvlhsh_init(&vm->externals_hash); + nxt_lvlhsh_init(&vm->external_prototypes_hash); + + vm->ops = options->ops; + + vm->trace.level = NXT_LEVEL_TRACE; + vm->trace.size = 2048; + vm->trace.handler = njs_parser_trace_handler; + vm->trace.data = vm; + + vm->trailer = options->trailer; + + if (options->backtrace) { + debug = nxt_array_create(4, sizeof(njs_function_debug_t), + &njs_array_mem_proto, + vm->mem_cache_pool); + if (nxt_slow_path(debug == NULL)) { + return NULL; + } + + vm->debug = debug; + } + + vm->accumulative = options->accumulative; + if (vm->accumulative) { + ret = njs_vm_init(vm); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + vm->retval = njs_value_void; } } - return NXT_OK; + return vm; } -static nxt_int_t -njs_externals_init(njs_vm_t *vm) +void +njs_vm_destroy(njs_vm_t *vm) { - nxt_uint_t ret; - const njs_extern_t *proto; - njs_opaque_value_t *value; + njs_event_t *event; + nxt_lvlhsh_each_t lhe; - static const nxt_str_t name = nxt_string_value("console"); - - proto = njs_vm_external_prototype(vm, &njs_externals[0]); - if (proto == NULL) { - fprintf(stderr, "failed to add console proto\n"); - return NXT_ERROR; - } + if (njs_is_pending_events(vm)) { + nxt_lvlhsh_each_init(&lhe, &njs_event_hash_proto); - value = nxt_mem_cache_zalloc(vm->mem_cache_pool, - sizeof(njs_opaque_value_t)); - if (value == NULL) { - return NXT_ERROR; - } + for ( ;; ) { + event = nxt_lvlhsh_each(&vm->events_hash, &lhe); - ret = njs_vm_external_create(vm, value, proto, NULL); - if (ret != NXT_OK) { - return NXT_ERROR; - } + if (event == NULL) { + break; + } - ret = njs_vm_external_bind(vm, &name, value); - if (ret != NXT_OK) { - return NXT_ERROR; + njs_del_event(vm, event, NJS_EVENT_RELEASE); + } } - return NXT_OK; + nxt_mem_cache_pool_destroy(vm->mem_cache_pool); } -static nxt_int_t -njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) +nxt_int_t +njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) { - njs_vm_t *vm; - nxt_int_t ret; - nxt_str_t line, out; + nxt_int_t ret; + njs_lexer_t *lexer; + njs_parser_t *parser, *prev; + njs_parser_node_t *node; - vm = njs_vm_create(vm_options); - if (vm == NULL) { - fprintf(stderr, "failed to create vm\n"); - return NXT_ERROR; + parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t)); + if (nxt_slow_path(parser == NULL)) { + return NJS_ERROR; } - if (njs_externals_init(vm) != NXT_OK) { - fprintf(stderr, "failed to add external protos\n"); - return NXT_ERROR; + if (vm->parser != NULL && !vm->accumulative) { + return NJS_ERROR; } - if (njs_editline_init(vm) != NXT_OK) { - fprintf(stderr, "failed to init completions\n"); - return NXT_ERROR; + prev = vm->parser; + vm->parser = parser; + + lexer = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_lexer_t)); + if (nxt_slow_path(lexer == NULL)) { + return NJS_ERROR; } - printf("interactive njscript %s\n\n", NJS_VERSION); + parser->lexer = lexer; + lexer->start = *start; + lexer->end = end; + lexer->line = 1; + lexer->keywords_hash = vm->shared->keywords_hash; - printf("v. -> the properties and prototype methods of v.\n"); - printf("type console.help() for more information\n\n"); + parser->code_size = sizeof(njs_vmcode_stop_t); + parser->scope_offset = NJS_INDEX_GLOBAL_OFFSET; - for ( ;; ) { - line.start = (u_char *) readline(">> "); - if (line.start == NULL) { - break; - } + if (vm->backtrace != NULL) { + nxt_array_reset(vm->backtrace); + } - line.length = strlen((char *) line.start); - if (line.length == 0) { - continue; - } + node = njs_parser(vm, parser, prev); + if (nxt_slow_path(node == NULL)) { + goto fail; + } - add_history((char *) line.start); + ret = njs_variables_scope_reference(vm, parser->scope); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } - ret = njs_process_script(vm, opts, &line, &out); - if (ret != NXT_OK) { - printf("shell: failed to get retval from VM\n"); - continue; - } + *start = parser->lexer->start; - printf("%.*s\n", (int) out.length, out.start); + /* + * Reset the code array to prevent it from being disassembled + * again in the next iteration of the accumulative mode. + */ + vm->code = NULL; - /* editline allocs a new buffer every time. */ - free(line.start); + ret = njs_generate_scope(vm, parser, node); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; } - return NXT_OK; + vm->current = parser->code_start; + + vm->global_scope = parser->local_scope; + vm->scope_size = parser->scope_size; + vm->variables_hash = parser->scope->variables; + + return NJS_OK; + +fail: + + vm->parser = prev; + + return NXT_ERROR; } -static nxt_int_t -njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options) +njs_vm_t * +njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external) { - int fd; - char *file; - u_char buf[4096], *p, *end, *start; - size_t size; - ssize_t n; - njs_vm_t *vm; - nxt_int_t ret; - nxt_str_t out, script; - struct stat sb; - - file = opts->file; + njs_vm_t *nvm; + uint32_t items; + nxt_int_t ret; + nxt_array_t *externals; + nxt_mem_cache_pool_t *nmcp; - if (file[0] == '-' && file[1] == '\0') { - fd = STDIN_FILENO; + nxt_thread_log_debug("CLONE:"); - } else { - fd = open(file, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "failed to open file: '%s' (%s)\n", - file, strerror(errno)); - return NXT_ERROR; - } + if (vm->accumulative) { + return NULL; } - if (fstat(fd, &sb) == -1) { - fprintf(stderr, "fstat(%d) failed while reading '%s' (%s)\n", - fd, file, strerror(errno)); - ret = NXT_ERROR; - goto close_fd; + nmcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL, + NULL, 2 * nxt_pagesize(), 128, 512, 16); + if (nxt_slow_path(nmcp == NULL)) { + return NULL; } - size = sizeof(buf); + nvm = nxt_mem_cache_zalign(nmcp, sizeof(njs_value_t), sizeof(njs_vm_t)); - if (S_ISREG(sb.st_mode) && sb.st_size) { - size = sb.st_size; - } + if (nxt_fast_path(nvm != NULL)) { + nvm->mem_cache_pool = nmcp; - script.length = 0; - script.start = realloc(NULL, size); - if (script.start == NULL) { - fprintf(stderr, "alloc failed while reading '%s'\n", file); - ret = NXT_ERROR; - goto done; - } + nvm->shared = vm->shared; - p = script.start; - end = p + size; + nvm->variables_hash = vm->variables_hash; + nvm->values_hash = vm->values_hash; + nvm->modules_hash = vm->modules_hash; - for ( ;; ) { - n = read(fd, buf, sizeof(buf)); + nvm->externals_hash = vm->externals_hash; + nvm->external_prototypes_hash = vm->external_prototypes_hash; - if (n == 0) { - break; + items = vm->external_objects->items; + externals = nxt_array_create(items + 4, sizeof(void *), + &njs_array_mem_proto, nvm->mem_cache_pool); + + if (nxt_slow_path(externals == NULL)) { + return NULL; } - if (n < 0) { - fprintf(stderr, "failed to read file: '%s' (%s)\n", - file, strerror(errno)); - ret = NXT_ERROR; - goto done; + if (items > 0) { + memcpy(externals->start, vm->external_objects->start, + items * sizeof(void *)); + externals->items = items; } - if (p + n > end) { - size *= 2; + nvm->external_objects = externals; - start = realloc(script.start, size); - if (start == NULL) { - fprintf(stderr, "alloc failed while reading '%s'\n", file); - ret = NXT_ERROR; - goto done; - } + nvm->ops = vm->ops; + + nvm->current = vm->current; - script.start = start; + nvm->external = external; - p = script.start + script.length; - end = script.start + size; + nvm->global_scope = vm->global_scope; + nvm->scope_size = vm->scope_size; + + nvm->debug = vm->debug; + + ret = njs_vm_init(nvm); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; } - memcpy(p, buf, n); + nvm->retval = njs_value_void; - p += n; - script.length += n; + return nvm; } - vm = njs_vm_create(vm_options); - if (vm == NULL) { - fprintf(stderr, "failed to create vm\n"); - ret = NXT_ERROR; - goto done; - } +fail: - ret = njs_externals_init(vm); - if (ret != NXT_OK) { - fprintf(stderr, "failed to add external protos\n"); - ret = NXT_ERROR; - goto done; - } + nxt_mem_cache_pool_destroy(nmcp); - ret = njs_process_script(vm, opts, &script, &out); - if (ret != NXT_OK) { - fprintf(stderr, "failed to get retval from VM\n"); - ret = NXT_ERROR; - goto done; - } + return NULL; +} + + +static nxt_int_t +njs_vm_init(njs_vm_t *vm) +{ + size_t size, scope_size; + u_char *values; + nxt_int_t ret; + njs_frame_t *frame; + nxt_array_t *backtrace; + + scope_size = vm->scope_size + NJS_INDEX_GLOBAL_OFFSET; + + size = NJS_GLOBAL_FRAME_SIZE + scope_size + NJS_FRAME_SPARE_SIZE; + size = nxt_align_size(size, NJS_FRAME_SPARE_SIZE); - if (!opts->disassemble) { - printf("%.*s\n", (int) out.length, out.start); + frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), size); + if (nxt_slow_path(frame == NULL)) { + return NXT_ERROR; } - ret = NXT_OK; + memset(frame, 0, NJS_GLOBAL_FRAME_SIZE); + + vm->top_frame = &frame->native; + vm->active_frame = frame; + + frame->native.size = size; + frame->native.free_size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size); + + values = (u_char *) frame + NJS_GLOBAL_FRAME_SIZE; -done: + frame->native.free = values + scope_size; + + vm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values; + memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope, + vm->scope_size); + + ret = njs_regexp_init(vm); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } - if (script.start != NULL) { - free(script.start); + ret = njs_builtin_objects_clone(vm); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; } -close_fd: + nxt_lvlhsh_init(&vm->events_hash); + nxt_queue_init(&vm->posted_events); - if (fd != STDIN_FILENO) { - close(fd); + if (vm->debug != NULL) { + backtrace = nxt_array_create(4, sizeof(njs_backtrace_entry_t), + &njs_array_mem_proto, vm->mem_cache_pool); + if (nxt_slow_path(backtrace == NULL)) { + return NXT_ERROR; + } + + vm->backtrace = backtrace; } - return ret; + vm->trace.level = NXT_LEVEL_TRACE; + vm->trace.size = 2048; + vm->trace.handler = njs_parser_trace_handler; + vm->trace.data = vm; + + return NXT_OK; } -static nxt_int_t -njs_process_script(njs_vm_t *vm, njs_opts_t *opts, const nxt_str_t *script, - nxt_str_t *out) +nxt_int_t +njs_vm_call(njs_vm_t *vm, njs_function_t *function, njs_opaque_value_t *args, + nxt_uint_t nargs) { - u_char *start; - nxt_int_t ret; + u_char *current; + njs_ret_t ret; + njs_value_t *this; - start = script->start; + static const njs_vmcode_stop_t stop[] = { + { .code = { .operation = njs_vmcode_stop, + .operands = NJS_VMCODE_1OPERAND, + .retval = NJS_VMCODE_NO_RETVAL }, + .retval = NJS_INDEX_GLOBAL_RETVAL }, + }; - ret = njs_vm_compile(vm, &start, start + script->length); + this = (njs_value_t *) &njs_value_void; - if (ret == NXT_OK) { - if (opts->disassemble) { - njs_disassembler(vm); - printf("\n"); - } + ret = njs_function_frame(vm, function, this, + (njs_value_t *) args, nargs, 0); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } - ret = njs_vm_run(vm); - if (ret == NXT_AGAIN) { - return ret; - } + current = vm->current; + vm->current = (u_char *) stop; + + ret = njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0); + if (nxt_slow_path(ret == NXT_ERROR)) { + return ret; } - if (njs_vm_retval_to_ext_string(vm, out) != NXT_OK) { - return NXT_ERROR; + ret = njs_vmcode_interpreter(vm); + + vm->current = current; + + if (ret == NJS_STOP) { + ret = NXT_OK; } - return NXT_OK; + return ret; } -static nxt_int_t -njs_editline_init(njs_vm_t *vm) +njs_vm_event_t +njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, + njs_host_event_t host_ev, njs_event_destructor destructor) { - rl_completion_append_character = '\0'; - rl_attempted_completion_function = njs_completion_handler; - rl_basic_word_break_characters = (char *) " \t\n\"\\'`@$><=;,|&{("; + njs_event_t *event; - setlocale(LC_ALL, ""); - - njs_completion.completions = njs_vm_completions(vm, NULL); - if (njs_completion.completions == NULL) { - return NXT_ERROR; + event = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_event_t)); + if (nxt_slow_path(event == NULL)) { + return NULL; } - njs_completion.vm = vm; + event->host_event = host_ev; + event->destructor = destructor; + event->function = function; + event->posted = 0; + event->nargs = 0; + event->args = NULL; - return NXT_OK; + if (njs_add_event(vm, event) != NJS_OK) { + return NULL; + } + + return event; } -static char ** -njs_completion_handler(const char *text, int start, int end) +void +njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event) { - rl_attempted_completion_over = 1; + njs_event_t *event; + + event = (njs_event_t *) vm_event; - return rl_completion_matches(text, njs_completion_generator); + njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE); } -/* editline frees the buffer every time. */ -#define njs_editline(s) strndup((char *) (s)->start, (s)->length) +nxt_int_t +njs_vm_pending(njs_vm_t *vm) +{ + return njs_is_pending_events(vm); +} -#define njs_completion(c, i) &(((nxt_str_t *) (c)->start)[i]) -static char * -njs_completion_generator(const char *text, int state) +nxt_int_t +njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event, + njs_opaque_value_t *args, nxt_uint_t nargs) { - char *completion; - size_t len; - nxt_str_t expression, *suffix; - const char *p; - njs_variable_t *var; - njs_completion_t *cmpl; + njs_event_t *event; - cmpl = &njs_completion; + event = (njs_event_t *) vm_event; - if (state == 0) { - cmpl->index = 0; - cmpl->length = strlen(text); - cmpl->phase = NJS_COMPLETION_GLOBAL; + if (nargs != 0 && !event->posted) { + event->nargs = nargs; + event->args = nxt_mem_cache_alloc(vm->mem_cache_pool, + sizeof(njs_opaque_value_t) * nargs); + if (nxt_slow_path(event->args == NULL)) { + return NJS_ERROR; + } - nxt_lvlhsh_each_init(&cmpl->lhe, &njs_variables_hash_proto); + memcpy(event->args, args, sizeof(njs_opaque_value_t) * nargs); } - if (cmpl->phase == NJS_COMPLETION_GLOBAL) { - for ( ;; ) { - if (cmpl->index >= cmpl->completions->items) { - break; - } + if (!event->posted) { + event->posted = 1; + nxt_queue_insert_tail(&vm->posted_events, &event->link); + } - suffix = njs_completion(cmpl->completions, cmpl->index++); + return NJS_OK; +} - if (suffix->start[0] == '.' || suffix->length < cmpl->length) { - continue; - } - if (strncmp(text, (char *) suffix->start, - nxt_min(suffix->length, cmpl->length)) == 0) - { - return njs_editline(suffix); - } - } +nxt_int_t +njs_vm_run(njs_vm_t *vm) +{ + nxt_str_t s; + nxt_int_t ret; - if (cmpl->vm->parser != NULL) { - for ( ;; ) { - var = nxt_lvlhsh_each(&cmpl->vm->parser->scope->variables, - &cmpl->lhe); - if (var == NULL || var->name.length < cmpl->length) { - break; - } - - if (strncmp(text, (char *) var->name.start, - nxt_min(var->name.length, cmpl->length)) == 0) - { - return njs_editline(&var->name); - } - } - } + nxt_thread_log_debug("RUN:"); - if (cmpl->length == 0) { - return NULL; - } + if (vm->backtrace != NULL) { + nxt_array_reset(vm->backtrace); + } - /* Getting the longest prefix before a '.' */ + ret = njs_vmcode_interpreter(vm); - p = &text[cmpl->length - 1]; - while (p > text && *p != '.') { p--; } + if (ret == NJS_STOP) { + ret = njs_vm_handle_events(vm); + } - if (*p != '.') { - return NULL; - } + if (nxt_slow_path(ret == NXT_AGAIN)) { + nxt_thread_log_debug("VM: AGAIN"); + return ret; + } - expression.start = (u_char *) text; - expression.length = p - text; + if (nxt_slow_path(ret != NJS_STOP)) { + nxt_thread_log_debug("VM: ERROR"); + return ret; + } - cmpl->suffix_completions = njs_vm_completions(cmpl->vm, &expression); - if (cmpl->suffix_completions == NULL) { - return NULL; + if (vm->retval.type == NJS_NUMBER) { + nxt_thread_log_debug("VM: %f", vm->retval.data.u.number); + + } else if (vm->retval.type == NJS_BOOLEAN) { + nxt_thread_log_debug("VM: boolean: %d", vm->retval.data.truth); + + } else if (vm->retval.type == NJS_STRING) { + + if (njs_vm_value_to_ext_string(vm, &s, &vm->retval, 0) == NJS_OK) { + nxt_thread_log_debug("VM: '%V'", &s); } - cmpl->index = 0; - cmpl->phase = NJS_COMPLETION_SUFFIX; - } + } else if (vm->retval.type == NJS_FUNCTION) { + nxt_thread_log_debug("VM: function"); - /* Getting the right-most suffix after a '.' */ + } else if (vm->retval.type == NJS_NULL) { + nxt_thread_log_debug("VM: null"); - len = 0; - p = &text[cmpl->length - 1]; + } else if (vm->retval.type == NJS_VOID) { + nxt_thread_log_debug("VM: void"); - while (p > text && *p != '.') { - p--; - len++; + } else { + nxt_thread_log_debug("VM: unknown: %d", vm->retval.type); } - p++; + return NJS_OK; +} + + +static nxt_int_t +njs_vm_handle_events(njs_vm_t *vm) +{ + nxt_int_t ret; + njs_event_t *ev; + nxt_queue_t *events; + nxt_queue_link_t *link; + + events = &vm->posted_events; for ( ;; ) { - if (cmpl->index >= cmpl->suffix_completions->items) { + link = nxt_queue_first(events); + + if (link == nxt_queue_tail(events)) { break; } - suffix = njs_completion(cmpl->suffix_completions, cmpl->index++); + ev = nxt_queue_link_data(link, njs_event_t, link); - if (len != 0 && strncmp((char *) suffix->start, p, - nxt_min(len, suffix->length)) != 0) - { - continue; - } + njs_del_event(vm, ev, NJS_EVENT_DELETE); - len = suffix->length + (p - text) + 1; - completion = malloc(len); - if (completion == NULL) { - return NULL; - } + ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs); - snprintf(completion, len, "%.*s%.*s", (int) (p - text), text, - (int) suffix->length, suffix->start); - return completion; + if (ret == NJS_ERROR) { + return ret; + } } - return NULL; + return njs_is_pending_events(vm) ? NJS_AGAIN : NJS_STOP; } -static njs_ret_t -njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, - njs_index_t unused) +nxt_noinline njs_value_t * +njs_vm_retval(njs_vm_t *vm) { - nxt_str_t msg; - - msg.length = 0; - msg.start = NULL; + return &vm->retval; +} - if (nargs >= 2 - && njs_vm_value_to_ext_string(vm, &msg, njs_argument(args, 1), 0) - == NJS_ERROR) - { - return NJS_ERROR; - } - - printf("%.*s\n", (int) msg.length, msg.start); +nxt_noinline void +njs_vm_retval_set(njs_vm_t *vm, njs_opaque_value_t *value) +{ + vm->retval = *(njs_value_t *) value; +} - vm->retval = njs_value_void; - return NJS_OK; +nxt_noinline void +njs_vm_memory_error(njs_vm_t *vm) +{ + njs_set_memory_error(vm, &vm->retval); } -static njs_ret_t -njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, - njs_index_t unused) +njs_ret_t njs_vm_retval_to_ext_string(njs_vm_t *vm, nxt_str_t *retval) { - nxt_uint_t i; + if (vm->top_frame == NULL) { + /* An exception was thrown during compilation. */ - printf("VM built-in objects:\n"); - for (i = NJS_CONSTRUCTOR_OBJECT; i < NJS_CONSTRUCTOR_MAX; i++) { - printf(" %.*s\n", (int) njs_constructor_init[i]->name.length, - njs_constructor_init[i]->name.start); + njs_vm_init(vm); } - for (i = NJS_OBJECT_THIS; i < NJS_OBJECT_MAX; i++) { - if (njs_object_init[i] != NULL) { - printf(" %.*s\n", (int) njs_object_init[i]->name.length, - njs_object_init[i]->name.start); - } + return njs_vm_value_to_ext_string(vm, retval, &vm->retval, 1); +} + + +njs_value_t * +njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, const nxt_str_t *key) +{ + nxt_int_t ret; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + if (nxt_slow_path(!njs_is_object(value))) { + return NULL; } - printf("\nEmbedded objects:\n"); - printf(" console\n"); + lhq.key = *key; + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.proto = &njs_object_hash_proto; - printf("\n"); + ret = nxt_lvlhsh_find(&value->data.u.object->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } - vm->retval = njs_value_void; + prop = lhq.value; - return NJS_OK; + return &prop->value; } diff --git a/njs/njscript.h b/njs/njs.h similarity index 98% rename from njs/njscript.h rename to njs/njs.h index b5ed023a..4731bf13 100644 --- a/njs/njscript.h +++ b/njs/njs.h @@ -3,11 +3,11 @@ * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. * - * njsScript public header. + * njs public header. */ -#ifndef _NJSCRIPT_H_INCLUDED_ -#define _NJSCRIPT_H_INCLUDED_ +#ifndef _NJS_H_INCLUDED_ +#define _NJS_H_INCLUDED_ #include @@ -220,4 +220,4 @@ NXT_EXPORT njs_value_t *njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, extern const nxt_mem_proto_t njs_vm_mem_cache_pool_proto; -#endif /* _NJSCRIPT_H_INCLUDED_ */ +#endif /* _NJS_H_INCLUDED_ */ diff --git a/njs/njs_core.h b/njs/njs_core.h index 82e33691..e288ef00 100644 --- a/njs/njs_core.h +++ b/njs/njs_core.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include diff --git a/njs/njs_shell.c b/njs/njs_shell.c new file mode 100644 index 00000000..c83b1098 --- /dev/null +++ b/njs/njs_shell.c @@ -0,0 +1,651 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +typedef enum { + NJS_COMPLETION_GLOBAL = 0, + NJS_COMPLETION_SUFFIX, +} njs_completion_phase_t; + + +typedef struct { + char *file; + nxt_int_t version; + nxt_int_t disassemble; + nxt_int_t interactive; +} njs_opts_t; + + +typedef struct { + size_t index; + size_t length; + njs_vm_t *vm; + nxt_array_t *completions; + nxt_array_t *suffix_completions; + nxt_lvlhsh_each_t lhe; + njs_completion_phase_t phase; +} njs_completion_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_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, + const nxt_str_t *script, nxt_str_t *out); +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); + +static njs_ret_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); + + +static njs_external_t njs_ext_console[] = { + + { nxt_string("log"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_log, + 0 }, + + { nxt_string("help"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + njs_ext_console_help, + 0 }, +}; + +static njs_external_t njs_externals[] = { + + { nxt_string("console"), + NJS_EXTERN_OBJECT, + njs_ext_console, + nxt_nitems(njs_ext_console), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, +}; + + +static njs_completion_t njs_completion; + + +int +main(int argc, char **argv) +{ + nxt_int_t ret; + njs_opts_t opts; + njs_vm_opt_t vm_options; + + memset(&opts, 0, sizeof(njs_opts_t)); + opts.interactive = 1; + + ret = njs_get_options(&opts, argc, argv); + if (ret != NXT_OK) { + return (ret == NXT_DONE) ? EXIT_SUCCESS : EXIT_FAILURE; + } + + if (opts.version != 0) { + printf("%s\n", NJS_VERSION); + return EXIT_SUCCESS; + } + + memset(&vm_options, 0, sizeof(njs_vm_opt_t)); + + vm_options.accumulative = 1; + vm_options.backtrace = 1; + + if (opts.interactive) { + ret = njs_interactive_shell(&opts, &vm_options); + + } else { + ret = njs_process_file(&opts, &vm_options); + } + + return (ret == NXT_OK) ? EXIT_SUCCESS : EXIT_FAILURE; +} + + +static nxt_int_t +njs_get_options(njs_opts_t *opts, int argc, char** argv) +{ + char *p; + nxt_int_t i, ret; + + ret = NXT_DONE; + + for (i = 1; i < argc; i++) { + + p = argv[i]; + + if (p[0] != '-' || (p[0] == '-' && p[1] == '\0')) { + opts->interactive = 0; + opts->file = argv[i]; + continue; + } + + p++; + + switch (*p) { + case 'd': + opts->disassemble = 1; + break; + + case 'V': + opts->version = 1; + break; + + default: + fprintf(stderr, "Unknown argument: \"%s\"\n", argv[i]); + ret = NXT_ERROR; + + /* Fall through. */ + + case 'h': + case '?': + printf("Usage: %s [|-] [-dV]\n", argv[0]); + return ret; + } + } + + return NXT_OK; +} + + +static nxt_int_t +njs_externals_init(njs_vm_t *vm) +{ + nxt_uint_t ret; + const njs_extern_t *proto; + njs_opaque_value_t *value; + + static const nxt_str_t name = nxt_string_value("console"); + + proto = njs_vm_external_prototype(vm, &njs_externals[0]); + if (proto == NULL) { + fprintf(stderr, "failed to add console proto\n"); + return NXT_ERROR; + } + + value = nxt_mem_cache_zalloc(vm->mem_cache_pool, + sizeof(njs_opaque_value_t)); + if (value == NULL) { + return NXT_ERROR; + } + + ret = njs_vm_external_create(vm, value, proto, NULL); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + ret = njs_vm_external_bind(vm, &name, value); + if (ret != NXT_OK) { + return NXT_ERROR; + } + + return NXT_OK; +} + + +static nxt_int_t +njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) +{ + njs_vm_t *vm; + nxt_int_t ret; + nxt_str_t line, out; + + 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) { + 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; + } + + printf("interactive njscript %s\n\n", NJS_VERSION); + + printf("v. -> the properties and prototype methods of v.\n"); + printf("type console.help() for more information\n\n"); + + for ( ;; ) { + line.start = (u_char *) readline(">> "); + if (line.start == NULL) { + break; + } + + line.length = strlen((char *) line.start); + if (line.length == 0) { + continue; + } + + add_history((char *) line.start); + + ret = njs_process_script(vm, opts, &line, &out); + if (ret != NXT_OK) { + printf("shell: failed to get retval from VM\n"); + continue; + } + + printf("%.*s\n", (int) out.length, out.start); + + /* editline allocs a new buffer every time. */ + free(line.start); + } + + return NXT_OK; +} + + +static nxt_int_t +njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options) +{ + int fd; + char *file; + u_char buf[4096], *p, *end, *start; + size_t size; + ssize_t n; + njs_vm_t *vm; + nxt_int_t ret; + nxt_str_t out, script; + struct stat sb; + + file = opts->file; + + if (file[0] == '-' && file[1] == '\0') { + fd = STDIN_FILENO; + + } else { + fd = open(file, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "failed to open file: '%s' (%s)\n", + file, strerror(errno)); + return NXT_ERROR; + } + } + + if (fstat(fd, &sb) == -1) { + fprintf(stderr, "fstat(%d) failed while reading '%s' (%s)\n", + fd, file, strerror(errno)); + ret = NXT_ERROR; + goto close_fd; + } + + size = sizeof(buf); + + if (S_ISREG(sb.st_mode) && sb.st_size) { + size = sb.st_size; + } + + script.length = 0; + script.start = realloc(NULL, size); + if (script.start == NULL) { + fprintf(stderr, "alloc failed while reading '%s'\n", file); + ret = NXT_ERROR; + goto done; + } + + p = script.start; + end = p + size; + + for ( ;; ) { + n = read(fd, buf, sizeof(buf)); + + if (n == 0) { + break; + } + + if (n < 0) { + fprintf(stderr, "failed to read file: '%s' (%s)\n", + file, strerror(errno)); + ret = NXT_ERROR; + goto done; + } + + if (p + n > end) { + size *= 2; + + start = realloc(script.start, size); + if (start == NULL) { + fprintf(stderr, "alloc failed while reading '%s'\n", file); + ret = NXT_ERROR; + goto done; + } + + script.start = start; + + p = script.start + script.length; + end = script.start + size; + } + + memcpy(p, buf, n); + + p += n; + script.length += n; + } + + vm = njs_vm_create(vm_options); + if (vm == NULL) { + fprintf(stderr, "failed to create vm\n"); + ret = NXT_ERROR; + goto done; + } + + ret = njs_externals_init(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, &out); + if (ret != NXT_OK) { + fprintf(stderr, "failed to get retval from VM\n"); + ret = NXT_ERROR; + goto done; + } + + if (!opts->disassemble) { + printf("%.*s\n", (int) out.length, out.start); + } + + ret = NXT_OK; + +done: + + if (script.start != NULL) { + free(script.start); + } + +close_fd: + + if (fd != STDIN_FILENO) { + close(fd); + } + + return ret; +} + + +static nxt_int_t +njs_process_script(njs_vm_t *vm, njs_opts_t *opts, const nxt_str_t *script, + nxt_str_t *out) +{ + u_char *start; + nxt_int_t ret; + + start = script->start; + + ret = njs_vm_compile(vm, &start, start + script->length); + + if (ret == NXT_OK) { + if (opts->disassemble) { + njs_disassembler(vm); + printf("\n"); + } + + ret = njs_vm_run(vm); + if (ret == NXT_AGAIN) { + return ret; + } + } + + if (njs_vm_retval_to_ext_string(vm, out) != NXT_OK) { + return NXT_ERROR; + } + + return NXT_OK; +} + + +static nxt_int_t +njs_editline_init(njs_vm_t *vm) +{ + rl_completion_append_character = '\0'; + rl_attempted_completion_function = njs_completion_handler; + rl_basic_word_break_characters = (char *) " \t\n\"\\'`@$><=;,|&{("; + + 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; +} + + +static char ** +njs_completion_handler(const char *text, int start, int end) +{ + rl_attempted_completion_over = 1; + + return rl_completion_matches(text, njs_completion_generator); +} + + +/* editline frees the buffer every time. */ +#define njs_editline(s) strndup((char *) (s)->start, (s)->length) + +#define njs_completion(c, i) &(((nxt_str_t *) (c)->start)[i]) + +static char * +njs_completion_generator(const char *text, int state) +{ + char *completion; + size_t len; + nxt_str_t expression, *suffix; + const char *p; + njs_variable_t *var; + njs_completion_t *cmpl; + + cmpl = &njs_completion; + + if (state == 0) { + cmpl->index = 0; + cmpl->length = strlen(text); + cmpl->phase = NJS_COMPLETION_GLOBAL; + + nxt_lvlhsh_each_init(&cmpl->lhe, &njs_variables_hash_proto); + } + + if (cmpl->phase == NJS_COMPLETION_GLOBAL) { + for ( ;; ) { + if (cmpl->index >= cmpl->completions->items) { + break; + } + + suffix = njs_completion(cmpl->completions, cmpl->index++); + + if (suffix->start[0] == '.' || suffix->length < cmpl->length) { + continue; + } + + if (strncmp(text, (char *) suffix->start, + nxt_min(suffix->length, cmpl->length)) == 0) + { + return njs_editline(suffix); + } + } + + if (cmpl->vm->parser != NULL) { + for ( ;; ) { + var = nxt_lvlhsh_each(&cmpl->vm->parser->scope->variables, + &cmpl->lhe); + if (var == NULL || var->name.length < cmpl->length) { + break; + } + + if (strncmp(text, (char *) var->name.start, + nxt_min(var->name.length, cmpl->length)) == 0) + { + return njs_editline(&var->name); + } + } + } + + if (cmpl->length == 0) { + return NULL; + } + + /* Getting the longest prefix before a '.' */ + + p = &text[cmpl->length - 1]; + while (p > text && *p != '.') { p--; } + + if (*p != '.') { + return NULL; + } + + expression.start = (u_char *) text; + expression.length = p - text; + + cmpl->suffix_completions = njs_vm_completions(cmpl->vm, &expression); + if (cmpl->suffix_completions == NULL) { + return NULL; + } + + cmpl->index = 0; + cmpl->phase = NJS_COMPLETION_SUFFIX; + } + + /* Getting the right-most suffix after a '.' */ + + len = 0; + p = &text[cmpl->length - 1]; + + while (p > text && *p != '.') { + p--; + len++; + } + + p++; + + for ( ;; ) { + if (cmpl->index >= cmpl->suffix_completions->items) { + break; + } + + suffix = njs_completion(cmpl->suffix_completions, cmpl->index++); + + if (len != 0 && strncmp((char *) suffix->start, p, + nxt_min(len, suffix->length)) != 0) + { + continue; + } + + len = suffix->length + (p - text) + 1; + completion = malloc(len); + if (completion == NULL) { + return NULL; + } + + snprintf(completion, len, "%.*s%.*s", (int) (p - text), text, + (int) suffix->length, suffix->start); + return completion; + } + + return NULL; +} + + +static njs_ret_t +njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_str_t msg; + + msg.length = 0; + msg.start = NULL; + + if (nargs >= 2 + && njs_vm_value_to_ext_string(vm, &msg, njs_argument(args, 1), 0) + == NJS_ERROR) + { + + return NJS_ERROR; + } + + printf("%.*s\n", (int) msg.length, msg.start); + + vm->retval = njs_value_void; + + return NJS_OK; +} + + +static njs_ret_t +njs_ext_console_help(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_uint_t i; + + printf("VM built-in objects:\n"); + for (i = NJS_CONSTRUCTOR_OBJECT; i < NJS_CONSTRUCTOR_MAX; i++) { + printf(" %.*s\n", (int) njs_constructor_init[i]->name.length, + njs_constructor_init[i]->name.start); + } + + for (i = NJS_OBJECT_THIS; i < NJS_OBJECT_MAX; i++) { + if (njs_object_init[i] != NULL) { + printf(" %.*s\n", (int) njs_object_init[i]->name.length, + njs_object_init[i]->name.start); + } + } + + printf("\nEmbedded objects:\n"); + printf(" console\n"); + + printf("\n"); + + vm->retval = njs_value_void; + + return NJS_OK; +} diff --git a/njs/test/njs_benchmark.c b/njs/test/njs_benchmark.c index 4aed0567..45c625a4 100644 --- a/njs/test/njs_benchmark.c +++ b/njs/test/njs_benchmark.c @@ -4,7 +4,7 @@ * Copyright (C) NGINX, Inc. */ -#include +#include #include #include #include diff --git a/njs/test/njs_interactive_test.c b/njs/test/njs_interactive_test.c index 3b3e1dfe..ae98c09f 100644 --- a/njs/test/njs_interactive_test.c +++ b/njs/test/njs_interactive_test.c @@ -4,7 +4,7 @@ * Copyright (C) NGINX, Inc. */ -#include +#include #include #include #include -- 2.47.3