]> git.kaiwu.me - njs.git/commitdiff
Interactive shell.
authorDmitry Volyntsev <xeioex@nginx.com>
Mon, 17 Jul 2017 17:38:00 +0000 (20:38 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Mon, 17 Jul 2017 17:38:00 +0000 (20:38 +0300)
23 files changed:
Makefile
njs/njs.c [new file with mode: 0644]
njs/njs_array.c
njs/njs_boolean.c
njs/njs_builtin.c
njs/njs_date.c
njs/njs_function.c
njs/njs_lexer_keyword.c
njs/njs_math.c
njs/njs_number.c
njs/njs_object.c
njs/njs_object.h
njs/njs_parser.c
njs/njs_parser.h
njs/njs_regexp.c
njs/njs_string.c
njs/njs_variable.c
njs/njs_vm.h
njs/njscript.c
njs/njscript.h
njs/test/njs_interactive_test.c [new file with mode: 0644]
nxt/auto/configure
nxt/auto/editline [new file with mode: 0644]

index e7d43fc2e7dd908a4f492ab206c49cc437deb0a6..a1df0c5b522d21b932c65207ed98a3d57d23a7ee 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -74,11 +74,15 @@ $(NXT_BUILDDIR)/libnjs.a: \
 
 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 @@ $(NXT_BUILDDIR)/njs_disassembler.o: \
                -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 @@ $(NXT_BUILDDIR)/njs_unit_test: \
                $(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 --git a/njs/njs.c b/njs/njs.c
new file mode 100644 (file)
index 0000000..4ff329f
--- /dev/null
+++ b/njs/njs.c
@@ -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;
+}
index 0fa1a1a5e57c21581f27a7f1c0f3676b6c880966..ef9ac75997cd4a2adbba1e715fa8e7d7f123e01a 100644 (file)
@@ -373,6 +373,7 @@ static const njs_object_prop_t  njs_array_constructor_properties[] =
 
 
 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_array_prototype_properties[] =
 
 
 const njs_object_init_t  njs_array_prototype_init = {
+    nxt_string("Array"),
     njs_array_prototype_properties,
     nxt_nitems(njs_array_prototype_properties),
 };
index d75476a10c7e9fb607d8df9cf386c904d3bf28d4..f6839af5777f93e939121a69d11a5101aa30f129 100644 (file)
@@ -78,6 +78,7 @@ static const njs_object_prop_t  njs_boolean_constructor_properties[] =
 
 
 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_boolean_prototype_properties[] =
 
 
 const njs_object_init_t  njs_boolean_prototype_init = {
+    nxt_string("Boolean"),
     njs_boolean_prototype_properties,
     nxt_nitems(njs_boolean_prototype_properties),
 };
index 58b64351779c01b1b02c0715d266f841fab5b3f4..1ce47075978a700cd3bc268c2d930a5f7dfc8222 100644 (file)
@@ -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;
+}
index b1c4aa0d9b2f3b7601cb1a61853de334a566165d..8a170805140f4c907b9dead6e717cfd81d8e4af3 100644 (file)
@@ -931,6 +931,7 @@ static const njs_object_prop_t  njs_date_constructor_properties[] =
 
 
 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_prototype_properties[] =
 
 
 const njs_object_init_t  njs_date_prototype_init = {
+    nxt_string("Date"),
     njs_date_prototype_properties,
     nxt_nitems(njs_date_prototype_properties),
 };
index 88eafad4cbd16be197351ec0d246b88635cd5a1f..4ccc93883e0110e51c37cd913e7965a90837234d 100644 (file)
@@ -497,6 +497,7 @@ static const njs_object_prop_t  njs_function_constructor_properties[] =
 
 
 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_function_prototype_properties[] =
 
 
 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_function_properties[] =
 
 
 const njs_object_init_t  njs_eval_function_init = {
+    nxt_string("Function"),
     njs_eval_function_properties,
     nxt_nitems(njs_eval_function_properties),
 };
