]> git.kaiwu.me - njs.git/commitdiff
Moving njs.c functions into njs_vm.c and njs_value.c
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 23 Jul 2019 16:42:25 +0000 (19:42 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Tue, 23 Jul 2019 16:42:25 +0000 (19:42 +0300)
NO functional changes.

auto/sources
njs/njs.c [deleted file]
njs/njs_value.c
njs/njs_vm.c

index 06867db2e1bd7ec5ce9aa9d298c9b5764fcd6206..20e5ac87a9179a87d623604ad859443dd9fd1bff 100644 (file)
@@ -29,7 +29,6 @@ NXT_TEST_SRCS=" \
 "
 
 NJS_LIB_SRCS=" \
-   njs/njs.c \
    njs/njs_value.c \
    njs/njs_vm.c \
    njs/njs_vmcode.c \
diff --git a/njs/njs.c b/njs/njs.c
deleted file mode 100644 (file)
index 25fecca..0000000
--- a/njs/njs.c
+++ /dev/null
@@ -1,1105 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) NGINX, Inc.
- */
-
-#include <njs_core.h>
-#include <njs_regexp.h>
-#include <string.h>
-
-
-static nxt_int_t njs_vm_init(njs_vm_t *vm);
-static nxt_int_t njs_vm_handle_events(njs_vm_t *vm);
-
-
-static void *
-njs_alloc(void *mem, size_t size)
-{
-    return nxt_malloc(size);
-}
-
-
-static void *
-njs_zalloc(void *mem, size_t size)
-{
-    void  *p;
-
-    p = nxt_malloc(size);
-
-    if (p != NULL) {
-        nxt_memzero(p, size);
-    }
-
-    return p;
-}
-
-
-static void *
-njs_align(void *mem, size_t alignment, size_t size)
-{
-    return nxt_memalign(alignment, size);
-}
-
-
-static void
-njs_free(void *mem, void *p)
-{
-    nxt_free(p);
-}
-
-
-const nxt_mem_proto_t  njs_vm_mp_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_mp_alloc(mem, size);
-}
-
-
-static void
-njs_array_mem_free(void *mem, void *p)
-{
-    nxt_mp_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)
-{
-    nxt_mp_t              *mp;
-    njs_vm_t              *vm;
-    nxt_int_t             ret;
-    nxt_array_t           *debug;
-    njs_regexp_pattern_t  *pattern;
-
-    mp = nxt_mp_create(&njs_vm_mp_proto, NULL, NULL, 2 * nxt_pagesize(),
-                       128, 512, 16);
-    if (nxt_slow_path(mp == NULL)) {
-        return NULL;
-    }
-
-    vm = nxt_mp_zalign(mp, sizeof(njs_value_t), sizeof(njs_vm_t));
-
-    if (nxt_fast_path(vm != NULL)) {
-        vm->mem_pool = mp;
-
-        ret = njs_regexp_init(vm);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NULL;
-        }
-
-        vm->options = *options;
-
-        if (options->shared != NULL) {
-            vm->shared = options->shared;
-
-        } else {
-            vm->shared = nxt_mp_zalloc(mp, sizeof(njs_vm_shared_t));
-            if (nxt_slow_path(vm->shared == NULL)) {
-                return NULL;
-            }
-
-            options->shared = vm->shared;
-
-            nxt_lvlhsh_init(&vm->shared->keywords_hash);
-
-            ret = njs_lexer_keywords_init(mp, &vm->shared->keywords_hash);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return NULL;
-            }
-
-            nxt_lvlhsh_init(&vm->shared->values_hash);
-
-            pattern = njs_regexp_pattern_create(vm, (u_char *) "(?:)",
-                                                nxt_length("(?:)"), 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_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->trace.level = NXT_LEVEL_TRACE;
-        vm->trace.size = 2048;
-        vm->trace.handler = njs_parser_trace_handler;
-        vm->trace.data = vm;
-
-        if (options->backtrace) {
-            debug = nxt_array_create(4, sizeof(njs_function_debug_t),
-                                     &njs_array_mem_proto, vm->mem_pool);
-            if (nxt_slow_path(debug == NULL)) {
-                return NULL;
-            }
-
-            vm->debug = debug;
-        }
-
-        if (options->accumulative) {
-            ret = njs_vm_init(vm);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return NULL;
-            }
-        }
-    }
-
-    return vm;
-}
-
-
-void
-njs_vm_destroy(njs_vm_t *vm)
-{
-    njs_event_t        *event;
-    nxt_lvlhsh_each_t  lhe;
-
-    if (njs_waiting_events(vm)) {
-        nxt_lvlhsh_each_init(&lhe, &njs_event_hash_proto);
-
-        for ( ;; ) {
-            event = nxt_lvlhsh_each(&vm->events_hash, &lhe);
-
-            if (event == NULL) {
-                break;
-            }
-
-            njs_del_event(vm, event, NJS_EVENT_RELEASE);
-        }
-    }
-
-    nxt_mp_destroy(vm->mem_pool);
-}
-
-
-nxt_int_t
-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, *prev;
-    njs_generator_t     generator;
-    njs_parser_scope_t  *scope;
-
-    if (vm->parser != NULL && !vm->options.accumulative) {
-        return NJS_ERROR;
-    }
-
-    if (vm->modules != NULL && vm->options.accumulative) {
-        njs_module_reset(vm);
-    }
-
-    parser = nxt_mp_zalloc(vm->mem_pool, sizeof(njs_parser_t));
-    if (nxt_slow_path(parser == NULL)) {
-        return NJS_ERROR;
-    }
-
-    prev = vm->parser;
-    vm->parser = parser;
-
-    ret = njs_lexer_init(vm, &lexer, &vm->options.file, *start, end);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return NJS_ERROR;
-    }
-
-    parser->lexer = &lexer;
-
-    if (vm->backtrace != NULL) {
-        nxt_array_reset(vm->backtrace);
-    }
-
-    vm->retval = njs_value_undefined;
-
-    ret = njs_parser(vm, parser, prev);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        goto fail;
-    }
-
-    parser->lexer = NULL;
-
-    scope = parser->scope;
-
-    ret = njs_variables_scope_reference(vm, scope);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        goto fail;
-    }
-
-    *start = lexer.start;
-
-    /*
-     * Reset the code array to prevent it from being disassembled
-     * again in the next iteration of the accumulative mode.
-     */
-    vm->code = NULL;
-
-    nxt_memzero(&generator, sizeof(njs_generator_t));
-
-    ret = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        goto fail;
-    }
-
-    vm->current = generator.code_start;
-    vm->global_scope = generator.local_scope;
-    vm->scope_size = generator.scope_size;
-
-    vm->variables_hash = scope->variables;
-
-    if (vm->options.init) {
-        ret = njs_vm_init(vm);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-    }
-
-    return NJS_OK;
-
-fail:
-
-    vm->parser = prev;
-
-    return NXT_ERROR;
-}
-
-
-njs_vm_t *
-njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
-{
-    nxt_mp_t     *nmp;
-    njs_vm_t     *nvm;
-    uint32_t     items;
-    nxt_int_t    ret;
-    nxt_array_t  *externals;
-
-    nxt_thread_log_debug("CLONE:");
-
-    if (vm->options.accumulative) {
-        return NULL;
-    }
-
-    nmp = nxt_mp_create(&njs_vm_mp_proto, NULL, NULL, 2 * nxt_pagesize(),
-                        128, 512, 16);
-    if (nxt_slow_path(nmp == NULL)) {
-        return NULL;
-    }
-
-    nvm = nxt_mp_zalign(nmp, sizeof(njs_value_t), sizeof(njs_vm_t));
-
-    if (nxt_fast_path(nvm != NULL)) {
-        nvm->mem_pool = nmp;
-
-        nvm->shared = vm->shared;
-
-        nvm->trace = vm->trace;
-        nvm->trace.data = nvm;
-
-        nvm->variables_hash = vm->variables_hash;
-        nvm->values_hash = vm->values_hash;
-
-        nvm->modules = vm->modules;
-        nvm->modules_hash = vm->modules_hash;
-
-        nvm->externals_hash = vm->externals_hash;
-        nvm->external_prototypes_hash = vm->external_prototypes_hash;
-
-        items = vm->external_objects->items;
-        externals = nxt_array_create(items + 4, sizeof(void *),
-                                     &njs_array_mem_proto, nvm->mem_pool);
-
-        if (nxt_slow_path(externals == NULL)) {
-            return NULL;
-        }
-
-        if (items > 0) {
-            memcpy(externals->start, vm->external_objects->start,
-                   items * sizeof(void *));
-            externals->items = items;
-        }
-
-        nvm->external_objects = externals;
-
-        nvm->options = vm->options;
-
-        nvm->current = vm->current;
-
-        nvm->external = external;
-
-        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;
-        }
-
-        return nvm;
-    }
-
-fail:
-
-    nxt_mp_destroy(nmp);
-
-    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);
-
-    frame = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
-    if (nxt_slow_path(frame == NULL)) {
-        return NXT_ERROR;
-    }
-
-    nxt_memzero(frame, 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;
-
-    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;
-    }
-
-    ret = njs_builtin_objects_clone(vm);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return NXT_ERROR;
-    }
-
-    nxt_lvlhsh_init(&vm->events_hash);
-    nxt_queue_init(&vm->posted_events);
-
-    if (vm->debug != NULL) {
-        backtrace = nxt_array_create(4, sizeof(njs_backtrace_entry_t),
-                                     &njs_array_mem_proto, vm->mem_pool);
-        if (nxt_slow_path(backtrace == NULL)) {
-            return NXT_ERROR;
-        }
-
-        vm->backtrace = backtrace;
-    }
-
-    if (njs_is_null(&vm->retval)) {
-        vm->retval = njs_value_undefined;
-    }
-
-    return NXT_OK;
-}
-
-
-nxt_int_t
-njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
-    nxt_uint_t nargs)
-{
-    return njs_vm_invoke(vm, function, args, nargs, (njs_index_t) &vm->retval);
-}
-
-
-nxt_int_t
-njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
-    nxt_uint_t nargs, njs_index_t retval)
-{
-    njs_ret_t    ret;
-    njs_value_t  *this;
-
-    this = (njs_value_t *) &njs_value_undefined;
-
-    ret = njs_function_frame(vm, function, this, (njs_value_t *) args, nargs,
-                             0);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return ret;
-    }
-
-    return njs_function_frame_invoke(vm, retval);
-}
-
-
-njs_vm_event_t
-njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, nxt_uint_t once,
-    njs_host_event_t host_ev, njs_event_destructor_t destructor)
-{
-    njs_event_t  *event;
-
-    event = nxt_mp_alloc(vm->mem_pool, sizeof(njs_event_t));
-    if (nxt_slow_path(event == NULL)) {
-        return NULL;
-    }
-
-    event->host_event = host_ev;
-    event->destructor = destructor;
-    event->function = function;
-    event->once = once;
-    event->posted = 0;
-    event->nargs = 0;
-    event->args = NULL;
-
-    if (njs_add_event(vm, event) != NJS_OK) {
-        return NULL;
-    }
-
-    return event;
-}
-
-
-void
-njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event)
-{
-    njs_event_t  *event;
-
-    event = (njs_event_t *) vm_event;
-
-    njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
-}
-
-
-nxt_int_t
-njs_vm_waiting(njs_vm_t *vm)
-{
-    return njs_waiting_events(vm);
-}
-
-
-nxt_int_t
-njs_vm_posted(njs_vm_t *vm)
-{
-    return njs_posted_events(vm);
-}
-
-
-nxt_int_t
-njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
-    const njs_value_t *args, nxt_uint_t nargs)
-{
-    njs_event_t  *event;
-
-    event = (njs_event_t *) vm_event;
-
-    if (nargs != 0 && !event->posted) {
-        event->nargs = nargs;
-        event->args = nxt_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
-        if (nxt_slow_path(event->args == NULL)) {
-            return NJS_ERROR;
-        }
-
-        memcpy(event->args, args, sizeof(njs_value_t) * nargs);
-    }
-
-    if (!event->posted) {
-        event->posted = 1;
-        nxt_queue_insert_tail(&vm->posted_events, &event->link);
-    }
-
-    return NJS_OK;
-}
-
-
-nxt_int_t
-njs_vm_run(njs_vm_t *vm)
-{
-    if (nxt_slow_path(vm->backtrace != NULL)) {
-        nxt_array_reset(vm->backtrace);
-    }
-
-    return njs_vm_handle_events(vm);
-}
-
-
-nxt_int_t
-njs_vm_start(njs_vm_t *vm)
-{
-    njs_ret_t  ret;
-
-    ret = njs_module_load(vm);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return ret;
-    }
-
-    ret = njs_vmcode_interpreter(vm);
-
-    if (ret == NJS_STOP) {
-        ret = NJS_OK;
-    }
-
-    return ret;
-}
-
-
-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 ( ;; ) {
-        link = nxt_queue_first(events);
-
-        if (link == nxt_queue_tail(events)) {
-            break;
-        }
-
-        ev = nxt_queue_link_data(link, njs_event_t, link);
-
-        if (ev->once) {
-            njs_del_event(vm, ev, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
-
-        } else {
-            ev->posted = 0;
-            nxt_queue_remove(&ev->link);
-        }
-
-        ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs);
-
-        if (ret == NJS_ERROR) {
-            return ret;
-        }
-    }
-
-    return njs_posted_events(vm) ? NJS_AGAIN : NJS_OK;
-}
-
-
-nxt_int_t
-njs_vm_add_path(njs_vm_t *vm, const nxt_str_t *path)
-{
-    nxt_str_t  *item;
-
-    if (vm->paths == NULL) {
-        vm->paths = nxt_array_create(4, sizeof(nxt_str_t),
-                                     &njs_array_mem_proto, vm->mem_pool);
-        if (nxt_slow_path(vm->paths == NULL)) {
-            return NXT_ERROR;
-        }
-    }
-
-    item = nxt_array_add(vm->paths, &njs_array_mem_proto, vm->mem_pool);
-    if (nxt_slow_path(item == NULL)) {
-        return NXT_ERROR;
-    }
-
-    *item = *path;
-
-    return NXT_OK;
-}
-
-
-njs_value_t *
-njs_vm_retval(njs_vm_t *vm)
-{
-    return &vm->retval;
-}
-
-
-void
-njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value)
-{
-    vm->retval = *value;
-}
-
-
-void
-njs_value_undefined_set(njs_value_t *value)
-{
-    njs_set_undefined(value);
-}
-
-
-void
-njs_value_boolean_set(njs_value_t *value, int yn)
-{
-    njs_set_boolean(value, yn);
-}
-
-
-void
-njs_value_number_set(njs_value_t *value, double num)
-{
-    njs_set_number(value, num);
-}
-
-
-void
-njs_value_data_set(njs_value_t *value, void *data)
-{
-    njs_set_data(value, data);
-}
-
-
-njs_ret_t
-njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
-    uint32_t size)
-{
-    return njs_string_set(vm, value, start, size);
-}
-
-
-u_char *
-njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size)
-{
-    return njs_string_alloc(vm, value, size, 0);
-}
-
-
-nxt_noinline void
-njs_vm_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...)
-{
-    va_list  args;
-    u_char   buf[NXT_MAX_ERROR_STR], *p;
-
-    p = buf;
-
-    if (fmt != NULL) {
-        va_start(args, fmt);
-        p = nxt_vsprintf(buf, buf + sizeof(buf), fmt, args);
-        va_end(args);
-    }
-
-    njs_error_new(vm, value, NJS_OBJECT_ERROR, buf, p - buf);
-}
-
-
-uint8_t
-njs_value_bool(const njs_value_t *value)
-{
-    return njs_bool(value);
-}
-
-
-double
-njs_value_number(const njs_value_t *value)
-{
-    return njs_number(value);
-}
-
-
-void *
-njs_value_data(const njs_value_t *value)
-{
-    return njs_data(value);
-}
-
-
-njs_function_t *
-njs_value_function(const njs_value_t *value)
-{
-    return njs_function(value);
-}
-
-
-nxt_int_t
-njs_value_is_null(const njs_value_t *value)
-{
-    return njs_is_null(value);
-}
-
-
-nxt_int_t
-njs_value_is_undefined(const njs_value_t *value)
-{
-    return njs_is_undefined(value);
-}
-
-
-nxt_int_t
-njs_value_is_null_or_undefined(const njs_value_t *value)
-{
-    return njs_is_null_or_undefined(value);
-}
-
-
-nxt_int_t
-njs_value_is_boolean(const njs_value_t *value)
-{
-    return njs_is_boolean(value);
-}
-
-
-nxt_int_t
-njs_value_is_number(const njs_value_t *value)
-{
-    return njs_is_number(value);
-}
-
-
-nxt_int_t
-njs_value_is_valid_number(const njs_value_t *value)
-{
-    return njs_is_number(value)
-           && !isnan(njs_number(value))
-           && !isinf(njs_number(value));
-}
-
-
-nxt_int_t
-njs_value_is_string(const njs_value_t *value)
-{
-    return njs_is_string(value);
-}
-
-
-nxt_int_t
-njs_value_is_object(const njs_value_t *value)
-{
-    return njs_is_object(value);
-}
-
-
-nxt_int_t
-njs_value_is_function(const njs_value_t *value)
-{
-    return njs_is_function(value);
-}
-
-
-nxt_noinline void
-njs_vm_memory_error(njs_vm_t *vm)
-{
-    njs_memory_error_set(vm, &vm->retval);
-}
-
-
-nxt_array_t *
-njs_vm_backtrace(njs_vm_t *vm)
-{
-    if (vm->backtrace != NULL && !nxt_array_is_empty(vm->backtrace)) {
-        return vm->backtrace;
-    }
-
-    return NULL;
-}
-
-
-static njs_ret_t
-njs_vm_backtrace_dump(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
-{
-    u_char                 *p, *start, *end;
-    size_t                 len, count;
-    nxt_uint_t             i;
-    nxt_array_t            *backtrace;
-    njs_backtrace_entry_t  *be, *prev;
-
-    backtrace = njs_vm_backtrace(vm);
-
-    len = dst->length + 1;
-
-    count = 0;
-    prev = NULL;
-
-    be = backtrace->start;
-
-    for (i = 0; i < backtrace->items; i++) {
-        if (i != 0 && prev->name.start == be->name.start
-            && prev->line == be->line)
-        {
-            count++;
-
-        } else {
-
-            if (count != 0) {
-                len += nxt_length("      repeats  times\n")
-                       + NXT_INT_T_LEN;
-                count = 0;
-            }
-
-            len += be->name.length + nxt_length("    at  ()\n");
-
-            if (be->line != 0) {
-                len += be->file.length + NXT_INT_T_LEN + 1;
-
-            } else {
-                len += nxt_length("native");
-            }
-        }
-
-        prev = be;
-        be++;
-    }
-
-    p = nxt_mp_alloc(vm->mem_pool, len);
-    if (p == NULL) {
-        njs_memory_error(vm);
-        return NXT_ERROR;
-    }
-
-    start = p;
-    end = start + len;
-
-    p = nxt_cpymem(p, dst->start, dst->length);
-    *p++ = '\n';
-
-    count = 0;
-    prev = NULL;
-
-    be = backtrace->start;
-
-    for (i = 0; i < backtrace->items; i++) {
-        if (i != 0 && prev->name.start == be->name.start
-            && prev->line == be->line)
-        {
-            count++;
-
-        } else {
-            if (count != 0) {
-                p = nxt_sprintf(p, end, "      repeats %uz times\n",
-                                count);
-                count = 0;
-            }
-
-            p = nxt_sprintf(p, end, "    at %V ", &be->name);
-
-            if (be->line != 0) {
-                p = nxt_sprintf(p, end, "(%V:%uD)\n", &be->file,
-                                be->line);
-
-            } else {
-                p = nxt_sprintf(p, end, "(native)\n");
-            }
-        }
-
-        prev = be;
-        be++;
-    }
-
-    dst->start = start;
-    dst->length = p - dst->start;
-
-    return NXT_OK;
-}
-
-
-njs_ret_t
-njs_vm_value_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
-{
-    njs_ret_t   ret;
-    nxt_uint_t  exception;
-
-    if (nxt_slow_path(src->type == NJS_NUMBER
-                      && njs_number(src) == 0
-                      && signbit(njs_number(src))))
-    {
-        njs_string_get(&njs_string_minus_zero, dst);
-        return NXT_OK;
-    }
-
-    exception = 1;
-
-again:
-
-    ret = njs_vm_value_to_string(vm, dst, src);
-
-    if (nxt_fast_path(ret == NXT_OK)) {
-
-        if (njs_vm_backtrace(vm) != NULL) {
-            ret = njs_vm_backtrace_dump(vm, dst, src);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return NXT_ERROR;
-            }
-        }
-
-        return NXT_OK;
-    }
-
-    if (exception) {
-        exception = 0;
-
-        /* value evaluation threw an exception. */
-
-        src = &vm->retval;
-        goto again;
-    }
-
-    dst->length = 0;
-    dst->start = NULL;
-
-    return NXT_ERROR;
-}
-
-
-njs_ret_t
-njs_vm_retval_string(njs_vm_t *vm, nxt_str_t *dst)
-{
-    if (vm->top_frame == NULL) {
-        /* An exception was thrown during compilation. */
-
-        njs_vm_init(vm);
-    }
-
-    return njs_vm_value_string(vm, dst, &vm->retval);
-}
-
-
-njs_ret_t
-njs_vm_retval_dump(njs_vm_t *vm, nxt_str_t *dst, nxt_uint_t indent)
-{
-    if (vm->top_frame == NULL) {
-        /* An exception was thrown during compilation. */
-
-        njs_vm_init(vm);
-    }
-
-    return njs_vm_value_dump(vm, dst, &vm->retval, 0, 1);
-}
-
-
-njs_ret_t
-njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
-{
-    va_list             args;
-    nxt_int_t           ret;
-    njs_ret_t           rc;
-    njs_value_t         *name, *value;
-    njs_object_t        *object;
-    njs_object_prop_t   *prop;
-    nxt_lvlhsh_query_t  lhq;
-
-    object = njs_object_alloc(vm);
-    if (nxt_slow_path(object == NULL)) {
-        return NJS_ERROR;
-    }
-
-    rc = NJS_ERROR;
-
-    va_start(args, retval);
-
-    for ( ;; ) {
-        name = va_arg(args, njs_value_t *);
-        if (name == NULL) {
-            break;
-        }
-
-        value = va_arg(args, njs_value_t *);
-        if (value == NULL) {
-            njs_type_error(vm, "missed value for a key");
-            goto done;
-        }
-
-        if (nxt_slow_path(!njs_is_string(name))) {
-            njs_type_error(vm, "prop name is not a string");
-            goto done;
-        }
-
-        lhq.replace = 0;
-        lhq.pool = vm->mem_pool;
-        lhq.proto = &njs_object_hash_proto;
-
-        njs_string_get(name, &lhq.key);
-        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
-
-        prop = njs_object_prop_alloc(vm, name, value, 1);
-        if (nxt_slow_path(prop == NULL)) {
-            goto done;
-        }
-
-        lhq.value = prop;
-
-        ret = nxt_lvlhsh_insert(&object->hash, &lhq);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            njs_internal_error(vm, NULL);
-            goto done;
-        }
-    }
-
-    rc = NJS_OK;
-
-    njs_set_object(retval, object);
-
-done:
-
-    va_end(args);
-
-    return rc;
-}
-
-
-njs_value_t *
-njs_vm_object_prop(njs_vm_t *vm, const 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;
-    }
-
-    lhq.key = *key;
-    lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
-    lhq.proto = &njs_object_hash_proto;
-
-    ret = nxt_lvlhsh_find(njs_object_hash(value), &lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return NULL;
-    }
-
-    prop = lhq.value;
-
-    return &prop->value;
-}
index ac82314e65e88d1971b895604f540b97dcace720..0d2203a1cbc82c6ea0b9561615332050a16b5b8a 100644 (file)
@@ -329,3 +329,126 @@ njs_arg_type_string(uint8_t arg)
         return "unknown";
     }
 }
