[njs] Interactive shell.

Dmitry Volyntsev xeioex at nginx.com
Mon Jul 17 18:01:34 UTC 2017


details:   http://hg.nginx.org/njs/rev/f2ecbe1c2261
branches:  
changeset: 388:f2ecbe1c2261
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Jul 17 20:38:00 2017 +0300
description:
Interactive shell.

diffstat:

 Makefile                        |   26 ++
 njs/njs.c                       |  466 ++++++++++++++++++++++++++++++++++++++++
 njs/njs_array.c                 |    2 +
 njs/njs_boolean.c               |    2 +
 njs/njs_builtin.c               |  235 +++++++++++++++++--
 njs/njs_date.c                  |    2 +
 njs/njs_function.c              |    3 +
 njs/njs_lexer_keyword.c         |    9 +-
 njs/njs_math.c                  |    1 +
 njs/njs_number.c                |    2 +
 njs/njs_object.c                |    2 +
 njs/njs_object.h                |    1 +
 njs/njs_parser.c                |   45 +++-
 njs/njs_parser.h                |   13 +-
 njs/njs_regexp.c                |    2 +
 njs/njs_string.c                |    2 +
 njs/njs_variable.c              |   47 ++-
 njs/njs_vm.h                    |    1 +
 njs/njscript.c                  |   43 +++-
 njs/njscript.h                  |    7 +-
 njs/test/njs_interactive_test.c |  197 ++++++++++++++++
 nxt/auto/configure              |    1 +
 nxt/auto/editline               |   25 ++
 23 files changed, 1071 insertions(+), 63 deletions(-)

diffs (truncated from 1569 to 1000 lines):

diff -r 182d765687ee -r f2ecbe1c2261 Makefile
--- a/Makefile	Mon Jul 17 15:29:02 2017 +0300
+++ b/Makefile	Mon Jul 17 20:38:00 2017 +0300
@@ -74,11 +74,15 @@ NXT_BUILDDIR =	build
 
 all:	test lib_test
 
+njs:   $(NXT_BUILDDIR)/njs
+
 test:	\
 	$(NXT_BUILDDIR)/njs_unit_test \
+	$(NXT_BUILDDIR)/njs_interactive_test \
 	$(NXT_BUILDDIR)/njs_benchmark \
 
 	$(NXT_BUILDDIR)/njs_unit_test d
+	$(NXT_BUILDDIR)/njs_interactive_test
 
 clean:
 	rm -rf $(NXT_BUILDDIR)
@@ -386,6 +390,17 @@ dist:
 		-I$(NXT_LIB) -Injs \
 		njs/njs_disassembler.c
 
+$(NXT_BUILDDIR)/njs: \
+	$(NXT_BUILDDIR)/libnxt.a \
+	$(NXT_BUILDDIR)/libnjs.a \
+	njs/njs.c \
+
+	$(NXT_CC) -o $(NXT_BUILDDIR)/njs $(NXT_CFLAGS) \
+		-I$(NXT_LIB) -Injs \
+		njs/njs.c \
+		$(NXT_BUILDDIR)/libnjs.a \
+		-lm $(NXT_PCRE_LIB) $(NXT_EDITLINE_LIB)
+
 $(NXT_BUILDDIR)/njs_unit_test: \
 	$(NXT_BUILDDIR)/libnxt.a \
 	$(NXT_BUILDDIR)/libnjs.a \
@@ -397,6 +412,17 @@ dist:
 		$(NXT_BUILDDIR)/libnjs.a \
 		-lm $(NXT_PCRE_LIB)
 