index e80f085d44a51a80b65116eb5526fd4c8bd76a1c..a6f4313a712ed3a66e8c9cd35bda44dc71206368 100644 (file)
 #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 *lhq, void *data)
 }
 
 
-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,
index 500540c2111a6735428518a10679558c4eaa736d..0a2831316721f8084214c509e3e82204ba2247f9 100644 (file)
@@ -1088,6 +1088,7 @@ static const njs_object_prop_t  njs_math_object_properties[] =
 
 
 const njs_object_init_t  njs_math_object_init = {
+    nxt_string("Math"),
     njs_math_object_properties,
     nxt_nitems(njs_math_object_properties),
 };
index a4678188fc457f5024ad339989201b0c62dc6a6a..4e19d7de36a2ea97dcd85ad605543e7b754ab9d1 100644 (file)
@@ -528,6 +528,7 @@ static const njs_object_prop_t  njs_number_constructor_properties[] =
 
 
 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_number_prototype_properties[] =
 
 
 const njs_object_init_t  njs_number_prototype_init = {
+    nxt_string("Number"),
     njs_number_prototype_properties,
     nxt_nitems(njs_number_prototype_properties),
 };
index e1ba1e79e9077d86d53f1bd8cd35bca7e3102b33..046a28c1ba289b4a606265d927455ba50e6bb1bd 100644 (file)
@@ -1153,6 +1153,7 @@ static const njs_object_prop_t  njs_object_constructor_properties[] =
 
 
 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_object_prototype_properties[] =
 
 
 const njs_object_init_t  njs_object_prototype_init = {
+    nxt_string("Object"),
     njs_object_prototype_properties,
     nxt_nitems(njs_object_prototype_properties),
 };
index 3dd98fb683e4472ee6b043d4cd0501f0e1b94aef..bee87d3ebd7b88215616471353e3515b98cf8000 100644 (file)
@@ -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;
 };
index 85b8bcbe4360504c46ec4099fc21b05882f28a0d..20ff89a86a75896702d616749b57257fc5387be8 100644 (file)
@@ -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_token(njs_vm_t *vm,
 
 
 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;
 
     ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_GLOBAL);
     if (nxt_slow_path(ret != NXT_OK)) {
         return NULL;
     }
 
+    if (prev != NULL) {
+        /*
+         * Copy the global scope variables from the previous
+         * iteration of the accumulative mode.
+         */
+        nxt_lvlhsh_each_init(&lhe, &njs_variables_hash_proto);
+
+        lhq.proto = &njs_variables_hash_proto;
+        lhq.replace = 0;
+        lhq.pool = vm->mem_cache_pool;
+
+        variables = &parser->scope->variables;
+        prev_variables = &prev->scope->variables;
+
+        for ( ;; ) {
+            var = nxt_lvlhsh_each(prev_variables, &lhe);
+
+            if (var == NULL) {
+                break;
+            }
+
+            lhq.value = var;
+            lhq.key = var->name;
+            lhq.key_hash = nxt_djb_hash(var->name.start, var->name.length);
+
+            ret = nxt_lvlhsh_insert(variables, &lhq);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NULL;
+            }
+        }
+    }
+
     token = njs_parser_token(parser);
 
     while (token != NJS_TOKEN_END) {
index 96ba7fd8cf22ebf4cac9844ddfd769738852ca36..f0b85be7d7999860d46b4222049990c88113a51d 100644 (file)
@@ -334,6 +334,13 @@ struct njs_parser_s {
 };
 
 
+typedef struct {
+    nxt_str_t                       name;
+    njs_token_t                     token;
+    double                          number;
+} njs_keyword_t;
+
+
 njs_token_t njs_lexer_token(njs_lexer_t *lexer);
 nxt_int_t njs_lexer_keywords_init(nxt_mem_cache_pool_t *mcp,
     nxt_lvlhsh_t *hash);
@@ -341,7 +348,8 @@ njs_token_t njs_lexer_keyword(njs_lexer_t *lexer);
 
 njs_extern_t *njs_parser_external(njs_vm_t *vm, njs_parser_t *parser);
 
-njs_parser_node_t *njs_parser(njs_vm_t *vm, njs_parser_t *parser);
+njs_parser_node_t *njs_parser(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_t *prev);
 njs_token_t njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *parent);
 njs_token_t njs_parser_expression(njs_vm_t *vm, njs_parser_t *parser,
@@ -375,4 +383,7 @@ nxt_int_t njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser,
     } while (0)
 
 
