NO functional changes.
"
NJS_LIB_SRCS=" \
- njs/njs.c \
njs/njs_value.c \
njs/njs_vm.c \
njs/njs_vmcode.c \
+++ /dev/null
-
-/*
- * 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;
-}
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);
+}
+
+
#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");
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)
}
+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)
{