+$(NXT_BUILDDIR)/njs_interactive_test: \
+	$(NXT_BUILDDIR)/libnxt.a \
+	$(NXT_BUILDDIR)/libnjs.a \
+	njs/test/njs_interactive_test.c \
+
+	$(NXT_CC) -o $(NXT_BUILDDIR)/njs_interactive_test $(NXT_CFLAGS) \
+		-I$(NXT_LIB) -Injs \
+		njs/test/njs_interactive_test.c \
+		$(NXT_BUILDDIR)/libnjs.a \
+		-lm $(NXT_PCRE_LIB)
+
 $(NXT_BUILDDIR)/njs_benchmark: \
 	$(NXT_BUILDDIR)/libnxt.a \
 	$(NXT_BUILDDIR)/libnjs.a \
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs.c	Mon Jul 17 20:38:00 2017 +0300
@@ -0,0 +1,466 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_malloc.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+
+#include <editline/readline.h>
+
+
+typedef enum {
+    NJS_COMPLETION_GLOBAL = 0,
+    NJS_COMPLETION_SUFFIX,
+} njs_completion_phase_t;
+
+
+typedef struct {
+    char                    *file;
+    nxt_int_t               disassemble;
+    nxt_int_t               interactive;
+} njs_opts_t;
+
+
+typedef struct {
+    size_t                  index;
+    size_t                  length;
+    njs_vm_t                *vm;
+    const char              **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_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_completion_t  njs_completion;
+
+
+int
+main(int argc, char **argv)
+{
+    nxt_int_t             ret;
+    njs_opts_t            opts;
+    njs_vm_opt_t          vm_options;
+    nxt_mem_cache_pool_t  *mcp;
+
+    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;
+    }
+
+    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 EXIT_FAILURE;
+    }
+
+    memset(&vm_options, 0, sizeof(njs_vm_opt_t));
+
+    vm_options.mcp = mcp;
+    vm_options.accumulative = 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;
+
+        default:
+            fprintf(stderr, "Unknown argument: \"%s\"\n", argv[i]);
+            ret = NXT_ERROR;
+
+            /* Fall through. */
+
+        case 'h':
+        case '?':
+            printf("Usage: %s [<file>|-] [-d]\n", argv[0]);
+            return ret;
+        }
+    }
+
+    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_editline_init(vm) != NXT_OK) {
+        fprintf(stderr, "failed to init completions\n");
+        return NXT_ERROR;
+    }
+
+    printf("interactive njscript\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("njs_process_script() failed\n");
+            return NXT_ERROR;
+        }
+
+        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;
+    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;
+        }
+    }
+
+    fstat(fd, &sb);
+
+    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);
+        return NXT_ERROR;
+    }
+
+    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));
+            return NXT_ERROR;
+        }
+
+        if (p + n > end) {
+            size *= 2;
+
+            script.start = realloc(script.start, size);
+            if (script.start == NULL) {
+                fprintf(stderr, "alloc failed while reading '%s'\n", file);
+                return NXT_ERROR;
+            }
+
+            p = script.start;
+            end = p + 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");
+        return NXT_ERROR;
+    }
+
+    ret = njs_process_script(vm, opts, &script, &out);
+    if (ret != NXT_OK) {
+        fprintf(stderr, "njs_process_script() failed\n");
+        return NXT_ERROR;
+    }
+
+    if (!opts->disassemble) {
+        printf("%.*s\n", (int) out.length, out.start);
+    }
+
+    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)
+{
+    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_OK) {
+            if (njs_vm_retval(vm, out) != NXT_OK) {
+                return NXT_ERROR;
+            }
+
+        } else {
+            njs_vm_exception(vm, out);
+        }
+
+    } else {
+        njs_vm_exception(vm, out);
+    }
+
+    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\"\\'`@$><=;,|&{(";
+
+    njs_completion.completions = njs_vm_completions(vm);
+    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);
+}
+
+
+static char *
+njs_completion_generator(const char *text, int state)
+{
+    char              *completion;
+    size_t            len;
+    const char        *name, *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 ( ;; ) {
+            name = cmpl->completions[cmpl->index];
+            if (name == NULL) {
+                break;
+            }
+
+            cmpl->index++;
+
+            if (name[0] == '.') {
+                continue;
+            }
+
+            if (strncmp(name, text, cmpl->length) == 0) {
+                /* editline frees the buffer every time. */
+                return strdup(name);
+            }
+        }
+
+        if (cmpl->vm->parser != NULL) {
+            for ( ;; ) {
+                var = nxt_lvlhsh_each(&cmpl->vm->parser->scope->variables,
+                                      &cmpl->lhe);
+                if (var == NULL) {
+                    break;
+                }
+
+                if (strncmp((char *) var->name.start, text, cmpl->length)
+                    == 0)
+                {
+                    completion = malloc(var->name.length + 1);
+                    if (completion == NULL) {
+                        return NULL;
+                    }
+
+                    memcpy(completion, var->name.start, var->name.length);
+                    completion[var->name.length] = '\0';
+
+                    return completion;
+                }
+            }
+        }
+
+        if (cmpl->length == 0) {
+            return NULL;
+        }
+
+        cmpl->index = 0;
+        cmpl->phase = NJS_COMPLETION_SUFFIX;
+    }
+
+    len = 1;
+    p = &text[cmpl->length - 1];
+
+    while (p > text && *p != '.') {
+        p--;
+        len++;
+    }
+
+    if (*p != '.') {
+        return NULL;
+    }
+
+    for ( ;; ) {
+        name = cmpl->completions[cmpl->index++];
+        if (name == NULL) {
+            break;
+        }
+
+        if (name[0] != '.') {
+            continue;
+        }
+
+        if (strncmp(name, p, len) != 0) {
+            continue;
+        }
+
+        len = strlen(name) + (p - text) + 2;
+        completion = malloc(len);
+        if (completion == NULL) {
+            return NULL;
+        }
+
+        snprintf(completion, len, "%.*s%s", (int) (p - text), text, name);
+        return completion;
+    }
+
+    return NULL;
+}
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_array.c
--- a/njs/njs_array.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_array.c	Mon Jul 17 20:38:00 2017 +0300
@@ -373,6 +373,7 @@ static const njs_object_prop_t  njs_arra
 
 
 const njs_object_init_t  njs_array_constructor_init = {
+    nxt_string("Array"),
     njs_array_constructor_properties,
     nxt_nitems(njs_array_constructor_properties),
 };