+extern const nxt_lvlhsh_proto_t  njs_keyword_hash_proto;
+
+
 #endif /* _NJS_PARSER_H_INCLUDED_ */
index 8aa86a9454454d556c6669ef1c7c72b5c335bdbe..c64a449c66510d6c867fc402ea78d4b76133efe2 100644 (file)
@@ -843,6 +843,7 @@ static const njs_object_prop_t  njs_regexp_constructor_properties[] =
 
 
 const njs_object_init_t  njs_regexp_constructor_init = {
+    nxt_string("RegExp"),
     njs_regexp_constructor_properties,
     nxt_nitems(njs_regexp_constructor_properties),
 };
@@ -903,6 +904,7 @@ static const njs_object_prop_t  njs_regexp_prototype_properties[] =
 
 
 const njs_object_init_t  njs_regexp_prototype_init = {
+    nxt_string("RegExp"),
     njs_regexp_prototype_properties,
     nxt_nitems(njs_regexp_prototype_properties),
 };
index 287cba8aadbf03528204a57b8ccdf2f4899f42ff..040c38877171a6866dd2f436b959831f72cf7afa 100644 (file)
@@ -425,6 +425,7 @@ static const njs_object_prop_t  njs_string_constructor_properties[] =
 
 
 const njs_object_init_t  njs_string_constructor_init = {
+    nxt_string("String"),
     njs_string_constructor_properties,
     nxt_nitems(njs_string_constructor_properties),
 };
@@ -3262,6 +3263,7 @@ static const njs_object_prop_t  njs_string_prototype_properties[] =
 
 
 const njs_object_init_t  njs_string_prototype_init = {
+    nxt_string("String"),
     njs_string_prototype_properties,
     nxt_nitems(njs_string_prototype_properties),
 };
index 0af2844c172b6cc3b366fb4d921e617d6f026977..9d2faad6b0e5f9d124ebabc4da40e58761dc38ba 100644 (file)
@@ -137,7 +137,7 @@ njs_variable_add(njs_vm_t *vm, njs_parser_t *parser, njs_variable_type_t type)
         return var;
     }
 
-    lhq.replace = 0;
+    lhq.replace = vm->accumulative;
     lhq.value = var;
     lhq.pool = vm->mem_cache_pool;
 
@@ -355,21 +355,43 @@ njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node)
         goto not_found;
     }
 
-    values = vs.scope->values[n];
+    if (vm->accumulative && vs.scope->type == NJS_SCOPE_GLOBAL) {
 
-    if (values == NULL) {
-        values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto,
-                                  vm->mem_cache_pool);
-        if (nxt_slow_path(values == NULL)) {
+        /*
+         * When non-clonable VM runs in accumulative mode all
+         * global variables should be allocated in absolute scope
+         * to share them among consecutive VM invocations.
+         */
+
+        value = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+                                    sizeof(njs_value_t));
+        if (nxt_slow_path(value == NULL)) {
             return NULL;
         }
 
-        vs.scope->values[n] = values;
-    }
+        index = (njs_index_t) value;
+
+    } else {
+        values = vs.scope->values[n];
+
+        if (values == NULL) {
+            values = nxt_array_create(4, sizeof(njs_value_t),
+                                      &njs_array_mem_proto, vm->mem_cache_pool);
+            if (nxt_slow_path(values == NULL)) {
+                return NULL;
+            }
+
+            vs.scope->values[n] = values;
+        }
+
+        value = nxt_array_add(values, &njs_array_mem_proto, vm->mem_cache_pool);
+        if (nxt_slow_path(value == NULL)) {
+            return NULL;
+        }
+
+        index = vs.scope->next_index[n];
+        vs.scope->next_index[n] += sizeof(njs_value_t);
 
-    value = nxt_array_add(values, &njs_array_mem_proto, vm->mem_cache_pool);
-    if (nxt_slow_path(value == NULL)) {
-        return NULL;
     }
 
     if (njs_is_object(&var->value)) {
@@ -379,9 +401,6 @@ njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node)
         *value = njs_value_void;
     }
 