+
+
+void
+njs_value_undefined_set(njs_value_t *value)
+{
+    njs_set_undefined(value);
+}
+
+
+void
+njs_value_boolean_set(njs_value_t *value, int yn)
+{
+    njs_set_boolean(value, yn);
+}
+
+
+void
+njs_value_number_set(njs_value_t *value, double num)
+{
+    njs_set_number(value, num);
+}
+
+
+void
+njs_value_data_set(njs_value_t *value, void *data)
+{
+    njs_set_data(value, data);
+}
+
+
+uint8_t
+njs_value_bool(const njs_value_t *value)
+{
+    return njs_bool(value);
+}
+
+
+double
+njs_value_number(const njs_value_t *value)
+{
+    return njs_number(value);
+}
+
+
+void *
+njs_value_data(const njs_value_t *value)
+{
+    return njs_data(value);
+}
+
+
+njs_function_t *
+njs_value_function(const njs_value_t *value)
+{
+    return njs_function(value);
+}
+
+
+nxt_int_t
+njs_value_is_null(const njs_value_t *value)
+{
+    return njs_is_null(value);
+}
+
+
+nxt_int_t
+njs_value_is_undefined(const njs_value_t *value)
+{
+    return njs_is_undefined(value);
+}
+
+
+nxt_int_t
+njs_value_is_null_or_undefined(const njs_value_t *value)
+{
+    return njs_is_null_or_undefined(value);
+}
+
+
+nxt_int_t
+njs_value_is_boolean(const njs_value_t *value)
+{
+    return njs_is_boolean(value);
+}
+
+
+nxt_int_t
+njs_value_is_number(const njs_value_t *value)
+{
+    return njs_is_number(value);
+}
+
+
+nxt_int_t
+njs_value_is_valid_number(const njs_value_t *value)
+{
+    return njs_is_number(value)
+           && !isnan(njs_number(value))
+           && !isinf(njs_number(value));
+}
+
+
+nxt_int_t
+njs_value_is_string(const njs_value_t *value)
+{
+    return njs_is_string(value);
+}
+
+
+nxt_int_t
+njs_value_is_object(const njs_value_t *value)
+{
+    return njs_is_object(value);
+}
+
+
+nxt_int_t
+njs_value_is_function(const njs_value_t *value)
+{
+    return njs_is_function(value);
+}
+
+
index c94c8bef73e9e19fb055aca9eba58612e9b573fb..90f6cfbd994d040587d9e11f8965a77626bbdbd5 100644 (file)
@@ -9,6 +9,10 @@
 #include <string.h>
 
 