@@ -2234,6 +2235,7 @@ static const njs_object_prop_t  njs_arra
 
 
 const njs_object_init_t  njs_array_prototype_init = {
+    nxt_string("Array"),
     njs_array_prototype_properties,
     nxt_nitems(njs_array_prototype_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_boolean.c
--- a/njs/njs_boolean.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_boolean.c	Mon Jul 17 20:38:00 2017 +0300
@@ -78,6 +78,7 @@ static const njs_object_prop_t  njs_bool
 
 
 const njs_object_init_t  njs_boolean_constructor_init = {
+    nxt_string("Boolean"),
     njs_boolean_constructor_properties,
     nxt_nitems(njs_boolean_constructor_properties),
 };
@@ -156,6 +157,7 @@ static const njs_object_prop_t  njs_bool
 
 
 const njs_object_init_t  njs_boolean_prototype_init = {
+    nxt_string("Boolean"),
     njs_boolean_prototype_properties,
     nxt_nitems(njs_boolean_prototype_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_builtin.c
--- a/njs/njs_builtin.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_builtin.c	Mon Jul 17 20:38:00 2017 +0300
@@ -27,6 +27,7 @@
 #include <njs_date.h>
 #include <njs_math.h>
 #include <string.h>
+#include <stdio.h>
 
 
 typedef struct {
@@ -35,6 +36,40 @@ typedef struct {
 } njs_function_init_t;
 
 
+static nxt_int_t njs_builtin_completions(njs_vm_t *vm, size_t *size,
+    const char **completions);
+
+
+static const njs_object_init_t    *object_init[] = {
+    NULL,                         /* global this        */
+    &njs_math_object_init,        /* Math               */
+};
+
+
+static const njs_object_init_t  *prototype_init[] = {
+    &njs_object_prototype_init,
+    &njs_array_prototype_init,
+    &njs_boolean_prototype_init,
+    &njs_number_prototype_init,
+    &njs_string_prototype_init,
+    &njs_function_prototype_init,
+    &njs_regexp_prototype_init,
+    &njs_date_prototype_init,
+};
+
+
+static const njs_object_init_t    *constructor_init[] = {
+    &njs_object_constructor_init,
+    &njs_array_constructor_init,
+    &njs_boolean_constructor_init,
+    &njs_number_constructor_init,
+    &njs_string_constructor_init,
+    &njs_function_constructor_init,
+    &njs_regexp_constructor_init,
+    &njs_date_constructor_init,
+};
+
+
 static njs_ret_t
 njs_prototype_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
@@ -54,17 +89,6 @@ njs_builtin_objects_create(njs_vm_t *vm)
     njs_function_t          *functions, *constructors;
     njs_object_prototype_t  *prototypes;
 
-    static const njs_object_init_t  *prototype_init[] = {
-        &njs_object_prototype_init,
-        &njs_array_prototype_init,
-        &njs_boolean_prototype_init,
-        &njs_number_prototype_init,
-        &njs_string_prototype_init,
-        &njs_function_prototype_init,
-        &njs_regexp_prototype_init,
-        &njs_date_prototype_init,
-    };
-
     static const njs_object_prototype_t  prototype_values[] = {
         /*
          * GCC 4 complains about uninitialized .shared field,
@@ -97,17 +121,6 @@ njs_builtin_objects_create(njs_vm_t *vm)
                             .object = { .type = NJS_DATE } } },
     };
 
-    static const njs_object_init_t    *constructor_init[] = {
-        &njs_object_constructor_init,
-        &njs_array_constructor_init,
-        &njs_boolean_constructor_init,
-        &njs_number_constructor_init,
-        &njs_string_constructor_init,
-        &njs_function_constructor_init,
-        &njs_regexp_constructor_init,
-        &njs_date_constructor_init,
-    };
-
     static const njs_function_init_t  native_constructors[] = {
         /* SunC does not allow empty array initialization. */
         { njs_object_constructor,     { 0 } },
@@ -121,11 +134,6 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { njs_date_constructor,       { 0 } },
     };
 
-    static const njs_object_init_t    *object_init[] = {
-        NULL,                         /* global this        */
-        &njs_math_object_init,        /* Math               */
-    };
-
     static const njs_object_init_t    *function_init[] = {
         &njs_eval_function_init,      /* eval               */
         NULL,                         /* toString           */
@@ -333,3 +341,176 @@ njs_builtin_objects_clone(njs_vm_t *vm)
 
     return NXT_OK;
 }
+
+
+const char **
+njs_vm_completions(njs_vm_t *vm)
+{
+    size_t      size;
+    const char  **completions;
+
+    if (njs_builtin_completions(vm, &size, NULL) != NXT_OK) {
+        return NULL;
+    }
+
+    completions = nxt_mem_cache_zalloc(vm->mem_cache_pool,
+                                       sizeof(char *) * (size + 1));
+
+    if (completions == NULL) {
+        return NULL;
+    }
+
+    if (njs_builtin_completions(vm, NULL, completions) != NXT_OK) {
+        return NULL;
+    }
+
+    return completions;
+}
+
+
+static nxt_int_t
+njs_builtin_completions(njs_vm_t *vm, size_t *size, const char **completions)
+{
+    char                    *compl;
+    size_t                  n, len;
+    nxt_str_t               string;
+    nxt_uint_t              i, k;
+    njs_object_t            *objects;
+    njs_keyword_t           *keyword;
+    njs_function_t          *constructors;
+    njs_object_prop_t       *prop;
+    nxt_lvlhsh_each_t       lhe;
+    njs_object_prototype_t  *prototypes;
+
+    n = 0;
+
+    nxt_lvlhsh_each_init(&lhe, &njs_keyword_hash_proto);
+
+    for ( ;; ) {
+        keyword = nxt_lvlhsh_each(&vm->shared->keywords_hash, &lhe);
+
+        if (keyword == NULL) {
+            break;
+        }
+
+        if (completions != NULL) {
+            completions[n++] = (char *) keyword->name.start;
+
+        } else {
+            n++;
+        }
+    }
+
+    objects = vm->shared->objects;
+
+    for (i = NJS_OBJECT_THIS; i < NJS_OBJECT_MAX; i++) {
+        if (object_init[i] == NULL) {
+            continue;
+        }
+
+        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&objects[i].shared_hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            if (completions != NULL) {
+                njs_string_get(&prop->name, &string);
+                len = object_init[i]->name.length + string.length + 2;
+
+                compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (compl == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(compl, len, "%s.%s", object_init[i]->name.start,
+                         string.start);
+
+                completions[n++] = (char *) compl;
+
+            } else {
+                n++;
+            }
+        }
+    }
+
+    prototypes = vm->shared->prototypes;
+
+    for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) {
+        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&prototypes[i].object.shared_hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            if (completions != NULL) {
+                njs_string_get(&prop->name, &string);
+                len = string.length + 2;
+
+                compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (compl == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(compl, len, ".%s", string.start);
+
+                for (k = 0; k < n; k++) {
+                    if (strncmp(completions[k], compl, len) == 0) {
+                        break;
+                    }
+                }
+
+                if (k == n) {
+                    completions[n++] = (char *) compl;
+                }
+
+            } else {
+                n++;
+            }
+        }
+    }
+
+    constructors = vm->shared->constructors;
+
+    for (i = NJS_CONSTRUCTOR_OBJECT; i < NJS_CONSTRUCTOR_MAX; i++) {
+        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&constructors[i].object.shared_hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            if (completions != NULL) {
+                njs_string_get(&prop->name, &string);
+                len = constructor_init[i]->name.length + string.length + 2;
+
+                compl = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (compl == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(compl, len, "%s.%s", constructor_init[i]->name.start,
+                         string.start);
+
+                completions[n++] = (char *) compl;
+
+            } else {
+                n++;
+            }
+        }
+    }
+
+    if (size) {
+        *size = n;
+    }
+
+    return NXT_OK;
+}
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_date.c
--- a/njs/njs_date.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_date.c	Mon Jul 17 20:38:00 2017 +0300
@@ -931,6 +931,7 @@ static const njs_object_prop_t  njs_date
 
 
 const njs_object_init_t  njs_date_constructor_init = {
+    nxt_string("Date"),
     njs_date_constructor_properties,
     nxt_nitems(njs_date_constructor_properties),
 };
@@ -2244,6 +2245,7 @@ static const njs_object_prop_t  njs_date
 
 
 const njs_object_init_t  njs_date_prototype_init = {
+    nxt_string("Date"),
     njs_date_prototype_properties,
     nxt_nitems(njs_date_prototype_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_function.c
--- a/njs/njs_function.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_function.c	Mon Jul 17 20:38:00 2017 +0300
@@ -497,6 +497,7 @@ static const njs_object_prop_t  njs_func
 
 
 const njs_object_init_t  njs_function_constructor_init = {
+    nxt_string("Function"),
     njs_function_constructor_properties,
     nxt_nitems(njs_function_constructor_properties),
 };
@@ -692,6 +693,7 @@ static const njs_object_prop_t  njs_func
 
 
 const njs_object_init_t  njs_function_prototype_init = {
+    nxt_string("Function"),
     njs_function_prototype_properties,
     nxt_nitems(njs_function_prototype_properties),
 };
@@ -724,6 +726,7 @@ static const njs_object_prop_t  njs_eval
 
 
 const njs_object_init_t  njs_eval_function_init = {
+    nxt_string("Function"),
     njs_eval_function_properties,
     nxt_nitems(njs_eval_function_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_lexer_keyword.c
--- a/njs/njs_lexer_keyword.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_lexer_keyword.c	Mon Jul 17 20:38:00 2017 +0300
@@ -23,13 +23,6 @@
 #include <string.h>
 
 
-typedef struct {
-    nxt_str_t        name;
-    njs_token_t      token;
-    double           number;
-} njs_keyword_t;
-
-
 static const njs_keyword_t  njs_keywords[] = {
 
     /* Values. */
@@ -150,7 +143,7 @@ njs_keyword_hash_test(nxt_lvlhsh_query_t
 }
 
 
-static const nxt_lvlhsh_proto_t  njs_keyword_hash_proto
+const nxt_lvlhsh_proto_t  njs_keyword_hash_proto
     nxt_aligned(64) =
 {
     NXT_LVLHSH_DEFAULT,
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_math.c
--- a/njs/njs_math.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_math.c	Mon Jul 17 20:38:00 2017 +0300
@@ -1088,6 +1088,7 @@ static const njs_object_prop_t  njs_math
 
 
 const njs_object_init_t  njs_math_object_init = {
+    nxt_string("Math"),
     njs_math_object_properties,
     nxt_nitems(njs_math_object_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_number.c
--- a/njs/njs_number.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_number.c	Mon Jul 17 20:38:00 2017 +0300
@@ -528,6 +528,7 @@ static const njs_object_prop_t  njs_numb
 
 
 const njs_object_init_t  njs_number_constructor_init = {
+    nxt_string("Number"),
     njs_number_constructor_properties,
     nxt_nitems(njs_number_constructor_properties),
 };
@@ -687,6 +688,7 @@ static const njs_object_prop_t  njs_numb
 
 
 const njs_object_init_t  njs_number_prototype_init = {
+    nxt_string("Number"),
     njs_number_prototype_properties,
     nxt_nitems(njs_number_prototype_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_object.c
--- a/njs/njs_object.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_object.c	Mon Jul 17 20:38:00 2017 +0300
@@ -1153,6 +1153,7 @@ static const njs_object_prop_t  njs_obje
 
 
 const njs_object_init_t  njs_object_constructor_init = {
+    nxt_string("Object"),
     njs_object_constructor_properties,
     nxt_nitems(njs_object_constructor_properties),
 };
@@ -1477,6 +1478,7 @@ static const njs_object_prop_t  njs_obje
 
 
 const njs_object_init_t  njs_object_prototype_init = {
+    nxt_string("Object"),
     njs_object_prototype_properties,
     nxt_nitems(njs_object_prototype_properties),
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_object.h
--- a/njs/njs_object.h	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_object.h	Mon Jul 17 20:38:00 2017 +0300
@@ -32,6 +32,7 @@ typedef struct {
 
 
 struct njs_object_init_s {
+    nxt_str_t                   name;
     const njs_object_prop_t     *properties;
     nxt_uint_t                  items;
 };
diff -r 182d765687ee -r f2ecbe1c2261 njs/njs_parser.c
--- a/njs/njs_parser.c	Mon Jul 17 15:29:02 2017 +0300
+++ b/njs/njs_parser.c	Mon Jul 17 20:38:00 2017 +0300
@@ -14,6 +14,7 @@
 #include <nxt_lvlhsh.h>
 #include <nxt_random.h>
 #include <nxt_mem_cache_pool.h>
+#include <nxt_djb_hash.h>
 #include <njscript.h>
 #include <njs_vm.h>
 #include <njs_number.h>
@@ -95,17 +96,53 @@ static njs_token_t njs_parser_unexpected
 
 
 njs_parser_node_t *
-njs_parser(njs_vm_t *vm, njs_parser_t *parser)
+njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_parser_t *prev)
 {
-    njs_ret_t          ret;
-    njs_token_t        token;
-    njs_parser_node_t  *node;
+    njs_ret_t           ret;
+    njs_token_t         token;
+    nxt_lvlhsh_t        *variables, *prev_variables;
+    njs_variable_t      *var;
+    njs_parser_node_t   *node;
+    nxt_lvlhsh_each_t   lhe;
+    nxt_lvlhsh_query_t  lhq;


More information about the nginx-devel mailing list