-    index = vs.scope->next_index[n];
-    vs.scope->next_index[n] += sizeof(njs_value_t);
-
     var->index = index;
     node->index = index;
 
index 06507504743f5bf67581c9c8d5fc1208141df26c..14dfaef095161290b5724cf965135d67f7a8e5b4 100644 (file)
@@ -904,6 +904,7 @@ struct njs_vm_s {
     nxt_random_t             random;
 
     uint8_t                  trailer;  /* 1 bit */
+    uint8_t                  accumulative; /* 1 bit */
 };
 
 
index 1871ae7f103cd9ab728259af5be8eb4c5a326510..362dedeaf02e6512f8f5cef2af6c88d3bdae66ea 100644 (file)
@@ -65,7 +65,7 @@ njs_free(void *mem, void *p)
 }
 
 
-static const nxt_mem_proto_t  njs_vm_mem_cache_pool_proto = {
+const nxt_mem_proto_t  njs_vm_mem_cache_pool_proto = {
     njs_alloc,
     njs_zalloc,
     njs_align,
@@ -175,6 +175,14 @@ njs_vm_create(njs_vm_opt_t *options)
         vm->trace.data = vm;
 
         vm->trailer = options->trailer;
+
+        vm->accumulative = options->accumulative;
+        if (vm->accumulative) {
+            ret = njs_vm_init(vm);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NULL;
+            }
+        }
     }
 
     return vm;
@@ -193,7 +201,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
 {
     nxt_int_t          ret;
     njs_lexer_t        *lexer;
-    njs_parser_t       *parser;
+    njs_parser_t       *parser, *prev;
     njs_parser_node_t  *node;
 
     parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t));
@@ -201,6 +209,11 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
         return NJS_ERROR;
     }
 
+    if (vm->parser != NULL && !vm->accumulative) {
+        return NJS_ERROR;
+    }
+
+    prev = vm->parser;
     vm->parser = parser;
 
     lexer = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_lexer_t));
@@ -217,21 +230,27 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
     parser->code_size = sizeof(njs_vmcode_stop_t);
     parser->scope_offset = NJS_INDEX_GLOBAL_OFFSET;
 
-    node = njs_parser(vm, parser);
+    node = njs_parser(vm, parser, prev);
     if (nxt_slow_path(node == NULL)) {
-        return NJS_ERROR;
+        goto fail;
     }
 
     ret = njs_variables_scope_reference(vm, parser->scope);
     if (nxt_slow_path(ret != NXT_OK)) {
-        return NJS_ERROR;
+        goto fail;
     }
 
     *start = parser->lexer->start;
 
+    /*
+     * Reset the code array to prevent it from being disassembled
+     * again in the next iteration of the accumulative mode.
+     */
+    vm->code = NULL;
+
     ret = njs_generate_scope(vm, parser, node);
     if (nxt_slow_path(ret != NXT_OK)) {
-        return NJS_ERROR;
+        goto fail;
     }
 
     vm->current = parser->code_start;
@@ -240,9 +259,13 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
     vm->scope_size = parser->scope_size;
     vm->variables_hash = parser->scope->variables;
 
-    vm->parser = NULL;
-
     return NJS_OK;
+
+fail:
+
+    vm->parser = prev;
+
+    return NXT_ERROR;
 }
 
 