+static nxt_int_t njs_vm_init(njs_vm_t *vm);
+static nxt_int_t njs_vm_handle_events(njs_vm_t *vm);
+
+
 const nxt_str_t  njs_entry_main =           nxt_string("main");
 const nxt_str_t  njs_entry_module =         nxt_string("module");
 const nxt_str_t  njs_entry_native =         nxt_string("native");
@@ -16,6 +20,472 @@ const nxt_str_t  njs_entry_unknown =        nxt_string("unknown");
 const nxt_str_t  njs_entry_anonymous =      nxt_string("anonymous");
 
 
+static void *
+njs_alloc(void *mem, size_t size)
+{
+    return nxt_malloc(size);
+}
+
+
+static void *
+njs_zalloc(void *mem, size_t size)
+{
+    void  *p;
+
+    p = nxt_malloc(size);
+
+    if (p != NULL) {
+        nxt_memzero(p, size);
+    }
+
+    return p;
+}
+
+
+static void *
+njs_align(void *mem, size_t alignment, size_t size)
+{
+    return nxt_memalign(alignment, size);
+}
+
+
+static void
+njs_free(void *mem, void *p)
+{
+    nxt_free(p);
+}
+
+
+const nxt_mem_proto_t  njs_vm_mp_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_mp_alloc(mem, size);
+}
+
+
+static void
+njs_array_mem_free(void *mem, void *p)
+{
+    nxt_mp_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)
+{
+    nxt_mp_t              *mp;
+    njs_vm_t              *vm;
+    nxt_int_t             ret;
+    nxt_array_t           *debug;
+    njs_regexp_pattern_t  *pattern;
+
+    mp = nxt_mp_create(&njs_vm_mp_proto, NULL, NULL, 2 * nxt_pagesize(),
+                       128, 512, 16);
+    if (nxt_slow_path(mp == NULL)) {
+        return NULL;
+    }
+
+    vm = nxt_mp_zalign(mp, sizeof(njs_value_t), sizeof(njs_vm_t));
+
+    if (nxt_fast_path(vm != NULL)) {
+        vm->mem_pool = mp;
+
+        ret = njs_regexp_init(vm);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NULL;
+        }
+
+        vm->options = *options;
+
+        if (options->shared != NULL) {
+            vm->shared = options->shared;
+
+        } else {
+            vm->shared = nxt_mp_zalloc(mp, sizeof(njs_vm_shared_t));
+            if (nxt_slow_path(vm->shared == NULL)) {
+                return NULL;
+            }
+
+            options->shared = vm->shared;
+
+            nxt_lvlhsh_init(&vm->shared->keywords_hash);
+
+            ret = njs_lexer_keywords_init(mp, &vm->shared->keywords_hash);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NULL;
+            }
+
+            nxt_lvlhsh_init(&vm->shared->values_hash);
+
+            pattern = njs_regexp_pattern_create(vm, (u_char *) "(?:)",
+                                                nxt_length("(?:)"), 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_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->trace.level = NXT_LEVEL_TRACE;
+        vm->trace.size = 2048;
+        vm->trace.handler = njs_parser_trace_handler;
+        vm->trace.data = vm;
+
+        if (options->backtrace) {
+            debug = nxt_array_create(4, sizeof(njs_function_debug_t),
+                                     &njs_array_mem_proto, vm->mem_pool);
+            if (nxt_slow_path(debug == NULL)) {
+                return NULL;
+            }
+
+            vm->debug = debug;
+        }
+
+        if (options->accumulative) {
+            ret = njs_vm_init(vm);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NULL;
+            }
+        }
+    }
+
+    return vm;
+}
+
+
+void
+njs_vm_destroy(njs_vm_t *vm)
+{
+    njs_event_t        *event;
+    nxt_lvlhsh_each_t  lhe;
+
+    if (njs_waiting_events(vm)) {
+        nxt_lvlhsh_each_init(&lhe, &njs_event_hash_proto);
+
+        for ( ;; ) {
+            event = nxt_lvlhsh_each(&vm->events_hash, &lhe);
+
+            if (event == NULL) {
+                break;
+            }
+
+            njs_del_event(vm, event, NJS_EVENT_RELEASE);
+        }
+    }
+
+    nxt_mp_destroy(vm->mem_pool);
+}
+
+
+nxt_int_t
+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, *prev;
+    njs_generator_t     generator;
+    njs_parser_scope_t  *scope;
+
+    if (vm->parser != NULL && !vm->options.accumulative) {
+        return NJS_ERROR;
+    }
+
+    if (vm->modules != NULL && vm->options.accumulative) {
+        njs_module_reset(vm);
+    }
+
+    parser = nxt_mp_zalloc(vm->mem_pool, sizeof(njs_parser_t));
+    if (nxt_slow_path(parser == NULL)) {
+        return NJS_ERROR;
+    }
+
+    prev = vm->parser;
+    vm->parser = parser;
+
+    ret = njs_lexer_init(vm, &lexer, &vm->options.file, *start, end);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_ERROR;
+    }
+
+    parser->lexer = &lexer;
+
+    if (vm->backtrace != NULL) {
+        nxt_array_reset(vm->backtrace);
+    }
+
+    vm->retval = njs_value_undefined;
+
+    ret = njs_parser(vm, parser, prev);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        goto fail;
+    }
+
+    parser->lexer = NULL;
+
+    scope = parser->scope;
+
+    ret = njs_variables_scope_reference(vm, scope);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        goto fail;
+    }
+
+    *start = lexer.start;
+
+    /*
+     * Reset the code array to prevent it from being disassembled
+     * again in the next iteration of the accumulative mode.
+     */
+    vm->code = NULL;
+
+    nxt_memzero(&generator, sizeof(njs_generator_t));
+
+    ret = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        goto fail;
+    }
+
+    vm->current = generator.code_start;
+    vm->global_scope = generator.local_scope;
+    vm->scope_size = generator.scope_size;
+
+    vm->variables_hash = scope->variables;
+
+    if (vm->options.init) {
+        ret = njs_vm_init(vm);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+    }
+
+    return NJS_OK;
+
+fail:
+
+    vm->parser = prev;
+
+    return NXT_ERROR;
+}
+
+
+njs_vm_t *
+njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
+{
+    nxt_mp_t     *nmp;
+    njs_vm_t     *nvm;
+    uint32_t     items;
+    nxt_int_t    ret;
+    nxt_array_t  *externals;
+
+    nxt_thread_log_debug("CLONE:");
+
+    if (vm->options.accumulative) {
+        return NULL;
+    }
+
+    nmp = nxt_mp_create(&njs_vm_mp_proto, NULL, NULL, 2 * nxt_pagesize(),
+                        128, 512, 16);
+    if (nxt_slow_path(nmp == NULL)) {
+        return NULL;
+    }
+
+    nvm = nxt_mp_zalign(nmp, sizeof(njs_value_t), sizeof(njs_vm_t));
+
+    if (nxt_fast_path(nvm != NULL)) {
+        nvm->mem_pool = nmp;
+
+        nvm->shared = vm->shared;
+
+        nvm->trace = vm->trace;
+        nvm->trace.data = nvm;
+
+        nvm->variables_hash = vm->variables_hash;
+        nvm->values_hash = vm->values_hash;
+
+        nvm->modules = vm->modules;
+        nvm->modules_hash = vm->modules_hash;
+
+        nvm->externals_hash = vm->externals_hash;
+        nvm->external_prototypes_hash = vm->external_prototypes_hash;
+
+        items = vm->external_objects->items;
+        externals = nxt_array_create(items + 4, sizeof(void *),
+                                     &njs_array_mem_proto, nvm->mem_pool);
+
+        if (nxt_slow_path(externals == NULL)) {
+            return NULL;
+        }
+
+        if (items > 0) {
+            memcpy(externals->start, vm->external_objects->start,
+                   items * sizeof(void *));
+            externals->items = items;
+        }
+
+        nvm->external_objects = externals;
+
+        nvm->options = vm->options;
+
+        nvm->current = vm->current;
+
+        nvm->external = external;
+
+        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;
+        }
+
+        return nvm;
+    }
+
+fail:
+
+    nxt_mp_destroy(nmp);
+
+    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);
+
+    frame = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
+    if (nxt_slow_path(frame == NULL)) {
+        return NXT_ERROR;
+    }
+
+    nxt_memzero(frame, 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;
+
+    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;
+    }
+
+    ret = njs_builtin_objects_clone(vm);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NXT_ERROR;
+    }
+
+    nxt_lvlhsh_init(&vm->events_hash);
+    nxt_queue_init(&vm->posted_events);
+
+    if (vm->debug != NULL) {
+        backtrace = nxt_array_create(4, sizeof(njs_backtrace_entry_t),
+                                     &njs_array_mem_proto, vm->mem_pool);
+        if (nxt_slow_path(backtrace == NULL)) {
+            return NXT_ERROR;
+        }
+
+        vm->backtrace = backtrace;
+    }
+
+    if (njs_is_null(&vm->retval)) {
+        vm->retval = njs_value_undefined;
+    }
+
+    return NXT_OK;
+}
+
+
+nxt_int_t
+njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
+    nxt_uint_t nargs)
+{
+    return njs_vm_invoke(vm, function, args, nargs, (njs_index_t) &vm->retval);
+}
+
+
+nxt_int_t
+njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t retval)
+{
+    njs_ret_t    ret;
+    njs_value_t  *this;
+
+    this = (njs_value_t *) &njs_value_undefined;
+
+    ret = njs_function_frame(vm, function, this, (njs_value_t *) args, nargs,
+                             0);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    return njs_function_frame_invoke(vm, retval);
+}
+
+
 void
 njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
     njs_native_frame_t *previous)