@@ -255,6 +278,10 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external)
 
     nxt_thread_log_debug("CLONE:");
 
+    if (vm->accumulative) {
+        return NULL;
+    }
+
     nmcp = mcp;
 
     if (nmcp == NULL) {
index 65111d4c84901fbd65cb5ee2d9dd49d7eb9b300c..28c119047984d8579ddfbd74599a84f31d10acb7 100644 (file)
@@ -70,7 +70,8 @@ typedef struct {
     njs_vm_shared_t                 *shared;
     nxt_mem_cache_pool_t            *mcp;
 
-    uint8_t                         trailer;   /* 1 bit */
+    uint8_t                         trailer;         /* 1 bit */
+    uint8_t                         accumulative;    /* 1 bit */
 } njs_vm_opt_t;
 
 
@@ -113,5 +114,9 @@ NXT_EXPORT void *njs_value_data(njs_value_t *value);
 NXT_EXPORT nxt_int_t njs_value_string_copy(njs_vm_t *vm, nxt_str_t *retval,
     njs_value_t *value, uintptr_t *next);
 
+NXT_EXPORT const char **njs_vm_completions(njs_vm_t *vm);
+
+
+const nxt_mem_proto_t  njs_vm_mem_cache_pool_proto;
 
 #endif /* _NJSCRIPT_H_INCLUDED_ */
diff --git a/njs/test/njs_interactive_test.c b/njs/test/njs_interactive_test.c
new file mode 100644 (file)
index 0000000..b096f35
--- /dev/null
@@ -0,0 +1,197 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#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_mem_cache_pool.h>
+#include <njscript.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <time.h>
+
+
+typedef struct {
+    nxt_str_t  script;
+    nxt_str_t  ret;
+} njs_interactive_test_t;
+
+
+#define ENTER "\n"
+
+
+static njs_interactive_test_t  njs_test[] =
+{
+    { nxt_string("var a = 3" ENTER
+                 "a * 2" ENTER),
+      nxt_string("6") },
+
+    { nxt_string("var a = \"aa\\naa\"" ENTER
+                 "a" ENTER),
+      nxt_string("aa\naa") },
+
+    { nxt_string("var a = 3" ENTER
+                 "var a = 'str'" ENTER
+                 "a" ENTER),
+      nxt_string("str") },
+
+    { nxt_string("var a = 2" ENTER
+                 "a *= 2" ENTER
+                 "a *= 2" ENTER
+                 "a *= 2" ENTER),
+      nxt_string("16") },
+
+    { nxt_string("var a = 2" ENTER
+                 "var b = 3" ENTER
+                 "a * b" ENTER),
+      nxt_string("6") },
+
+    { nxt_string("var a = 2; var b = 3;" ENTER
+                 "a * b" ENTER),
+      nxt_string("6") },
+
+    { nxt_string("function sq(f) { return f() * f() }" ENTER
+                 "sq(function () { return 3 })" ENTER),
+      nxt_string("9") },
+
+    /* Error handling */
+
+    { nxt_string("var a = ;" ENTER
+                 "2 + 2" ENTER),
+      nxt_string("4") },
+
+    { nxt_string("function f() { return b;" ENTER),
+      nxt_string("SyntaxError: Unexpected end of input in 1") },
+
+    { nxt_string("function f() { return b;" ENTER
+                 "2 + 2" ENTER),
+      nxt_string("4") },
+
+    { nxt_string("function f() { return function() { return 1" ENTER
+                 "2 + 2" ENTER),
+      nxt_string("4") },
+
+    { nxt_string("function f() { return b;}" ENTER
+                 "2 + 2" ENTER),
+      nxt_string("4") },
+
+    { nxt_string("function f(o) { return o.a.a;}; f{{}}" ENTER
+                 "2 + 2" ENTER),
+      nxt_string("4") },
+
+    { nxt_string("function ff(o) {return o.a.a}" ENTER
+                 "function f(o) {return ff(o)}" ENTER
+                 "f({})" ENTER),
+      nxt_string("TypeError") },
+
+    { nxt_string("function f(ff, o) {return ff(o)}" ENTER
+                 "f(function (o) {return o.a.a}, {})" ENTER),
+      nxt_string("TypeError") },
+
+};
+
+
+static nxt_int_t
+njs_interactive_test(void)
+{
+    u_char                  *start, *last, *end;
+    njs_vm_t                *vm;
+    nxt_int_t               ret;
+    nxt_str_t               s;
+    nxt_uint_t              i;
+    nxt_bool_t              success;
+    njs_vm_opt_t            options;
+    nxt_mem_cache_pool_t    *mcp;
+    njs_interactive_test_t  *test;
+
+    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 NXT_ERROR;
+    }
+
+    ret = NXT_ERROR;
+
+    for (i = 0; i < nxt_nitems(njs_test); i++) {
+
+        test = &njs_test[i];
+
+        printf("\"%.*s\"\n", (int) test->script.length, test->script.start);
+        fflush(stdout);
+
+        memset(&options, 0, sizeof(njs_vm_opt_t));
+
+        options.mcp = mcp;
+        options.accumulative = 1;
+
+        vm = njs_vm_create(&options);
+        if (vm == NULL) {
+            goto fail;
+        }
+
+        start = test->script.start;
+        last = start + test->script.length;
+        end = NULL;
+
+        for ( ;; ) {
+            start = (end != NULL) ? end + 1 : start;
+            if (start >= last) {
+                break;
+            }
+
+            end = (u_char *) strchr((char *) start, '\n');
+
+            ret = njs_vm_compile(vm, &start, end);
+            if (ret == NXT_OK) {
+                ret = njs_vm_run(vm);
+            }
+        }
+
+        if (ret == NXT_OK) {
+            if (njs_vm_retval(vm, &s) != NXT_OK) {
+                goto fail;
+            }
+
+        } else {
+            njs_vm_exception(vm, &s);
+        }
+
+        success = nxt_strstr_eq(&test->ret, &s);
+        if (success) {
+            continue;
+        }
+
+        printf("njs_interactive(\"%.*s\") failed: \"%.*s\" vs \"%.*s\"\n",
+               (int) test->script.length, test->script.start,
+               (int) test->ret.length, test->ret.start,
+               (int) s.length, s.start);
+
+        goto fail;
+    }
+
+    ret = NXT_OK;
+
+    printf("njs interactive tests passed\n");
+
+fail:
+
+    nxt_mem_cache_pool_destroy(mcp);
+
+    return ret;
+}
+
+
+int nxt_cdecl
+main(int argc, char **argv)
+{
+    return njs_interactive_test();
+}
index a4b828dd575dc391b4982f856d3df14bc11e4818..0ea33a7b1816bedbdd47eb81415275efe6ea1388 100755 (executable)
@@ -55,3 +55,4 @@ END
 . ${NXT_AUTO}memalign
 . ${NXT_AUTO}getrandom
 . ${NXT_AUTO}pcre
+. ${NXT_AUTO}editline
diff --git a/nxt/auto/editline b/nxt/auto/editline
new file mode 100644 (file)
index 0000000..45b0670
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) NGINX, Inc.
+
+nxt_feature="editline library"
+nxt_feature_name=NXT_HAVE_EDITLINE
+nxt_feature_run=no
+nxt_feature_incs=
+nxt_feature_libs=-ledit
+nxt_feature_test="#include <editline/readline.h>
+
+                 int main(void) {
+                     add_history(NULL);
+                     return 0;
+                 }"
+. ${NXT_AUTO}feature
+
+if [ $nxt_found = yes ]; then
+    cat << END >> $NXT_MAKEFILE_CONF
+NXT_EDITLINE_LIB = -ledit
+END
+
+else
+    $nxt_echo " - building interactive shell is not possible"
+
+fi