@@ -68,6 +538,511 @@ njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
 }
 
 
+njs_vm_event_t
+njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, nxt_uint_t once,
+    njs_host_event_t host_ev, njs_event_destructor_t destructor)
+{
+    njs_event_t  *event;
+
+    event = nxt_mp_alloc(vm->mem_pool, sizeof(njs_event_t));
+    if (nxt_slow_path(event == NULL)) {
+        return NULL;
+    }
+
+    event->host_event = host_ev;
+    event->destructor = destructor;
+    event->function = function;
+    event->once = once;
+    event->posted = 0;
+    event->nargs = 0;
+    event->args = NULL;
+
+    if (njs_add_event(vm, event) != NJS_OK) {
+        return NULL;
+    }
+
+    return event;
+}
+
+
+void
+njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event)
+{
+    njs_event_t  *event;
+
+    event = (njs_event_t *) vm_event;
+
+    njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
+}
+
+
+nxt_int_t
+njs_vm_waiting(njs_vm_t *vm)
+{
+    return njs_waiting_events(vm);
+}
+
+
+nxt_int_t
+njs_vm_posted(njs_vm_t *vm)
+{
+    return njs_posted_events(vm);
+}
+
+
+nxt_int_t
+njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
+    const njs_value_t *args, nxt_uint_t nargs)
+{
+    njs_event_t  *event;
+
+    event = (njs_event_t *) vm_event;
+
+    if (nargs != 0 && !event->posted) {
+        event->nargs = nargs;
+        event->args = nxt_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
+        if (nxt_slow_path(event->args == NULL)) {
+            return NJS_ERROR;
+        }
+
+        memcpy(event->args, args, sizeof(njs_value_t) * nargs);
+    }
+
+    if (!event->posted) {
+        event->posted = 1;
+        nxt_queue_insert_tail(&vm->posted_events, &event->link);
+    }
+
+    return NJS_OK;
+}
+
+
+nxt_int_t
+njs_vm_run(njs_vm_t *vm)
+{
+    if (nxt_slow_path(vm->backtrace != NULL)) {
+        nxt_array_reset(vm->backtrace);
+    }
+
+    return njs_vm_handle_events(vm);
+}
+
+
+nxt_int_t
+njs_vm_start(njs_vm_t *vm)
+{
+    njs_ret_t  ret;
+
+    ret = njs_module_load(vm);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    ret = njs_vmcode_interpreter(vm);
+
+    if (ret == NJS_STOP) {
+        ret = NJS_OK;
+    }
+
+    return ret;
+}
+
+
+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 ( ;; ) {
+        link = nxt_queue_first(events);
+
+        if (link == nxt_queue_tail(events)) {
+            break;
+        }
+
+        ev = nxt_queue_link_data(link, njs_event_t, link);
+
+        if (ev->once) {
+            njs_del_event(vm, ev, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
+
+        } else {
+            ev->posted = 0;
+            nxt_queue_remove(&ev->link);
+        }
+
+        ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs);
+
+        if (ret == NJS_ERROR) {
+            return ret;
+        }
+    }
+
+    return njs_posted_events(vm) ? NJS_AGAIN : NJS_OK;
+}
+
+
+nxt_int_t
+njs_vm_add_path(njs_vm_t *vm, const nxt_str_t *path)
+{
+    nxt_str_t  *item;
+
+    if (vm->paths == NULL) {
+        vm->paths = nxt_array_create(4, sizeof(nxt_str_t),
+                                     &njs_array_mem_proto, vm->mem_pool);
+        if (nxt_slow_path(vm->paths == NULL)) {
+            return NXT_ERROR;
+        }
+    }
+
+    item = nxt_array_add(vm->paths, &njs_array_mem_proto, vm->mem_pool);
+    if (nxt_slow_path(item == NULL)) {
+        return NXT_ERROR;
+    }
+
+    *item = *path;
+
+    return NXT_OK;
+}
+
+
+njs_value_t *
+njs_vm_retval(njs_vm_t *vm)
+{
+    return &vm->retval;
+}
+
+
+void
+njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value)
+{
+    vm->retval = *value;
+}
+
+
+njs_ret_t
+njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
+    uint32_t size)
+{
+    return njs_string_set(vm, value, start, size);
+}
+
+
+u_char *
+njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size)
+{
+    return njs_string_alloc(vm, value, size, 0);
+}
+
+
+nxt_noinline void
+njs_vm_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...)
+{
+    va_list  args;
+    u_char   buf[NXT_MAX_ERROR_STR], *p;
+
+    p = buf;
+
+    if (fmt != NULL) {
+        va_start(args, fmt);
+        p = nxt_vsprintf(buf, buf + sizeof(buf), fmt, args);
+        va_end(args);
+    }
+
+    njs_error_new(vm, value, NJS_OBJECT_ERROR, buf, p - buf);
+}
+
+
+nxt_noinline void
+njs_vm_memory_error(njs_vm_t *vm)
+{
+    njs_memory_error_set(vm, &vm->retval);
+}
+
+
+nxt_array_t *
+njs_vm_backtrace(njs_vm_t *vm)
+{
+    if (vm->backtrace != NULL && !nxt_array_is_empty(vm->backtrace)) {
+        return vm->backtrace;
+    }
+
+    return NULL;
+}
+
+
+static njs_ret_t
+njs_vm_backtrace_dump(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
+{
+    u_char                 *p, *start, *end;
+    size_t                 len, count;
+    nxt_uint_t             i;
+    nxt_array_t            *backtrace;
+    njs_backtrace_entry_t  *be, *prev;
+
+    backtrace = njs_vm_backtrace(vm);
+
+    len = dst->length + 1;
+
+    count = 0;
+    prev = NULL;
+
+    be = backtrace->start;
+
+    for (i = 0; i < backtrace->items; i++) {
+        if (i != 0 && prev->name.start == be->name.start
+            && prev->line == be->line)
+        {
+            count++;
+
+        } else {
+
+            if (count != 0) {
+                len += nxt_length("      repeats  times\n")
+                       + NXT_INT_T_LEN;
+                count = 0;
+            }
+
+            len += be->name.length + nxt_length("    at  ()\n");
+
+            if (be->line != 0) {
+                len += be->file.length + NXT_INT_T_LEN + 1;
+
+            } else {
+                len += nxt_length("native");
+            }
+        }
+
+        prev = be;
+        be++;
+    }
+
+    p = nxt_mp_alloc(vm->mem_pool, len);
+    if (p == NULL) {
+        njs_memory_error(vm);
+        return NXT_ERROR;
+    }
+
+    start = p;
+    end = start + len;
+
+    p = nxt_cpymem(p, dst->start, dst->length);
+    *p++ = '\n';
+
+    count = 0;
+    prev = NULL;
+
+    be = backtrace->start;
+
+    for (i = 0; i < backtrace->items; i++) {
+        if (i != 0 && prev->name.start == be->name.start
+            && prev->line == be->line)
+        {
+            count++;
+
+        } else {
+            if (count != 0) {
+                p = nxt_sprintf(p, end, "      repeats %uz times\n",
+                                count);
+                count = 0;
+            }
+
+            p = nxt_sprintf(p, end, "    at %V ", &be->name);
+
+            if (be->line != 0) {
+                p = nxt_sprintf(p, end, "(%V:%uD)\n", &be->file,
+                                be->line);
+
+            } else {
+                p = nxt_sprintf(p, end, "(native)\n");
+            }
+        }
+
+        prev = be;
+        be++;
+    }
+
+    dst->start = start;
+    dst->length = p - dst->start;
+
+    return NXT_OK;
+}
+
+
+njs_ret_t
+njs_vm_value_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
+{
+    njs_ret_t   ret;
+    nxt_uint_t  exception;
+
+    if (nxt_slow_path(src->type == NJS_NUMBER
+                      && njs_number(src) == 0
+                      && signbit(njs_number(src))))
+    {
+        njs_string_get(&njs_string_minus_zero, dst);
+        return NXT_OK;
+    }
+
+    exception = 1;
+
+again:
+
+    ret = njs_vm_value_to_string(vm, dst, src);
+
+    if (nxt_fast_path(ret == NXT_OK)) {
+
+        if (njs_vm_backtrace(vm) != NULL) {
+            ret = njs_vm_backtrace_dump(vm, dst, src);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NXT_ERROR;
+            }
+        }
+
+        return NXT_OK;
+    }
+
+    if (exception) {
+        exception = 0;
+
+        /* value evaluation threw an exception. */
+
+        src = &vm->retval;
+        goto again;
+    }
+
+    dst->length = 0;
+    dst->start = NULL;
+
+    return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vm_retval_string(njs_vm_t *vm, nxt_str_t *dst)
+{
+    if (vm->top_frame == NULL) {
+        /* An exception was thrown during compilation. */
+
+        njs_vm_init(vm);
+    }
+
+    return njs_vm_value_string(vm, dst, &vm->retval);
+}
+
+
+njs_ret_t
+njs_vm_retval_dump(njs_vm_t *vm, nxt_str_t *dst, nxt_uint_t indent)
+{
+    if (vm->top_frame == NULL) {
+        /* An exception was thrown during compilation. */
+
+        njs_vm_init(vm);
+    }
+
+    return njs_vm_value_dump(vm, dst, &vm->retval, 0, 1);
+}
+
+
+njs_ret_t
+njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
+{
+    va_list             args;
+    nxt_int_t           ret;
+    njs_ret_t           rc;
+    njs_value_t         *name, *value;
+    njs_object_t        *object;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    object = njs_object_alloc(vm);
+    if (nxt_slow_path(object == NULL)) {
+        return NJS_ERROR;
+    }
+
+    rc = NJS_ERROR;
+
+    va_start(args, retval);
+
+    for ( ;; ) {
+        name = va_arg(args, njs_value_t *);
+        if (name == NULL) {
+            break;
+        }
+
+        value = va_arg(args, njs_value_t *);
+        if (value == NULL) {
+            njs_type_error(vm, "missed value for a key");
+            goto done;
+        }
+
+        if (nxt_slow_path(!njs_is_string(name))) {
+            njs_type_error(vm, "prop name is not a string");
+            goto done;
+        }
+
+        lhq.replace = 0;
+        lhq.pool = vm->mem_pool;
+        lhq.proto = &njs_object_hash_proto;
+
+        njs_string_get(name, &lhq.key);
+        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+
+        prop = njs_object_prop_alloc(vm, name, value, 1);
+        if (nxt_slow_path(prop == NULL)) {
+            goto done;
+        }
+
+        lhq.value = prop;
+
+        ret = nxt_lvlhsh_insert(&object->hash, &lhq);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_internal_error(vm, NULL);
+            goto done;
+        }
+    }
+
+    rc = NJS_OK;
+
+    njs_set_object(retval, object);
+
+done:
+
+    va_end(args);
+
+    return rc;
+}
+
+
+njs_value_t *
+njs_vm_object_prop(njs_vm_t *vm, const 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;
+    }
+
+    lhq.key = *key;
+    lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+    lhq.proto = &njs_object_hash_proto;
+
+    ret = nxt_lvlhsh_find(njs_object_hash(value), &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NULL;
+    }
+
+    prop = lhq.value;
+
+    return &prop->value;
+}
+
+
 njs_ret_t
 njs_vm_value_to_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
 {