--- /dev/null
+/*
+ * Copyright (C) 2015 Igor Sysoev
+ * Copyright (C) 2015 NGINX, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
--- /dev/null
+
+NJS_VER = 20150922
+
+NXT_LIB = nxt
+
+include $(NXT_LIB)/Makefile.conf
+
+NXT_BUILDDIR = build
+
+
+$(NXT_BUILDDIR)/libnjs.a: \
+ $(NXT_BUILDDIR)/njscript.o \
+ $(NXT_BUILDDIR)/njs_vm.o \
+ $(NXT_BUILDDIR)/njs_number.o \
+ $(NXT_BUILDDIR)/njs_string.o \
+ $(NXT_BUILDDIR)/njs_object.o \
+ $(NXT_BUILDDIR)/njs_array.o \
+ $(NXT_BUILDDIR)/njs_function.o \
+ $(NXT_BUILDDIR)/njs_regexp.o \
+ $(NXT_BUILDDIR)/njs_variable.o \
+ $(NXT_BUILDDIR)/njs_extern.o \
+ $(NXT_BUILDDIR)/njs_shared.o \
+ $(NXT_BUILDDIR)/njs_lexer.o \
+ $(NXT_BUILDDIR)/njs_lexer_keyword.o \
+ $(NXT_BUILDDIR)/njs_nonrecursive_parser.o \
+ $(NXT_BUILDDIR)/njs_parser.o \
+ $(NXT_BUILDDIR)/njs_parser_expression.o \
+ $(NXT_BUILDDIR)/njs_generator.o \
+ $(NXT_BUILDDIR)/njs_disassembler.o \
+ $(NXT_BUILDDIR)/nxt_djb_hash.o \
+ $(NXT_BUILDDIR)/nxt_utf8.o \
+ $(NXT_BUILDDIR)/nxt_array.o \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_lvlhsh.o \
+ $(NXT_BUILDDIR)/nxt_malloc.o \
+ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
+
+ ar -r -c $(NXT_BUILDDIR)/libnjs.a \
+ $(NXT_BUILDDIR)/njscript.o \
+ $(NXT_BUILDDIR)/njs_vm.o \
+ $(NXT_BUILDDIR)/njs_number.o \
+ $(NXT_BUILDDIR)/njs_string.o \
+ $(NXT_BUILDDIR)/njs_object.o \
+ $(NXT_BUILDDIR)/njs_array.o \
+ $(NXT_BUILDDIR)/njs_function.o \
+ $(NXT_BUILDDIR)/njs_regexp.o \
+ $(NXT_BUILDDIR)/njs_variable.o \
+ $(NXT_BUILDDIR)/njs_extern.o \
+ $(NXT_BUILDDIR)/njs_shared.o \
+ $(NXT_BUILDDIR)/njs_lexer.o \
+ $(NXT_BUILDDIR)/njs_lexer_keyword.o \
+ $(NXT_BUILDDIR)/njs_nonrecursive_parser.o \
+ $(NXT_BUILDDIR)/njs_parser.o \
+ $(NXT_BUILDDIR)/njs_parser_expression.o \
+ $(NXT_BUILDDIR)/njs_generator.o \
+ $(NXT_BUILDDIR)/njs_disassembler.o \
+ $(NXT_BUILDDIR)/nxt_djb_hash.o \
+ $(NXT_BUILDDIR)/nxt_utf8.o \
+ $(NXT_BUILDDIR)/nxt_array.o \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_lvlhsh.o \
+ $(NXT_BUILDDIR)/nxt_malloc.o \
+ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
+
+all: test lib_test
+
+test: \
+ $(NXT_BUILDDIR)/njs_unit_test \
+
+ $(NXT_BUILDDIR)/njs_unit_test
+
+clean:
+ rm -rf $(NXT_BUILDDIR)
+ rm $(NXT_LIB)/Makefile.conf $(NXT_LIB)/nxt_auto_config.h
+
+tarball:
+ make clean
+ mkdir njs-$(NJS_VER)
+ cp -rp configure Makefile LICENSE README $(NXT_LIB) njs nginx \
+ njs-$(NJS_VER)
+ tar czf njs-$(NJS_VER).tar.gz njs-$(NJS_VER)
+ rm -rf njs-$(NJS_VER)
+
+$(NXT_BUILDDIR)/njscript.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njscript.h \
+ njs/njscript.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njscript.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njscript.c
+
+$(NXT_BUILDDIR)/njs_vm.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_parser.h \
+ njs/njs_object_hash.h \
+ njs/njs_vm.h \
+ njs/njs_vm.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_vm.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_vm.c
+
+$(NXT_BUILDDIR)/njs_number.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_number.h \
+ njs/njs_number.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_number.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_number.c
+
+$(NXT_BUILDDIR)/njs_string.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_object_hash.h \
+ njs/njs_string.h \
+ njs/njs_string.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_string.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs $(NXT_PCRE_CFLAGS) \
+ njs/njs_string.c
+
+$(NXT_BUILDDIR)/njs_object.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_object_hash.h \
+ njs/njs_object.h \
+ njs/njs_object.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_object.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_object.c
+
+$(NXT_BUILDDIR)/njs_array.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_object_hash.h \
+ njs/njs_array.h \
+ njs/njs_array.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_array.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_array.c
+
+$(NXT_BUILDDIR)/njs_function.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_function.h \
+ njs/njs_function.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_function.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_function.c
+
+$(NXT_BUILDDIR)/njs_regexp.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_object_hash.h \
+ njs/njs_regexp.h \
+ njs/njs_regexp.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_regexp.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs $(NXT_PCRE_CFLAGS) \
+ njs/njs_regexp.c
+
+$(NXT_BUILDDIR)/njs_variable.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_variable.h \
+ njs/njs_variable.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_variable.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_variable.c
+
+$(NXT_BUILDDIR)/njs_extern.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_extern.h \
+ njs/njs_extern.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_extern.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_extern.c
+
+$(NXT_BUILDDIR)/njs_shared.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_shared.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_shared.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_shared.c
+
+$(NXT_BUILDDIR)/njs_lexer.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_lexer.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_lexer.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_lexer.c
+
+$(NXT_BUILDDIR)/njs_lexer_keyword.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_lexer_keyword.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_lexer_keyword.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_lexer_keyword.c
+
+$(NXT_BUILDDIR)/njs_nonrecursive_parser.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_nonrecursive_parser.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_nonrecursive_parser.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_nonrecursive_parser.c
+
+$(NXT_BUILDDIR)/njs_parser.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_parser.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_parser.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_parser.c \
+
+$(NXT_BUILDDIR)/njs_parser_expression.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_parser_expression.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_parser_expression.o \
+ $(NXT_CFLAGS) -I$(NXT_LIB) -Injs \
+ njs/njs_parser_expression.c
+
+$(NXT_BUILDDIR)/njs_generator.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_generator.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_generator.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_generator.c
+
+$(NXT_BUILDDIR)/njs_disassembler.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_parser.h \
+ njs/njs_disassembler.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_disassembler.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_disassembler.c
+
+$(NXT_BUILDDIR)/njs_unit_test: \
+ $(NXT_BUILDDIR)/libnjs.a \
+ njs/test/njs_unit_test.c \
+
+ $(NXT_CC) -o $(NXT_BUILDDIR)/njs_unit_test $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/test/njs_unit_test.c \
+ $(NXT_BUILDDIR)/libnjs.a -lm $(NXT_PCRE_LIB)
+
+include $(NXT_LIB)/Makefile
--- /dev/null
+
+Configure nginx with HTTP JavaScript module using the --add-module option:
+
+ ./configure --add-module=<path-to-njs>/nginx
+
+Please report your experiences to the NGINX development mailing list
+nginx-devel@nginx.org (http://mailman.nginx.org/mailman/listinfo/nginx-devel).
+
+JavaScript objects
+------------------
+
+$r
+|- uri
+|- method
+|- httpVersion
+|- remoteAddress
+|- headers{}
+|- args{}
+|- response
+ |- status
+ |- headers{}
+ |- contentType
+ |- contentLength
+ |- sendHeader()
+ |- send(data)
+ |- finish()
+
+
+Example
+-------
+
+Create nginx.conf:
+
+ worker_processes 1;
+ pid logs/nginx.pid;
+
+ events {
+ worker_connections 256;
+ }
+
+ http {
+ js_set $summary "
+ var a, s, h;
+
+ s = 'JS summary\n\n';
+
+ s += 'Method: ' + $r.method + '\n';
+ s += 'HTTP version: ' + $r.httpVersion + '\n';
+ s += 'Host: ' + $r.headers.host + '\n';
+ s += 'Remote Address: ' + $r.remoteAddress + '\n';
+ s += 'URI: ' + $r.uri + '\n';
+
+ s += 'Headers:\n';
+ for (h in $r.headers) {
+ s += ' header \"' + h + '\" is \"' + $r.headers[h] + '\"\n';
+ }
+
+ s += 'Args:\n';
+ for (a in $r.args) {
+ s += ' arg \"' + a + '\" is \"' + $r.args[a] + '\"\n';
+ }
+
+ s;
+ ";
+
+ server {
+ listen 8000;
+
+ location / {
+ js_run "
+ var res;
+ res = $r.response;
+ res.headers.foo = 1234;
+ res.status = 302;
+ res.contentType = 'text/plain; charset=utf-8';
+ res.contentLength = 11;
+ res.sendHeader();
+ res.send('nginx');
+ res.send('java');
+ res.send('script');
+ res.finish();
+ ";
+ }
+
+ location /summary {
+ return 200 $summary;
+ }
+ }
+ }
+
+Run nginx & test the output:
+
+$ curl 127.0.0.1:8000
+
+nginxjavascript
+
+$ curl -H "Foo: 1099" '127.0.0.1:8000/summary?a=1&fooo=bar&zyx=xyz'
+
+JS summary
+
+Method: GET
+HTTP version: 1.1
+Host: 127.0.0.1:8000
+Remote Address: 127.0.0.1
+URI: /summary
+Headers:
+ header "Host" is "127.0.0.1:8000"
+ header "User-Agent" is "curl/7.43.0"
+ header "Accept" is "*/*"
+ header "Foo" is "1099"
+Args:
+ arg "a" is "1"
+ arg "fooo" is "bar"
+ arg "zyx" is "xyz"
+
+
+--
+NGINX, Inc., http://nginx.com
--- /dev/null
+#!/bin/sh
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+# Disable localized program messages.
+LANG=C
+export LANG
+
+# Stop on error exit status.
+set -e
+# Stop on uninitialized variable.
+set -u
+
+CC=${CC:-}
+NXT_BUILDDIR=${NXT_BUILDDIR:-build}
+
+test -d $NXT_BUILDDIR || mkdir $NXT_BUILDDIR
+
+cd nxt && NXT_BUILDDIR=../${NXT_BUILDDIR} CC=${CC} ./auto/configure
--- /dev/null
+ngx_addon_name="ngx_http_js_module"
+
+USE_PCRE=YES
+
+HTTP_MODULES="$HTTP_MODULES ngx_http_js_module"
+NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_js_module.c"
+
+LINK_DEPS="$LINK_DEPS $ngx_addon_dir/../build/libnjs.a"
+CORE_LIBS="$CORE_LIBS $ngx_addon_dir/../build/libnjs.a -lm"
+CORE_INCS="$CORE_INCS $ngx_addon_dir/../nxt $ngx_addon_dir/../njs"
--- /dev/null
+cat << END >> $NGX_MAKEFILE
+
+$ngx_addon_dir/../build/libnjs.a:
+ cd $ngx_addon_dir/.. \\
+ && if [ -f nxt/Makefile.conf ]; then \$(MAKE) clean; fi \\
+ && CFLAGS="\$(CFLAGS)" ./configure \\
+ && \$(MAKE)
+
+END
--- /dev/null
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+
+
+#define NGX_HTTP_JS_MCP_CLUSTER_SIZE (2 * ngx_pagesize)
+#define NGX_HTTP_JS_MCP_PAGE_ALIGNMENT 128
+#define NGX_HTTP_JS_MCP_PAGE_SIZE 512
+#define NGX_HTTP_JS_MCP_MIN_CHUNK_SIZE 16
+
+
+#define ngx_http_js_create_mem_cache_pool() \
+ nxt_mem_cache_pool_create(&ngx_http_js_mem_cache_pool_proto, NULL, NULL, \
+ NGX_HTTP_JS_MCP_CLUSTER_SIZE, \
+ NGX_HTTP_JS_MCP_PAGE_ALIGNMENT, \
+ NGX_HTTP_JS_MCP_PAGE_SIZE, \
+ NGX_HTTP_JS_MCP_MIN_CHUNK_SIZE)
+
+
+typedef struct {
+ njs_vm_t *vm;
+} ngx_http_js_loc_conf_t;
+
+
+typedef struct {
+ ngx_list_part_t *part;
+ ngx_uint_t item;
+} ngx_http_js_table_entry_t;
+
+
+static ngx_int_t ngx_http_js_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_js_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void ngx_http_js_cleanup_mem_cache_pool(void *data);
+
+static void *ngx_http_js_alloc(void *mem, size_t size);
+static void *ngx_http_js_calloc(void *mem, size_t size);
+static void *ngx_http_js_memalign(void *mem, size_t alignment, size_t size);
+static void ngx_http_js_free(void *mem, void *p);
+
+static njs_ret_t ngx_http_js_ext_undefined(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_set_string(njs_vm_t *vm, void *obj,
+ uintptr_t data, nxt_str_t *value);
+static njs_ret_t ngx_http_js_ext_each_header_start(njs_vm_t *vm, void *obj,
+ void *each, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_each_header(njs_vm_t *vm, njs_value_t *value,
+ void *obj, void *each);
+static ngx_table_elt_t *ngx_http_js_get_header(ngx_list_part_t *part,
+ u_char *data, size_t len);
+static njs_ret_t ngx_http_js_ext_get_header_out(njs_vm_t *vm,
+ njs_value_t *value, void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj,
+ uintptr_t data, nxt_str_t *value);
+static njs_ret_t ngx_http_js_ext_each_header_out_start(njs_vm_t *vm, void *obj,
+ void *each); /*FIXME*/
+static njs_ret_t ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_set_status(njs_vm_t *vm, void *obj,
+ uintptr_t data, nxt_str_t *value);
+static njs_ret_t ngx_http_js_ext_get_content_length(njs_vm_t *vm,
+ njs_value_t *value, void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_set_content_length(njs_vm_t *vm, void *obj,
+ uintptr_t data, nxt_str_t *value);
+static njs_ret_t ngx_http_js_ext_send_header(njs_vm_t *vm, njs_param_t *param);
+static njs_ret_t ngx_http_js_ext_send(njs_vm_t *vm, njs_param_t *param);
+static njs_ret_t ngx_http_js_ext_finish(njs_vm_t *vm, njs_param_t *param);
+static njs_ret_t ngx_http_js_ext_get_http_version(njs_vm_t *vm,
+ njs_value_t *value, void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_get_remote_address(njs_vm_t *vm,
+ njs_value_t *value, void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_each_header_in_start(njs_vm_t *vm, void *obj,
+ void *each); /*FIXME*/
+static njs_ret_t ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data);
+static njs_ret_t ngx_http_js_ext_each_arg_start(njs_vm_t *vm, void *obj,
+ void *each);
+static njs_ret_t ngx_http_js_ext_each_arg(njs_vm_t *vm, njs_value_t *value,
+ void *obj, void *each);
+
+static char *ngx_http_js_run(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static njs_vm_t *ngx_http_js_compile(ngx_conf_t *cf, ngx_str_t *script);
+static void *ngx_http_js_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_command_t ngx_http_js_commands[] = {
+
+ { ngx_string("js_run"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
+ ngx_http_js_run,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("js_set"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
+ ngx_http_js_set,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_js_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_js_create_loc_conf, /* create location configuration */
+ ngx_http_js_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_js_module = {
+ NGX_MODULE_V1,
+ &ngx_http_js_module_ctx, /* module context */
+ ngx_http_js_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static const nxt_mem_proto_t ngx_http_js_mem_cache_pool_proto = {
+ ngx_http_js_alloc,
+ ngx_http_js_calloc,
+ ngx_http_js_memalign,
+ NULL,
+ ngx_http_js_free,
+ NULL,
+ NULL,
+};
+
+
+static njs_external_t ngx_http_js_ext_response[] = {
+
+ { nxt_string("headers"),
+ NJS_EXTERN_OBJECT,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_header_out,
+ ngx_http_js_ext_set_header_out,
+ NULL,
+ ngx_http_js_ext_each_header_out_start,
+ ngx_http_js_ext_each_header,
+ NULL,
+ 0 },
+
+ { nxt_string("status"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_status,
+ ngx_http_js_ext_set_status,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ offsetof(ngx_http_request_t, headers_out.status) },
+
+ { nxt_string("contentType"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_string,
+ ngx_http_js_ext_set_string,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ offsetof(ngx_http_request_t, headers_out.content_type) },
+
+ { nxt_string("contentLength"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_content_length,
+ ngx_http_js_ext_set_content_length,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("sendHeader"),
+ NJS_EXTERN_METHOD,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ngx_http_js_ext_send_header,
+ 0 },
+
+ { nxt_string("send"),
+ NJS_EXTERN_METHOD,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ngx_http_js_ext_send,
+ 0 },
+
+ { nxt_string("finish"),
+ NJS_EXTERN_METHOD,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ngx_http_js_ext_finish,
+ 0 },
+};
+
+
+static njs_external_t ngx_http_js_ext_request[] = {
+
+ { nxt_string("response"),
+ NJS_EXTERN_OBJECT,
+ ngx_http_js_ext_response,
+ nxt_nitems(ngx_http_js_ext_response),
+ ngx_http_js_ext_undefined,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("uri"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_string,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ offsetof(ngx_http_request_t, uri) },
+
+ { nxt_string("method"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_string,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ offsetof(ngx_http_request_t, method_name) },
+
+ { nxt_string("httpVersion"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_http_version,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("remoteAddress"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_remote_address,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("headers"),
+ NJS_EXTERN_OBJECT,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_header_in,
+ NULL,
+ NULL,
+ ngx_http_js_ext_each_header_in_start,
+ ngx_http_js_ext_each_header,
+ NULL,
+ 0 },
+
+ { nxt_string("args"),
+ NJS_EXTERN_OBJECT,
+ NULL,
+ 0,
+ ngx_http_js_ext_get_arg,
+ NULL,
+ NULL,
+ ngx_http_js_ext_each_arg_start,
+ ngx_http_js_ext_each_arg,
+ NULL,
+ 0 },
+};
+
+
+static njs_external_t ngx_http_js_externals[] = {
+
+ { nxt_string("$r"),
+ NJS_EXTERN_OBJECT,
+ ngx_http_js_ext_request,
+ nxt_nitems(ngx_http_js_ext_request),
+ ngx_http_js_ext_undefined,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+};
+
+
+static ngx_int_t
+ngx_http_js_handler(ngx_http_request_t *r)
+{
+ nxt_str_t value;
+ njs_vm_t *nvm;
+ ngx_pool_cleanup_t *cln;
+ nxt_mem_cache_pool_t *mcp;
+ ngx_http_js_loc_conf_t *jlcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js handler");
+
+ mcp = ngx_http_js_create_mem_cache_pool();
+ if (mcp == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(r->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_js_cleanup_mem_cache_pool;
+ cln->data = mcp;
+
+ jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module);
+
+ nvm = njs_vm_clone(jlcf->vm, mcp, (void **) &r);
+ if (nvm == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (njs_vm_run(nvm) != NJS_OK) {
+ njs_vm_exception(nvm, &value);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "js exception: %*s", value.len, value.data);
+
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_js_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ njs_vm_t *vm = (njs_vm_t *) data;
+
+ nxt_str_t value;
+ njs_vm_t *nvm;
+ ngx_pool_cleanup_t *cln;
+ nxt_mem_cache_pool_t *mcp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js variable handler");
+
+ mcp = ngx_http_js_create_mem_cache_pool();
+ if (mcp == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(r->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_js_cleanup_mem_cache_pool;
+ cln->data = mcp;
+
+ nvm = njs_vm_clone(vm, mcp, (void **) &r);
+ if (nvm == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (njs_vm_run(nvm) == NJS_OK) {
+ if (njs_vm_retval(nvm, &value) != NJS_OK) {
+ return NGX_ERROR;
+ }
+
+ v->len = value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = value.data;
+
+ } else {
+ njs_vm_exception(nvm, &value);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "js exception: %*s", value.len, value.data);
+
+ v->not_found = 1;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js variable done");
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_js_cleanup_mem_cache_pool(void *data)
+{
+ nxt_mem_cache_pool_t *mcp = data;
+
+ nxt_mem_cache_pool_destroy(mcp);
+}
+
+
+static void *
+ngx_http_js_alloc(void *mem, size_t size)
+{
+ return ngx_alloc(size, ngx_cycle->log);
+}
+
+
+static void *
+ngx_http_js_calloc(void *mem, size_t size)
+{
+ return ngx_calloc(size, ngx_cycle->log);
+}
+
+
+static void *
+ngx_http_js_memalign(void *mem, size_t alignment, size_t size)
+{
+ return ngx_memalign(alignment, size, ngx_cycle->log);
+}
+
+
+static void
+ngx_http_js_free(void *mem, void *p)
+{
+ ngx_free(p);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_undefined(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ njs_void_set(value);
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ char *p = obj;
+
+ ngx_str_t *field;
+
+ field = (ngx_str_t *) (p + data);
+
+ return njs_string_create(vm, value, field->data, field->len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_set_string(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_str_t *value)
+{
+ char *p = obj;
+
+ ngx_str_t *field;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+
+ field = (ngx_str_t *) (p + data);
+ field->len = value->len;
+
+ field->data = ngx_pnalloc(r->pool, value->len);
+ if (field->data == NULL) {
+ return NJS_ERROR;
+ }
+
+ ngx_memcpy(field->data, value->data, value->len);
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_each_header_start(njs_vm_t *vm, void *obj, void *each,
+ uintptr_t data)
+{
+ char *p = obj;
+
+ ngx_list_t *headers;
+ ngx_http_request_t *r;
+ ngx_http_js_table_entry_t *entry, **e;
+
+ r = (ngx_http_request_t *) obj;
+
+ entry = ngx_palloc(r->pool, sizeof(ngx_http_js_table_entry_t));
+ if (entry == NULL) {
+ return NJS_ERROR;
+ }
+
+ headers = (ngx_list_t *) (p + data);
+
+ entry->part = &headers->part;
+ entry->item = 0;
+
+ e = (ngx_http_js_table_entry_t **) each;
+ *e = entry;
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_each_header(njs_vm_t *vm, njs_value_t *value, void *obj,
+ void *each)
+{
+ ngx_http_js_table_entry_t **e = each;
+
+ ngx_table_elt_t *header, *h;
+ ngx_http_js_table_entry_t *entry;
+
+ entry = *e;
+
+ while (entry->part) {
+
+ if (entry->item >= entry->part->nelts) {
+ entry->part = entry->part->next;
+ entry->item = 0;
+ continue;
+ }
+
+ header = entry->part->elts;
+ h = &header[entry->item++];
+
+ return njs_string_create(vm, value, h->key.data, h->key.len, 0);
+ }
+
+ return NJS_DONE;
+}
+
+
+static ngx_table_elt_t *
+ngx_http_js_get_header(ngx_list_part_t *part, u_char *data, size_t len)
+{
+ ngx_uint_t i;
+ ngx_table_elt_t *header, *h;
+
+ header = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ h = &header[i];
+
+ if (h->hash == 0) {
+ continue;
+ }
+
+ if (h->key.len == len && ngx_strncasecmp(h->key.data, data, len) == 0) {
+ return h;
+ }
+ }
+
+ return NULL;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_header_out(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ nxt_str_t *v;
+ ngx_table_elt_t *h;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+ v = (nxt_str_t *) data;
+
+ h = ngx_http_js_get_header(&r->headers_out.headers.part, v->data, v->len);
+ if (h == NULL) {
+ return njs_string_create(vm, value, NULL, 0, 0);
+ }
+
+ return njs_string_create(vm, value, h->value.data, h->value.len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_set_header_out(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_str_t *value)
+{
+ u_char *p;
+ nxt_str_t *v;
+ ngx_table_elt_t *h;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+ v = (nxt_str_t *) data;
+
+ h = ngx_http_js_get_header(&r->headers_out.headers.part, v->data, v->len);
+
+ if (h == NULL || h->hash == 0) {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NJS_ERROR;
+ }
+
+ p = ngx_pnalloc(r->pool, v->len);
+ if (p == NULL) {
+ return NJS_ERROR;
+ }
+
+ ngx_memcpy(p, v->data, v->len);
+
+ h->key.data = p;
+ h->key.len = v->len;
+ h->hash = 1;
+ }
+
+
+ p = ngx_pnalloc(r->pool, value->len);
+ if (p == NULL) {
+ return NJS_ERROR;
+ }
+
+ ngx_memcpy(p, value->data, value->len);
+
+ h->value.data = p;
+ h->value.len = value->len;
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_each_header_out_start(njs_vm_t *vm, void *obj, void *each)
+{
+ return ngx_http_js_ext_each_header_start(vm, obj, each,
+ offsetof(ngx_http_request_t, headers_out.headers));
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ size_t len;
+ u_char *p;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+
+ p = ngx_pnalloc(r->pool, 3);
+ if (p == NULL) {
+ return NJS_ERROR;
+ }
+
+ len = ngx_snprintf(p, 3, "%ui", r->headers_out.status) - p;
+
+ return njs_string_create(vm, value, p, len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_set_status(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_str_t *value)
+{
+ ngx_int_t n;
+ ngx_http_request_t *r;
+
+ n = ngx_atoi(value->data, value->len);
+ if (n == NGX_ERROR) {
+ return NJS_ERROR;
+ }
+
+ r = (ngx_http_request_t *) obj;
+
+ r->headers_out.status = n;
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_content_length(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ size_t len;
+ u_char *p;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NJS_ERROR;
+ }
+
+ len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;
+
+ return njs_string_create(vm, value, p, len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_set_content_length(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_str_t *value)
+{
+ ngx_int_t n;
+ ngx_http_request_t *r;
+
+ n = ngx_atoi(value->data, value->len);
+ if (n == NGX_ERROR) {
+ return NJS_ERROR;
+ }
+
+ r = (ngx_http_request_t *) obj;
+
+ r->headers_out.content_length_n = n;
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_send_header(njs_vm_t *vm, njs_param_t *param)
+{
+ ngx_http_request_t *r;
+
+ r = njs_value_data(param->object);
+
+ if (ngx_http_send_header(r) == NGX_ERROR) {
+ return NJS_ERROR;
+ }
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_send(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_int_t ret;
+ nxt_str_t s;
+ ngx_buf_t *b;
+ uintptr_t nargs, next;
+ ngx_uint_t n;
+ njs_value_t *args;
+ ngx_chain_t *out, *cl, **ll;
+ ngx_http_request_t *r;
+
+ r = njs_value_data(param->object);
+
+ out = NULL;
+ ll = &out;
+
+ args = param->args;
+ nargs = param->nargs;
+
+ for (n = 0; n < nargs; n++) {
+ next = 0;
+
+ for ( ;; ) {
+ ret = njs_value_string_copy(vm, &s, njs_argument(args, n), &next);
+
+ if (ret == NJS_DECLINED) {
+ break;
+ }
+
+ if (ret == NJS_ERROR) {
+ return NJS_ERROR;
+ }
+
+ /* TODO: njs_value_release(vm, value) in buf completion */
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http js send: \"%*s\"", s.len, s.data);
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NJS_ERROR;
+ }
+
+ b->start = s.data;
+ b->pos = b->start;
+ b->end = s.data + s.len;
+ b->last = b->end;
+ b->memory = 1;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NJS_ERROR;
+ }
+
+ cl->buf = b;
+
+ *ll = cl;
+ ll = &cl->next;
+ }
+ }
+
+ *ll = NULL;
+
+ if (ngx_http_output_filter(r, out) == NGX_ERROR) {
+ return NJS_ERROR;
+ }
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_finish(njs_vm_t *vm, njs_param_t *param)
+{
+ ngx_http_request_t *r;
+
+ r = njs_value_data(param->object);
+
+ if (ngx_http_send_special(r, NGX_HTTP_LAST) == NGX_ERROR) {
+ return NJS_ERROR;
+ }
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ ngx_str_t v;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+
+ switch (r->http_version) {
+
+ case NGX_HTTP_VERSION_9:
+ ngx_str_set(&v, "0.9");
+ break;
+
+ case NGX_HTTP_VERSION_10:
+ ngx_str_set(&v, "1.0");
+ break;
+
+ default: /* NGX_HTTP_VERSION_11 */
+ ngx_str_set(&v, "1.1");
+ break;
+ }
+
+ return njs_string_create(vm, value, v.data, v.len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_remote_address(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+ c = r->connection;
+
+ return njs_string_create(vm, value, c->addr_text.data, c->addr_text.len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_get_header_in(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ nxt_str_t *v;
+ ngx_table_elt_t *h;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+ v = (nxt_str_t *) data;
+
+ h = ngx_http_js_get_header(&r->headers_in.headers.part, v->data, v->len);
+ if (h == NULL) {
+ return njs_string_create(vm, value, NULL, 0, 0);
+ }
+
+ return njs_string_create(vm, value, h->value.data, h->value.len, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_each_header_in_start(njs_vm_t *vm, void *obj, void *each)
+{
+ return ngx_http_js_ext_each_header_start(vm, obj, each,
+ offsetof(ngx_http_request_t, headers_in.headers));
+}
+
+static njs_ret_t
+ngx_http_js_ext_get_arg(njs_vm_t *vm, njs_value_t *value, void *obj,
+ uintptr_t data)
+{
+ nxt_str_t *v;
+ ngx_str_t arg;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+ v = (nxt_str_t *) data;
+
+ if (ngx_http_arg(r, v->data, v->len, &arg) == NGX_OK) {
+ return njs_string_create(vm, value, arg.data, arg.len, 0);
+ }
+
+ return njs_string_create(vm, value, NULL, 0, 0);
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_each_arg_start(njs_vm_t *vm, void *obj, void *each)
+{
+ ngx_str_t *entry, **e;
+ ngx_http_request_t *r;
+
+ r = (ngx_http_request_t *) obj;
+
+ entry = ngx_palloc(r->pool, sizeof(ngx_str_t));
+ if (entry == NULL) {
+ return NJS_ERROR;
+ }
+
+ *entry = r->args;
+
+ e = (ngx_str_t **) each;
+ *e = entry;
+
+ return NJS_OK;
+}
+
+
+static njs_ret_t
+ngx_http_js_ext_each_arg(njs_vm_t *vm, njs_value_t *value, void *obj,
+ void *each)
+{
+ ngx_str_t **e = each;
+
+ size_t len;
+ u_char *p, *start, *end;
+ ngx_str_t *entry;
+
+ entry = *e;
+
+ if (entry->len == 0) {
+ return NJS_DONE;
+ }
+
+ start = entry->data;
+ end = start + entry->len;
+
+ p = ngx_strlchr(start, end, '=');
+ if (p == NULL) {
+ return NJS_ERROR;
+ }
+
+ len = p - start;
+ p++;
+
+ p = ngx_strlchr(p, end, '&');
+
+ if (p) {
+ entry->data = &p[1];
+ entry->len = end - entry->data;
+
+ } else {
+ entry->len = 0;
+ }
+
+ return njs_string_create(vm, value, start, len, 0);
+}
+
+
+static char *
+ngx_http_js_run(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_js_loc_conf_t *jlcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_core_loc_conf_t *clcf;
+
+ value = cf->args->elts;
+
+ if (jlcf->vm) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate js handler \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ jlcf->vm = ngx_http_js_compile(cf, &value[1]);
+ if (jlcf->vm == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_js_handler;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ njs_vm_t *vm;
+ ngx_str_t *value;
+ ngx_http_variable_t *v;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ vm = ngx_http_js_compile(cf, &value[2]);
+ if (vm == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ v->get_handler = ngx_http_js_variable;
+ v->data = (uintptr_t) vm;
+
+ return NGX_CONF_OK;
+}
+
+
+static njs_vm_t *
+ngx_http_js_compile(ngx_conf_t *cf, ngx_str_t *script)
+{
+ u_char *start, *end;
+ nxt_int_t rc;
+ nxt_str_t s;
+ njs_vm_t *vm;
+ nxt_lvlhsh_t externals;
+ njs_vm_shared_t *shared;
+ nxt_mem_cache_pool_t *mcp;
+
+ mcp = ngx_http_js_create_mem_cache_pool();
+ if (mcp == NULL) {
+ return NULL;
+ }
+
+ shared = NULL;
+
+ nxt_lvlhsh_init(&externals);
+
+ if (njs_add_external(&externals, mcp, 0, ngx_http_js_externals,
+ nxt_nitems(ngx_http_js_externals))
+ != NJS_OK)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "could not add js externals");
+ return NULL;
+ }
+
+ vm = njs_vm_create(mcp, &shared, &externals);
+ if (vm == NULL) {
+ return NULL;
+ }
+
+ start = script->data;
+ end = start + script->len;
+
+ rc = njs_vm_compile(vm, &start, end);
+
+ if (rc != NJS_OK) {
+ njs_vm_exception(vm, &s);
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "js compilation error: \"%*s\"", s.len, s.data);
+ return NULL;
+ }
+
+ if (start != end) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "extra characters in js script: \"%*s\"",
+ end - start, start);
+ return NULL;
+ }
+
+ return vm;
+}
+
+
+static void *
+ngx_http_js_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_js_loc_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_js_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->vm = NULL;
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_http_js_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_js_loc_conf_t *prev = parent;
+ ngx_http_js_loc_conf_t *conf = child;
+
+ if (conf->vm == NULL) {
+ conf->vm = prev->vm;
+ }
+
+ return NGX_CONF_OK;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <string.h>
+
+
+typedef struct {
+ njs_value_t retval;
+ int32_t index;
+ uint32_t length;
+} njs_array_each_t;
+
+
+static nxt_noinline njs_value_t *njs_array_copy(njs_value_t *dst,
+ njs_value_t *src);
+static nxt_int_t njs_array_next(njs_value_t *value, nxt_uint_t n,
+ nxt_uint_t length);
+
+
+njs_value_t *
+njs_array_add(njs_vm_t *vm, njs_value_t *value, u_char *start, size_t size)
+{
+ njs_ret_t ret;
+ njs_array_t *array;
+
+ if (value != NULL) {
+ array = value->data.u.array;
+
+ if (array->size == array->length) {
+ ret = njs_array_realloc(vm, array, 0, array->size + 1);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
+ }
+ }
+
+ } else {
+ 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;
+ }
+
+ array = njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
+ if (nxt_slow_path(array == NULL)) {
+ return NULL;
+ }
+
+ value->data.u.array = array;
+ value->type = NJS_ARRAY;
+ value->data.truth = 1;
+ }
+
+ ret = njs_string_create(vm, &array->start[array->length++], start, size, 0);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return value;
+ }
+
+ return NULL;
+}
+
+
+nxt_noinline njs_array_t *
+njs_array_alloc(njs_vm_t *vm, uint32_t length, uint32_t spare)
+{
+ uint32_t size;
+ njs_array_t *array;
+
+ array = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_array_t));
+
+ if (nxt_slow_path(array == NULL)) {
+ return NULL;
+ }
+
+ size = length + spare;
+
+ array->data = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ size * sizeof(njs_value_t));
+ if (nxt_slow_path(array->data == NULL)) {
+ return NULL;
+ }
+
+ array->start = array->data;
+ nxt_lvlhsh_init(&array->object.hash);
+ nxt_lvlhsh_init(&array->object.shared_hash);
+ array->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_ARRAY];
+ array->size = size;
+ array->length = length;
+
+ return array;
+}
+
+
+njs_ret_t
+njs_array_realloc(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
+ uint32_t size)
+{
+ nxt_uint_t n;
+ njs_value_t *value;
+
+ if (size != array->size) {
+ if (size < 16) {
+ size *= 2;
+
+ } else {
+ size += size / 2;
+ }
+ }
+
+ value = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ (prepend + size) * sizeof(njs_value_t));
+ if (nxt_slow_path(value == NULL)) {
+ return NXT_ERROR;
+ }
+
+ /* GC: old = array->data */
+
+ array->data = value;
+
+ while (prepend != 0) {
+ njs_set_invalid(value);
+ value++;
+ prepend--;
+ }
+
+ memcpy(value, array->start, array->size * sizeof(njs_value_t));
+
+ array->start = value;
+ n = array->size;
+ array->size = size;
+
+ value += n;
+ size -= n;
+
+ while (size != 0) {
+ njs_set_invalid(value);
+ value++;
+ size--;
+ }
+
+ /* GC: free old pointer. */
+
+ return NXT_OK;
+}
+
+
+njs_ret_t
+njs_array_function(njs_vm_t *vm, njs_param_t *param)
+{
+ double num;
+ uint32_t size;
+ njs_value_t *value, *args;
+ njs_array_t *array;
+
+ args = param->args;
+ size = param->nargs;
+
+ if (size == 1 && njs_is_number(&args[0])) {
+ num = args[0].data.u.number;
+ size = (uint32_t) num;
+
+ if ((double) size != num) {
+ vm->exception = &njs_exception_range_error;
+ return NXT_ERROR;
+ }
+
+ args = NULL;
+ }
+
+ array = njs_array_alloc(vm, size, NJS_ARRAY_SPARE);
+
+ if (nxt_fast_path(array != NULL)) {
+
+ vm->retval.data.u.array = array;
+ value = array->start;
+
+ if (args == NULL) {
+ while (size != 0) {
+ njs_set_invalid(value);
+ value++;
+ size--;
+ }
+
+ } else {
+ while (size != 0) {
+ njs_retain(args);
+ *value++ = *args++;
+ size--;
+ }
+ }
+
+ vm->retval.type = NJS_ARRAY;
+ vm->retval.data.truth = 1;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static const njs_object_prop_t njs_array_function_properties[] =
+{
+ { njs_string("Array"),
+ njs_string("name"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_value(NJS_NUMBER, 1, 1.0),
+ njs_string("length"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_prototype),
+ njs_string("prototype"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_array_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_array_function_properties,
+ nxt_nitems(njs_array_function_properties));
+}
+
+
+static njs_ret_t
+njs_array_prototype_length(njs_vm_t *vm, njs_value_t *array)
+{
+ njs_number_set(&vm->retval, array->data.u.array->length);
+
+ njs_release(vm, array);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_slice(njs_vm_t *vm, njs_param_t *param)
+{
+ int32_t start, end, length;
+ uint32_t n;
+ uintptr_t nargs;
+ njs_array_t *array;
+ njs_value_t *object, *args, *value;
+
+ start = 0;
+ length = 0;
+ object = param->object;
+
+ if (njs_is_array(object)) {
+ length = object->data.u.array->length;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ args = param->args;
+ start = njs_value_to_number(&args[0]);
+
+ if (start < 0) {
+ start += length;
+
+ if (start < 0) {
+ start = 0;
+ }
+ }
+
+ end = length;
+
+ if (nargs > 1) {
+ end = njs_value_to_number(&args[1]);
+
+ if (end < 0) {
+ end += length;
+ }
+ }
+
+ length = end - start;
+
+ if (length < 0) {
+ start = 0;
+ length = 0;
+ }
+ }
+ }
+
+ array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
+ if (nxt_slow_path(array == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.array = array;
+ vm->retval.type = NJS_ARRAY;
+ vm->retval.data.truth = 1;
+
+ if (length != 0) {
+ value = object->data.u.array->start;
+ n = 0;
+
+ do {
+ /* GC: retain long string and object in values[start]. */
+ array->start[n++] = value[start++];
+ length--;
+ } while (length != 0);
+ }
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_push(njs_vm_t *vm, njs_param_t *param)
+{
+ uintptr_t i, nargs;
+ njs_ret_t ret;
+ njs_value_t *args;
+ njs_array_t *array;
+
+ if (njs_is_array(param->object)) {
+ array = param->object->data.u.array;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ if (nargs > array->size - array->length) {
+ ret = njs_array_realloc(vm, array, 0, array->size + nargs);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ args = param->args;
+
+ for (i = 0; i < nargs; i++) {
+ /* GC: njs_retain(&args[i]); */
+ array->start[array->length++] = args[i];
+ }
+ }
+
+ njs_number_set(&vm->retval, array->length);
+ }
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_pop(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_array_t *array;
+ const njs_value_t *retval, *value;
+
+ retval = &njs_value_void;
+
+ if (njs_is_array(param->object)) {
+ array = param->object->data.u.array;
+
+ if (array->length != 0) {
+ array->length--;
+ value = &array->start[array->length];
+
+ if (njs_is_valid(value)) {
+ retval = value;
+ }
+ }
+ }
+
+ vm->retval = *retval;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_unshift(njs_vm_t *vm, njs_param_t *param)
+{
+ uintptr_t nargs;
+ njs_ret_t ret;
+ njs_value_t *args;
+ njs_array_t *array;
+
+ if (njs_is_array(param->object)) {
+ array = param->object->data.u.array;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ if ((intptr_t) nargs > (array->start - array->data)) {
+ ret = njs_array_realloc(vm, array, nargs, array->size);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ array->length += nargs;
+ args = param->args;
+
+ do {
+ nargs--;
+ /* GC: njs_retain(&args[nargs]); */
+ array->start--;
+ array->start[0] = args[nargs];
+ } while (nargs != 0);
+ }
+
+ njs_number_set(&vm->retval, array->length);
+ }
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_shift(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_array_t *array;
+ const njs_value_t *retval, *value;
+
+ retval = &njs_value_void;
+
+ if (njs_is_array(param->object)) {
+ array = param->object->data.u.array;
+
+ if (array->length != 0) {
+ array->length--;
+
+ value = &array->start[0];
+ array->start++;
+
+ if (njs_is_valid(value)) {
+ retval = value;
+ }
+ }
+ }
+
+ vm->retval = *retval;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_to_string(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_object_t *object;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = NJS_JOIN_HASH;
+ lhq.key.len = sizeof("join") - 1;
+ lhq.key.data = (u_char *) "join";
+
+ object = param->object->data.u.object;
+
+ prop = njs_object_property(vm, object, &lhq);
+
+ if (nxt_fast_path(prop != NULL
+ && (njs_is_function(&prop->value)
+ || njs_is_native(&prop->value))))
+ {
+ return njs_function_apply(vm, &prop->value, param);
+ }
+
+ lhq.key_hash = NJS_TO_STRING_HASH;
+ lhq.key.len = sizeof("toString") - 1;
+ lhq.key.data = (u_char *) "toString";
+
+ object = &vm->prototypes[NJS_PROTOTYPE_OBJECT];
+
+ prop = njs_object_property(vm, object, &lhq);
+
+ if (nxt_fast_path(prop != NULL)) {
+ return njs_function_apply(vm, &prop->value, param);
+ }
+
+ return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_array_prototype_join(njs_vm_t *vm, njs_param_t *param)
+{
+ u_char *p;
+ size_t size, length;
+ nxt_int_t ret;
+ nxt_uint_t i, n, max;
+ njs_array_t *array;
+ njs_value_t *value, *values;
+ njs_string_prop_t separator, string;
+
+ if (!njs_is_array(param->object)) {
+ goto empty;
+ }
+
+ array = param->object->data.u.array;
+
+ if (array->length == 0) {
+ goto empty;
+ }
+
+ if (param->nargs != 0) {
+ value = ¶m->args[0];
+
+ } else {
+ value = (njs_value_t *) &njs_string_comma;
+ }
+
+ (void) njs_string_prop(&separator, value);
+
+ max = 0;
+
+ for (i = 0; i < array->length; i++) {
+ value = &array->start[i];
+ if (njs_is_valid(value) && !njs_is_string(value)) {
+ max++;
+ }
+ }
+
+ values = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_value_t) * max);
+ if (nxt_slow_path(values == NULL)) {
+ return NXT_ERROR;
+ }
+
+ size = separator.size * (array->length - 1);
+ length = separator.length * (array->length - 1);
+ n = 0;
+
+ for (i = 0; i < array->length; i++) {
+ value = &array->start[i];
+
+ if (njs_is_valid(value)) {
+
+ if (!njs_is_string(value)) {
+ ret = njs_value_to_string(vm, &values[n], value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ value = &values[n++];
+ }
+
+ (void) njs_string_prop(&string, value);
+
+ size += string.size;
+ length += string.length;
+ }
+ }
+
+ p = njs_string_alloc(vm, &vm->retval, size, length);
+ if (nxt_slow_path(p == NULL)) {
+ return NXT_ERROR;
+ }
+
+ n = 0;
+
+ for (i = 0; i < array->length; i++) {
+ value = &array->start[i];
+
+ if (njs_is_valid(value)) {
+ if (!njs_is_string(value)) {
+ value = &values[n++];
+ }
+
+ (void) njs_string_prop(&string, value);
+
+ p = memcpy(p, string.start, string.size);
+ p += string.size;
+ }
+
+ if (i < array->length - 1) {
+ p = memcpy(p, separator.start, separator.size);
+ p += separator.size;
+ }
+ }
+
+ for (i = 0; i < max; i++) {
+ njs_release(vm, &values[i]);
+ }
+
+ nxt_mem_cache_free(vm->mem_cache_pool, values);
+
+ return NXT_OK;
+
+empty:
+
+ vm->retval = njs_string_empty;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_array_prototype_concat(njs_vm_t *vm, njs_param_t *param)
+{
+ size_t length;
+ uintptr_t nargs;
+ nxt_uint_t i;
+ njs_value_t *object, *args, *value;
+ njs_array_t *array;
+
+ object = param->object;
+
+ if (njs_is_array(object)) {
+ length = object->data.u.array->length;
+
+ } else {
+ length = 1;
+ }
+
+ nargs = param->nargs;
+ args = param->args;
+
+ for (i = 0; i < nargs; i++) {
+ if (njs_is_array(&args[i])) {
+ length += args[i].data.u.array->length;
+
+ } else {
+ length++;
+ }
+ }
+
+ array = njs_array_alloc(vm, length, NJS_ARRAY_SPARE);
+ if (nxt_slow_path(array == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.array = array;
+ vm->retval.type = NJS_ARRAY;
+ vm->retval.data.truth = 1;
+
+ value = njs_array_copy(array->start, object);
+
+ for (i = 0; i < nargs; i++) {
+ value = njs_array_copy(value, &args[i]);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_noinline njs_value_t *
+njs_array_copy(njs_value_t *dst, njs_value_t *src)
+{
+ nxt_uint_t n;
+
+ n = 1;
+
+ if (njs_is_array(src)) {
+ n = src->data.u.array->length;
+ src = src->data.u.array->start;
+ }
+
+ while (n != 0) {
+ /* GC: njs_retain src */
+ *dst++ = *src++;
+ n--;
+ }
+
+ return dst;
+}
+
+
+static njs_ret_t
+njs_array_prototype_for_each(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_int_t n;
+ uintptr_t nargs;
+ njs_param_t p;
+ njs_array_t *array;
+ njs_value_t *object, *args, *func, arguments[3];
+ njs_array_each_t *each;
+
+ object = param->object;
+
+ if (!vm->frame->reentrant) {
+ vm->frame->reentrant = 1;
+
+ if (!njs_is_array(object)) {
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+
+ array = object->data.u.array;
+ n = njs_array_next(array->start, 0, array->length);
+
+ if (n < 0) {
+ vm->retval = njs_value_void;
+ return NXT_OK;
+ }
+
+ each = njs_native_data(vm->frame);
+ each->index = n;
+ each->length = array->length;
+ }
+
+ each = njs_native_data(vm->frame);
+ n = each->index;
+
+ /* GC: array elt, array */
+ array = object->data.u.array;
+ arguments[0] = array->start[n];
+ njs_number_set(&arguments[1], n);
+ arguments[2] = *object;
+
+ n = njs_array_next(array->start, ++n, each->length);
+ each->index = n;
+
+ if (n > 0) {
+ vm->current -= sizeof(njs_vmcode_call_t);
+ }
+
+ nargs = param->nargs;
+ args = param->args;
+
+ p.object = (nargs > 1) ? &args[1] : (njs_value_t *) &njs_value_void;
+ p.args = arguments;
+ p.nargs = 3;
+ p.retval = (njs_index_t) &each->retval;
+
+ func = (nargs != 0) ? &args[0] : (njs_value_t *) &njs_value_void;
+
+ return njs_function_apply(vm, func, &p);
+}
+
+
+static njs_ret_t
+njs_array_prototype_some(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_int_t n;
+ uintptr_t nargs;
+ njs_param_t p;
+ njs_array_t *array;
+ njs_value_t *object, *args, *func, arguments[3];
+ njs_array_each_t *each;
+
+ object = param->object;
+
+ if (!vm->frame->reentrant) {
+ vm->frame->reentrant = 1;
+
+ if (!njs_is_array(object)) {
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+
+ array = object->data.u.array;
+ n = njs_array_next(array->start, 0, array->length);
+ each = njs_native_data(vm->frame);
+ each->index = n;
+ each->length = array->length;
+
+ } else {
+ each = njs_native_data(vm->frame);
+
+ if (njs_is_true(&each->retval)) {
+ vm->retval = njs_value_true;
+ return NXT_OK;
+ }
+ }
+
+ n = each->index;
+
+ if (n < 0) {
+ vm->retval = njs_value_false;
+ return NXT_OK;
+ }
+
+ /* GC: array elt, array */
+ array = object->data.u.array;
+ arguments[0] = array->start[n];
+ njs_number_set(&arguments[1], n);
+ arguments[2] = *object;
+
+ each->index = njs_array_next(array->start, ++n, each->length);
+
+ nargs = param->nargs;
+ args = param->args;
+
+ p.object = (nargs > 1) ? &args[1] : (njs_value_t *) &njs_value_void;
+ p.args = arguments;
+ p.nargs = 3;
+ p.retval = (njs_index_t) &each->retval;
+
+ func = (nargs != 0) ? &args[0] : (njs_value_t *) &njs_value_void;
+
+ vm->current -= sizeof(njs_vmcode_call_t);
+
+ return njs_function_apply(vm, func, &p);
+}
+
+
+static njs_ret_t
+njs_array_prototype_every(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_int_t n;
+ uintptr_t nargs;
+ njs_param_t p;
+ njs_array_t *array;
+ njs_value_t *object, *args, *func, arguments[3];
+ njs_array_each_t *each;
+
+ object = param->object;
+
+ if (!vm->frame->reentrant) {
+ vm->frame->reentrant = 1;
+
+ if (!njs_is_array(object)) {
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+
+ array = object->data.u.array;
+ n = njs_array_next(array->start, 0, array->length);
+ each = njs_native_data(vm->frame);
+ each->index = n;
+ each->length = array->length;
+
+ } else {
+ each = njs_native_data(vm->frame);
+
+ if (!njs_is_true(&each->retval)) {
+ vm->retval = njs_value_false;
+ return NXT_OK;
+ }
+ }
+
+ n = each->index;
+
+ if (n < 0) {
+ vm->retval = njs_value_true;
+ return NXT_OK;
+ }
+
+ /* GC: array elt, array */
+ array = object->data.u.array;
+ arguments[0] = array->start[n];
+ njs_number_set(&arguments[1], n);
+ arguments[2] = *object;
+
+ each->index = njs_array_next(array->start, ++n, each->length);
+
+ nargs = param->nargs;
+ args = param->args;
+
+ p.object = (nargs > 1) ? &args[1] : (njs_value_t *) &njs_value_void;
+ p.args = arguments;
+ p.nargs = 3;
+ p.retval = (njs_index_t) &each->retval;
+
+ func = (nargs != 0) ? &args[0] : (njs_value_t *) &njs_value_void;
+
+ vm->current -= sizeof(njs_vmcode_call_t);
+
+ return njs_function_apply(vm, func, &p);
+}
+
+
+static nxt_int_t
+njs_array_next(njs_value_t *value, nxt_uint_t n, nxt_uint_t length)
+{
+ while (n < length) {
+ if (njs_is_valid(&value[n])) {
+ return n;
+ }
+
+ n++;
+ }
+
+ return -1;
+}
+
+
+static const njs_object_prop_t njs_array_prototype_properties[] =
+{
+ { njs_getter(njs_array_prototype_length),
+ njs_string("length"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_slice, 0),
+ njs_string("slice"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_push, 0),
+ njs_string("push"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_pop, 0),
+ njs_string("pop"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_unshift, 0),
+ njs_string("unshift"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_shift, 0),
+ njs_string("shift"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_to_string, 0),
+ njs_string("toString"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_join, 0),
+ njs_string("join"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_concat, 0),
+ njs_string("concat"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_for_each,
+ njs_method_data_size(sizeof(njs_array_each_t))),
+ njs_string("forEach"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_some,
+ njs_method_data_size(sizeof(njs_array_each_t))),
+ njs_string("some"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_array_prototype_every,
+ njs_method_data_size(sizeof(njs_array_each_t))),
+ njs_string("every"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_array_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_array_prototype_properties,
+ nxt_nitems(njs_array_prototype_properties));
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_ARRAY_H_INCLUDED_
+#define _NJS_ARRAY_H_INCLUDED_
+
+
+#define NJS_ARRAY_SPARE 8
+
+struct njs_array_s {
+ /* Must be aligned to njs_value_t. */
+ njs_object_t object;
+ uint32_t size;
+ uint32_t length;
+ njs_value_t *start;
+ njs_value_t *data;
+};
+
+
+njs_array_t *njs_array_alloc(njs_vm_t *vm, uint32_t length, uint32_t spare);
+njs_ret_t njs_array_realloc(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
+ uint32_t size);
+njs_ret_t njs_array_function(njs_vm_t *vm, njs_param_t *param);
+nxt_int_t njs_array_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+nxt_int_t njs_array_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+#endif /* _NJS_ARRAY_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+
+
+typedef struct {
+ njs_vmcode_operation_t operation;
+ size_t size;
+ nxt_str_t name;
+} njs_code_name_t;
+
+
+static njs_code_name_t code_names[] = {
+
+ { njs_vmcode_object_create, sizeof(njs_vmcode_object_t),
+ nxt_string("OBJECT CREATE ") },
+ { njs_vmcode_array_create, sizeof(njs_vmcode_array_t),
+ nxt_string("ARRAY CREATE ") },
+ { njs_vmcode_function_create, sizeof(njs_vmcode_function_create_t),
+ nxt_string("FUNCTION CREATE ") },
+ { njs_vmcode_regexp_create, sizeof(njs_vmcode_regexp_t),
+ nxt_string("REGEXP CREATE ") },
+
+ { njs_vmcode_property_get, sizeof(njs_vmcode_prop_get_t),
+ nxt_string("PROPERTY GET ") },
+ { njs_vmcode_property_set, sizeof(njs_vmcode_prop_set_t),
+ nxt_string("PROPERTY SET ") },
+ { njs_vmcode_property_in, sizeof(njs_vmcode_3addr_t),
+ nxt_string("PROPERTY IN ") },
+ { njs_vmcode_property_delete, sizeof(njs_vmcode_3addr_t),
+ nxt_string("PROPERTY DELETE ") },
+ { njs_vmcode_property_each_start, sizeof(njs_vmcode_prop_start_t),
+ nxt_string("PROPERTY START ") },
+ { njs_vmcode_property_each, sizeof(njs_vmcode_prop_each_t),
+ nxt_string("PROPERTY EACH ") },
+ { njs_vmcode_instance_of, sizeof(njs_vmcode_instance_of_t),
+ nxt_string("INSTANCE OF ") },
+
+ { njs_vmcode_function, sizeof(njs_vmcode_function_t),
+ nxt_string("FUNCTION ") },
+ { njs_vmcode_method, sizeof(njs_vmcode_method_t),
+ nxt_string("METHOD ") },
+ { njs_vmcode_call, sizeof(njs_vmcode_call_t),
+ nxt_string("CALL ") },
+ { njs_vmcode_return, sizeof(njs_vmcode_stop_t),
+ nxt_string("RETURN ") },
+
+ { njs_vmcode_increment, sizeof(njs_vmcode_3addr_t),
+ nxt_string("INC ") },
+ { njs_vmcode_decrement, sizeof(njs_vmcode_3addr_t),
+ nxt_string("DEC ") },
+ { njs_vmcode_post_increment, sizeof(njs_vmcode_3addr_t),
+ nxt_string("POST INC ") },
+ { njs_vmcode_post_decrement, sizeof(njs_vmcode_3addr_t),
+ nxt_string("POST DEC ") },
+
+ { njs_vmcode_delete, sizeof(njs_vmcode_2addr_t),
+ nxt_string("DELETE ") },
+ { njs_vmcode_void, sizeof(njs_vmcode_2addr_t),
+ nxt_string("VOID ") },
+ { njs_vmcode_typeof, sizeof(njs_vmcode_2addr_t),
+ nxt_string("TYPEOF ") },
+
+ { njs_vmcode_unary_plus, sizeof(njs_vmcode_2addr_t),
+ nxt_string("PLUS ") },
+ { njs_vmcode_unary_negation, sizeof(njs_vmcode_2addr_t),
+ nxt_string("NEGATION ") },
+
+ { njs_vmcode_addition, sizeof(njs_vmcode_3addr_t),
+ nxt_string("ADD ") },
+ { njs_vmcode_substraction, sizeof(njs_vmcode_3addr_t),
+ nxt_string("SUBSTRACT ") },
+ { njs_vmcode_multiplication, sizeof(njs_vmcode_3addr_t),
+ nxt_string("MULTIPLY ") },
+ { njs_vmcode_division, sizeof(njs_vmcode_3addr_t),
+ nxt_string("DIVIDE ") },
+ { njs_vmcode_remainder, sizeof(njs_vmcode_3addr_t),
+ nxt_string("REMAINDER ") },
+
+ { njs_vmcode_left_shift, sizeof(njs_vmcode_3addr_t),
+ nxt_string("LEFT SHIFT ") },
+ { njs_vmcode_right_shift, sizeof(njs_vmcode_3addr_t),
+ nxt_string("RIGHT SHIFT ") },
+ { njs_vmcode_unsigned_right_shift, sizeof(njs_vmcode_3addr_t),
+ nxt_string("UNS RIGHT SHIFT ") },
+
+ { njs_vmcode_logical_not, sizeof(njs_vmcode_2addr_t),
+ nxt_string("LOGICAL NOT ") },
+ { njs_vmcode_logical_and, sizeof(njs_vmcode_3addr_t),
+ nxt_string("LOGICAL AND ") },
+ { njs_vmcode_logical_or, sizeof(njs_vmcode_3addr_t),
+ nxt_string("LOGICAL OR ") },
+
+ { njs_vmcode_bitwise_not, sizeof(njs_vmcode_2addr_t),
+ nxt_string("BINARY NOT ") },
+ { njs_vmcode_bitwise_and, sizeof(njs_vmcode_3addr_t),
+ nxt_string("BINARY AND ") },
+ { njs_vmcode_bitwise_xor, sizeof(njs_vmcode_3addr_t),
+ nxt_string("BINARY XOR ") },
+ { njs_vmcode_bitwise_or, sizeof(njs_vmcode_3addr_t),
+ nxt_string("BINARY OR ") },
+
+ { njs_vmcode_equal, sizeof(njs_vmcode_3addr_t),
+ nxt_string("EQUAL ") },
+ { njs_vmcode_not_equal, sizeof(njs_vmcode_3addr_t),
+ nxt_string("NOT EQUAL ") },
+ { njs_vmcode_less, sizeof(njs_vmcode_3addr_t),
+ nxt_string("LESS ") },
+ { njs_vmcode_less_or_equal, sizeof(njs_vmcode_3addr_t),
+ nxt_string("LESS OR EQUAL ") },
+ { njs_vmcode_greater, sizeof(njs_vmcode_3addr_t),
+ nxt_string("GREATER ") },
+ { njs_vmcode_greater_or_equal, sizeof(njs_vmcode_3addr_t),
+ nxt_string("GREATER OR EQUAL") },
+
+ { njs_vmcode_strict_equal, sizeof(njs_vmcode_3addr_t),
+ nxt_string("STRICT EQUAL ") },
+ { njs_vmcode_strict_not_equal, sizeof(njs_vmcode_3addr_t),
+ nxt_string("STRICT NOT EQUAL") },
+
+ { njs_vmcode_move, sizeof(njs_vmcode_move_t),
+ nxt_string("MOVE ") },
+ { njs_vmcode_validate, sizeof(njs_vmcode_validate_t),
+ nxt_string("VALIDATE ") },
+
+ { njs_vmcode_if_true_jump, sizeof(njs_vmcode_cond_jump_t),
+ nxt_string("JUMP IF TRUE ") },
+ { njs_vmcode_if_false_jump, sizeof(njs_vmcode_cond_jump_t),
+ nxt_string("JUMP IF FALSE ") },
+ { njs_vmcode_jump, sizeof(njs_vmcode_jump_t),
+ nxt_string("JUMP ") },
+ { njs_vmcode_stop, sizeof(njs_vmcode_stop_t),
+ nxt_string("STOP ") },
+
+ { njs_vmcode_try_start, sizeof(njs_vmcode_try_start_t),
+ nxt_string("TRY START ") },
+ { njs_vmcode_try_end, sizeof(njs_vmcode_try_end_t),
+ nxt_string("TRY END ") },
+ { njs_vmcode_throw, sizeof(njs_vmcode_throw_t),
+ nxt_string("THROW ") },
+ { njs_vmcode_catch, sizeof(njs_vmcode_catch_t),
+ nxt_string("CATCH ") },
+ { njs_vmcode_finally, sizeof(njs_vmcode_finally_t),
+ nxt_string("FINALLY ") },
+
+};
+
+
+void
+njs_disassembler(u_char *start, u_char *end, nxt_str_t *text)
+{
+ u_char *p;
+ nxt_uint_t n;
+ nxt_str_t *name;
+ njs_vmcode_1addr_t *code1;
+ njs_vmcode_2addr_t *code2;
+ njs_vmcode_3addr_t *code3;
+ njs_vmcode_method_t *method;
+ njs_code_name_t *code_name;
+ njs_vmcode_operation_t operation;
+
+ static nxt_str_t unknown = nxt_string("UNKOWN");
+
+ (void) name;
+ (void) code1;
+ (void) code2;
+ (void) code3;
+ (void) method;
+
+ p = start;
+
+ while (p < end) {
+ operation = *(njs_vmcode_operation_t *) p;
+ code_name = code_names;
+ n = nxt_nitems(code_names);
+
+ do {
+ if (operation == code_name->operation) {
+ name = &code_name->name;
+
+ if (code_name->size == sizeof(njs_vmcode_method_t)) {
+ method = (njs_vmcode_method_t *) p;
+ nxt_log_error(NXT_LOG_INFO, log, "%V %p %p %p %p",
+ name, method->function, method->object,
+ method->method, method->code.nargs);
+
+ } else if (code_name->size == sizeof(njs_vmcode_3addr_t)) {
+ code3 = (njs_vmcode_3addr_t *) p;
+ nxt_log_error(NXT_LOG_INFO, log, "%V %p %p %p",
+ name, code3->dst, code3->src1, code3->src2);
+
+ } else if (code_name->size == sizeof(njs_vmcode_2addr_t)) {
+ code2 = (njs_vmcode_2addr_t *) p;
+ nxt_log_error(NXT_LOG_INFO, log, "%V %p %p",
+ name, code2->dst, code2->src);
+
+ } else if (code_name->size == sizeof(njs_vmcode_1addr_t)) {
+ code1 = (njs_vmcode_1addr_t *) p;
+ nxt_log_error(NXT_LOG_INFO, log, "%V %p",
+ name, code1->index);
+ }
+
+ p += code_name->size;
+
+ goto next;
+ }
+
+ code_name++;
+ n--;
+
+ } while (n != 0);
+
+ p += sizeof(njs_vmcode_operation_t);
+ name = &unknown;
+
+ nxt_log_error(NXT_LOG_INFO, log, "%V %p", name, operation);
+
+ next:
+
+ continue;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_utf8.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+static nxt_int_t
+njs_extern_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_extern_t *ext;
+
+ ext = data;
+
+// STUB
+// if (nxt_strcasestr_eq(&lhq->key, &ext->name)) {
+ if (nxt_strstr_eq(&lhq->key, &ext->name)) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+const nxt_lvlhsh_proto_t njs_extern_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ NXT_LVLHSH_BATCH_ALLOC,
+ njs_extern_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+nxt_int_t
+njs_add_external(nxt_lvlhsh_t *hash, nxt_mem_cache_pool_t *mcp, uintptr_t object,
+ njs_external_t *external, nxt_uint_t n)
+{
+ nxt_int_t ret;
+ njs_extern_t *ext;
+ nxt_lvlhsh_query_t lhq;
+
+ do {
+ ext = nxt_mem_cache_align(mcp, sizeof(njs_value_t),
+ sizeof(njs_extern_t));
+ if (nxt_slow_path(ext == NULL)) {
+ return NXT_ERROR;
+ }
+
+ ext->name.len = external->name.len;
+ ext->name.data = nxt_mem_cache_alloc(mcp, external->name.len);
+ if (nxt_slow_path(ext->name.data == NULL)) {
+ return NXT_ERROR;
+ }
+
+ memcpy(ext->name.data, external->name.data, external->name.len);
+
+ ext->value.type = NJS_EXTERNAL;
+ ext->value.data.truth = 1;
+ ext->value.data.u.external = ext;
+
+ nxt_lvlhsh_init(&ext->hash);
+ ext->type = external->type;
+ ext->get = external->get;
+ ext->set = external->set;
+ ext->find = external->find;
+ ext->each_start = external->each_start;
+ ext->each = external->each;
+ ext->method = external->method;
+ ext->object = object;
+ ext->data = external->data;
+
+ lhq.key_hash = nxt_djb_hash(external->name.data, external->name.len);
+ lhq.key = ext->name;
+ lhq.replace = 0;
+ lhq.value = ext;
+ lhq.pool = mcp;
+ lhq.proto = &njs_extern_hash_proto;
+
+ ret = nxt_lvlhsh_insert(hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (external->properties != NULL) {
+ ret = njs_add_external(&ext->hash, mcp, object,
+ external->properties, external->nproperties);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ external++;
+ n--;
+
+ } while (n != 0);
+
+ return NXT_OK;
+}
+
+
+njs_extern_t *
+njs_parser_external(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = parser->lexer->key_hash;
+ lhq.key = parser->lexer->text;
+ lhq.proto = &njs_extern_hash_proto;
+
+ if (nxt_lvlhsh_find(&vm->externals_hash, &lhq) == NXT_OK) {
+ return lhq.value;
+ }
+
+ return NULL;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_EXTERN_H_INCLUDED_
+#define _NJS_EXTERN_H_INCLUDED_
+
+
+struct njs_extern_s {
+ njs_value_t value;
+
+ /* A hash of inclusive njs_extern_t. */
+ nxt_lvlhsh_t hash;
+
+ uintptr_t type;
+ nxt_str_t name;
+
+ njs_extern_get_t get;
+ njs_extern_set_t set;
+ njs_extern_find_t find;
+
+ njs_extern_each_start_t each_start;
+ njs_extern_each_t each;
+
+ njs_extern_method_t method;
+
+ uintptr_t object;
+ uintptr_t data;
+};
+
+
+extern const nxt_lvlhsh_proto_t njs_extern_hash_proto;
+
+
+#endif /* _NJS_EXTERN_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <string.h>
+
+
+njs_function_t *
+njs_function_alloc(njs_vm_t *vm)
+{
+ njs_function_t *func;
+ njs_function_script_t *script;
+
+ func = nxt_mem_cache_zalign(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_function_t));
+
+ if (nxt_fast_path(func != NULL)) {
+ func->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ func->args_offset = 1;
+
+ script = nxt_mem_cache_zalloc(vm->mem_cache_pool,
+ sizeof(njs_function_script_t));
+ if (nxt_slow_path(script == NULL)) {
+ return NULL;
+ }
+
+ func->code.script = script;
+ }
+
+ return func;
+}
+
+
+nxt_noinline njs_value_t *
+njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method, uintptr_t nargs,
+ nxt_bool_t ctor)
+{
+ size_t size, spare_size;
+ njs_value_t *this;
+ njs_native_frame_t *frame;
+
+ size= NJS_NATIVE_FRAME_SIZE
+ + method->data.string_size
+ + nargs * sizeof(njs_value_t);
+
+ if (nxt_fast_path(size <= vm->frame->size)) {
+ frame = (njs_native_frame_t *) vm->frame->last;
+ frame->size = vm->frame->size - size;
+ frame->start = 0;
+
+ } else {
+ spare_size = size + NJS_FRAME_SPARE_SIZE;
+ spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE);
+
+ frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ spare_size);
+ if (nxt_slow_path(frame == NULL)) {
+ return NULL;
+ }
+
+ frame->size = spare_size - size;
+ frame->start = 1;
+ }
+
+ frame->ctor = ctor;
+ frame->reentrant = 0;
+ frame->lvalue = 0;
+
+ frame->u.exception.next = NULL;
+ frame->u.exception.catch = NULL;
+
+ frame->last = (u_char *) frame + size;
+ frame->previous = vm->frame;
+ vm->frame = frame;
+
+ this = (njs_value_t *)
+ ((u_char *) njs_native_data(frame) + method->data.string_size);
+ frame->arguments = this + 1;
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = frame->arguments;
+
+ return this;
+}
+
+
+njs_ret_t
+njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
+ njs_value_t *value2, nxt_bool_t lvalue)
+{
+ size_t size, spare_size;
+ njs_value_t *data;
+ njs_native_frame_t *frame;
+
+ size = NJS_NATIVE_FRAME_SIZE + 3 * sizeof(njs_value_t);
+
+ if (nxt_fast_path(size <= vm->frame->size)) {
+ frame = (njs_native_frame_t *) vm->frame->last;
+ frame->size = vm->frame->size - size;
+ frame->start = 0;
+
+ } else {
+ spare_size = size + NJS_FRAME_SPARE_SIZE;
+ spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE);
+
+ frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ spare_size);
+ if (nxt_slow_path(frame == NULL)) {
+ return NXT_ERROR;
+ }
+
+ frame->size = spare_size - size;
+ frame->start = 1;
+ }
+
+ frame->ctor = 0;
+ frame->reentrant = 0;
+ frame->lvalue = lvalue;
+
+ data = njs_native_data(frame);
+ njs_set_invalid(&data[0]);
+ data[1] = *value1;
+
+ if (lvalue) {
+ data[2].data.u.value = value2;
+
+ } else {
+ data[2] = *value2;
+ }
+
+ frame->u.exception.catch = NULL;
+ frame->u.restart = vm->current;
+ vm->current = trap;
+
+ frame->last = (u_char *) frame + size;
+ frame->previous = vm->frame;
+ vm->frame = frame;
+
+ return NXT_OK;
+}
+
+
+nxt_noinline njs_ret_t
+njs_function_apply(njs_vm_t *vm, njs_value_t *name, njs_param_t *param)
+{
+ njs_ret_t ret;
+
+ if (njs_is_native(name)) {
+ return name->data.u.method(vm, param);
+
+ } else if (njs_is_function(name)) {
+
+ if (name->data.u.function->native) {
+ return name->data.u.function->code.native(vm, param);
+ }
+
+ ret = njs_vmcode_function_frame(vm, name, param, 0);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ vm->retval = njs_value_void;
+
+ return njs_function_call(vm, name->data.u.function, param->retval);
+ }
+ }
+
+ return NXT_ERROR;
+}
+
+
+nxt_noinline njs_ret_t
+njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name, njs_param_t *param,
+ nxt_bool_t ctor)
+{
+ size_t size, spare_size;
+ uintptr_t nargs, n;
+ njs_value_t *args, *arguments;
+ njs_frame_t *frame;
+ njs_function_t *func;
+
+ func = name->data.u.function;
+ nargs = nxt_max(param->nargs, func->code.script->nargs);
+
+ size = NJS_FRAME_SIZE
+ + nargs * sizeof(njs_value_t)
+ + func->code.script->local_size;
+ spare_size = size + func->code.script->spare_size;
+
+ if (spare_size <= vm->frame->size) {
+ frame = (njs_frame_t *) vm->frame->last;
+ frame->native.size = vm->frame->size - size;
+ frame->native.start = 0;
+
+ } else {
+ if (func->code.script->spare_size != 0) {
+ spare_size = size + NJS_FRAME_SPARE_SIZE;
+ spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE);
+ }
+
+ frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ spare_size);
+ if (nxt_slow_path(frame == NULL)) {
+ return NXT_ERROR;
+ }
+
+ frame->native.size = spare_size - size;
+ frame->native.start = 1;
+ }
+
+ frame->native.ctor = ctor;
+ frame->native.reentrant = 0;
+ frame->native.lvalue = 0;
+
+ frame->native.u.exception.next = NULL;
+ frame->native.u.exception.catch = NULL;
+
+ frame->native.last = (u_char *) frame + size;
+ frame->native.previous = vm->frame;
+ vm->frame = &frame->native;
+
+ args = (njs_value_t *) ((u_char *) frame + NJS_FRAME_SIZE);
+ frame->native.arguments = args + func->args_offset;
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = frame->native.arguments;
+
+ frame->local = &args[nargs];
+
+ *args++ = *param->object;
+ nargs--;
+
+ arguments = param->args;
+
+ if (arguments != NULL) {
+ n = param->nargs;
+
+ while (n != 0) {
+ *args++ = *arguments++;
+ nargs--;
+ n--;
+ }
+ }
+
+ while (nargs != 0) {
+ *args++ = njs_value_void;
+ nargs--;
+ }
+
+ memcpy(frame->local, func->code.script->local_scope,
+ func->code.script->local_size);
+
+ vm->retval = *name;
+
+ return NXT_OK;
+}
+
+
+nxt_noinline njs_ret_t
+njs_function_call(njs_vm_t *vm, njs_function_t *func, njs_index_t retval)
+{
+ njs_frame_t *frame;
+
+ frame = (njs_frame_t *) vm->frame;
+
+ frame->retval = retval;
+
+ frame->return_address = vm->current;
+
+ vm->current = func->code.script->u.code;
+
+ frame->prev_arguments = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments
+ - func->args_offset;
+#if (NXT_DEBUG)
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
+#endif
+ frame->prev_local = vm->scopes[NJS_SCOPE_LOCAL];
+ vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
+
+ return NJS_PASS;
+}
+
+
+static const njs_object_prop_t njs_function_function_properties[] =
+{
+ { njs_string("Function"),
+ njs_string("name"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_value(NJS_NUMBER, 0, 0.0),
+ njs_string("length"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_prototype),
+ njs_string("prototype"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_function_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_function_function_properties,
+ nxt_nitems(njs_function_function_properties));
+}
+
+
+static njs_ret_t
+njs_function_prototype_call(njs_vm_t *vm, njs_param_t *param)
+{
+ uintptr_t nargs;
+ njs_ret_t ret;
+ njs_param_t p;
+ njs_value_t *func;
+ njs_vmcode_call_t *call;
+
+ p.object = ¶m->args[0];
+ p.args = ¶m->args[1];
+
+ func = param->object;
+ nargs = param->nargs;
+
+ if (njs_is_native(func)) {
+
+ if (nargs != 0) {
+ p.nargs = nargs - 1;
+ p.retval = param->retval;
+
+ return func->data.u.method(vm, &p);
+ }
+
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+
+ if (func->data.u.function->native) {
+
+ if (nargs != 0) {
+ p.nargs = nargs - 1;
+ p.retval = param->retval;
+
+ return func->data.u.function->code.native(vm, &p);
+ }
+
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+
+ if (nargs != 0) {
+ nargs--;
+
+ } else {
+ p.object = (njs_value_t *) &njs_value_void;
+ }
+
+ p.nargs = nargs;
+
+ ret = njs_vmcode_function_frame(vm, func, &p, 0);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ /* Skip the "call" method frame. */
+ vm->frame->previous = vm->frame->previous->previous;
+
+ call = (njs_vmcode_call_t *) vm->current;
+
+ return njs_function_call(vm, func->data.u.function, call->retval);
+}
+
+
+static njs_ret_t
+njs_function_prototype_apply(njs_vm_t *vm, njs_param_t *param)
+{
+ uintptr_t nargs;
+ njs_ret_t ret;
+ njs_param_t p;
+ njs_array_t *array;
+ njs_value_t *func, *args;
+ njs_vmcode_call_t *code;
+
+ args = param->args;
+ p.object = &args[0];
+
+ nargs = param->nargs;
+ p.nargs = nargs;
+
+ if (nargs > 1) {
+ if (!njs_is_array(&args[1])) {
+ goto type_error;
+ }
+
+ array = args[1].data.u.array;
+ p.args = array->start;
+ p.nargs = array->length;
+ }
+
+ func = param->object;
+
+ if (njs_is_native(func)) {
+ p.retval = param->retval;
+
+ if (nargs < 2) {
+ if (nargs != 0) {
+ p.args = &args[1];
+ p.nargs = nargs - 1;
+
+ } else {
+ goto type_error;
+ }
+ }
+
+ return func->data.u.method(vm, &p);
+ }
+
+ if (func->data.u.function->native) {
+ p.retval = param->retval;
+
+ if (nargs < 2) {
+ if (nargs != 0) {
+ p.args = &args[1];
+ p.nargs = nargs - 1;
+
+ } else {
+ goto type_error;
+ }
+ }
+
+ return func->data.u.function->code.native(vm, &p);
+ }
+
+ if (nargs < 2) {
+ if (nargs != 0) {
+ p.nargs = 0;
+
+ } else {
+ p.object = (njs_value_t *) &njs_value_void;
+ }
+ }
+
+ ret = njs_vmcode_function_frame(vm, func, &p, 0);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ /* Skip the "apply" method frame. */
+ vm->frame->previous = vm->frame->previous->previous;
+
+ code = (njs_vmcode_call_t *) vm->current;
+
+ return njs_function_call(vm, func->data.u.function, code->retval);
+ }
+
+ return NXT_ERROR;
+
+type_error:
+
+ vm->exception = &njs_exception_type_error;
+
+ return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_function_prototype_bind(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_value_t *func;
+ njs_function_t *bound;
+
+ bound = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_function_t));
+
+ if (nxt_fast_path(bound != NULL)) {
+ nxt_lvlhsh_init(&bound->object.hash);
+ nxt_lvlhsh_init(&bound->object.shared_hash);
+ bound->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ bound->args_offset = 1;
+
+ func = param->object;
+ bound->code.script = func->data.u.function->code.script;
+
+ vm->retval.data.u.function = bound;
+ vm->retval.type = NJS_FUNCTION;
+ vm->retval.data.truth = 1;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static const njs_object_prop_t njs_function_prototype_properties[] =
+{
+ { njs_native_function(njs_function_prototype_call, 0),
+ njs_string("call"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_function_prototype_apply, 0),
+ njs_string("apply"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_function_prototype_bind, 0),
+ njs_string("bind"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_function_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_function_prototype_properties,
+ nxt_nitems(njs_function_prototype_properties));
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_FUNCTION_H_INCLUDED_
+#define _NJS_FUNCTION_H_INCLUDED_
+
+
+typedef struct {
+ uint32_t nargs;
+ uint32_t local_size;
+ /*
+ * Native methods do not allocate frame space so calling function
+ * reserves space in its scope for method frame and arguments.
+ */
+ uint32_t spare_size;
+
+ /* Initial values of local scope. */
+ njs_value_t *local_scope;
+
+ union {
+ u_char *code;
+ njs_parser_t *parser;
+ } u;
+} njs_function_script_t;
+
+
+struct njs_function_s {
+ njs_object_t object;
+
+#if (NXT_64BIT)
+ uint32_t native;
+ uint32_t args_offset;
+#else
+ uint8_t native;
+ uint16_t args_offset;
+#endif
+
+ union {
+ njs_function_script_t *script;
+ njs_native_t native;
+ } code;
+
+ njs_value_t *args;
+};
+
+
+/* The frame size must be aligned to njs_value_t. */
+#define NJS_NATIVE_FRAME_SIZE \
+ nxt_align_size(sizeof(njs_native_frame_t), sizeof(njs_value_t))
+
+/* The frame size must be aligned to njs_value_t. */
+#define NJS_FRAME_SIZE \
+ nxt_align_size(sizeof(njs_frame_t), sizeof(njs_value_t))
+
+/* The retval and return_address fields are not used in the global frame. */
+#define NJS_GLOBAL_FRAME_SIZE \
+ nxt_align_size(offsetof(njs_frame_t, retval), sizeof(njs_value_t))
+
+#define NJS_FRAME_SPARE_SIZE 512
+
+#define \
+njs_method_data_size(size) \
+ nxt_align_size(size, sizeof(njs_value_t))
+
+#define \
+njs_native_data(frame) \
+ (void *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE)
+
+
+typedef struct njs_exception_s njs_exception_t;
+
+struct njs_exception_s {
+ /*
+ * The next field must be the first to alias it with restart address
+ * because it is not used to detect catch block existance in the frame.
+ */
+ njs_exception_t *next;
+ u_char *catch;
+};
+
+
+typedef struct njs_native_frame_s njs_native_frame_t;
+
+struct njs_native_frame_s {
+ u_char *last;
+ njs_native_frame_t *previous;
+ njs_value_t *arguments;
+
+ union {
+ u_char *restart;
+ njs_exception_t exception;
+ } u;
+
+ uint32_t size;
+
+ uint8_t start; /* 1 bit */
+ uint8_t ctor; /* 1 bit */
+ uint8_t reentrant; /* 1 bit */
+ uint8_t lvalue; /* 1 bit */
+};
+
+
+typedef struct {
+ njs_native_frame_t native;
+
+ njs_value_t *prev_arguments;
+ njs_value_t *prev_local;
+ njs_value_t *local;
+ njs_value_t *closure;
+
+ njs_index_t retval;
+ u_char *return_address;
+} njs_frame_t;
+
+
+njs_function_t *njs_function_alloc(njs_vm_t *vm);
+njs_ret_t njs_function_apply(njs_vm_t *vm, njs_value_t *name,
+ njs_param_t *param);
+njs_value_t *njs_vmcode_native_frame(njs_vm_t *vm, njs_value_t *method,
+ uintptr_t nargs, nxt_bool_t ctor);
+njs_ret_t njs_vmcode_trap(njs_vm_t *vm, u_char *trap, njs_value_t *value1,
+ njs_value_t *value2, nxt_bool_t lvalue);
+njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *name,
+ njs_param_t *param, nxt_bool_t ctor);
+njs_ret_t njs_function_call(njs_vm_t *vm, njs_function_t *func,
+ njs_index_t retval);
+nxt_int_t njs_function_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+nxt_int_t njs_function_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+#endif /* _NJS_FUNCTION_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+static nxt_int_t njs_generator(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_variable(njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_cond_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_while_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_do_while_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_for_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_for_in_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_operation_assignment(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_object(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_array(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_function(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_regexp(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_delete(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_3addr_operation(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_2addr_operation(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_inc_dec_operation(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node, nxt_bool_t post);
+static nxt_int_t njs_generate_function_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_return_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static nxt_int_t njs_generate_throw_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static njs_index_t njs_generator_dest_index(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node);
+static njs_index_t njs_generator_temp_index_get(njs_parser_t *parser);
+static nxt_int_t njs_generator_children_indexes_release(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generator_node_index_release(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generator_index_release(njs_vm_t *vm, njs_parser_t *parser,
+ njs_index_t index);
+
+
+static nxt_int_t
+njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+
+ if (node == NULL) {
+ return NXT_OK;
+ }
+
+ switch (node->token) {
+
+ case NJS_TOKEN_IF:
+ return njs_generate_if_statement(vm, parser, node);
+
+ case NJS_TOKEN_CONDITIONAL:
+ return njs_generate_cond_expression(vm, parser, node);
+
+ case NJS_TOKEN_WHILE:
+ return njs_generate_while_statement(vm, parser, node);
+
+ case NJS_TOKEN_DO:
+ return njs_generate_do_while_statement(vm, parser, node);
+
+ case NJS_TOKEN_FOR:
+ return njs_generate_for_statement(vm, parser, node);
+
+ case NJS_TOKEN_FOR_IN:
+ return njs_generate_for_in_statement(vm, parser, node);
+
+ case NJS_TOKEN_STATEMENT:
+ case NJS_TOKEN_COMMA:
+
+ if (node->left != NULL) {
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ node->index = node->left->index;
+
+ ret = njs_generator_node_index_release(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ node->index = node->right->index;
+
+ return njs_generator_node_index_release(vm, parser, node->right);
+
+ case NJS_TOKEN_ASSIGNMENT:
+ return njs_generate_assignment(vm, parser, node);
+
+ case NJS_TOKEN_BITWISE_OR_ASSIGNMENT:
+ case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT:
+ case NJS_TOKEN_BITWISE_AND_ASSIGNMENT:
+ case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT:
+ case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT:
+ case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+ case NJS_TOKEN_ADDITION_ASSIGNMENT:
+ case NJS_TOKEN_SUBSTRACTION_ASSIGNMENT:
+ case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT:
+ case NJS_TOKEN_DIVISION_ASSIGNMENT:
+ case NJS_TOKEN_REMAINDER_ASSIGNMENT:
+ return njs_generate_operation_assignment(vm, parser, node);
+
+ case NJS_TOKEN_LOGICAL_OR:
+ case NJS_TOKEN_LOGICAL_AND:
+ case NJS_TOKEN_BITWISE_OR:
+ case NJS_TOKEN_BITWISE_XOR:
+ case NJS_TOKEN_BITWISE_AND:
+ case NJS_TOKEN_EQUAL:
+ case NJS_TOKEN_NOT_EQUAL:
+ case NJS_TOKEN_STRICT_EQUAL:
+ case NJS_TOKEN_STRICT_NOT_EQUAL:
+ case NJS_TOKEN_IN:
+ case NJS_TOKEN_INSTANCEOF:
+ case NJS_TOKEN_LESS:
+ case NJS_TOKEN_LESS_OR_EQUAL:
+ case NJS_TOKEN_GREATER:
+ case NJS_TOKEN_GREATER_OR_EQUAL:
+ case NJS_TOKEN_LEFT_SHIFT:
+ case NJS_TOKEN_RIGHT_SHIFT:
+ case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT:
+ case NJS_TOKEN_ADDITION:
+ case NJS_TOKEN_SUBSTRACTION:
+ case NJS_TOKEN_MULTIPLICATION:
+ case NJS_TOKEN_DIVISION:
+ case NJS_TOKEN_REMAINDER:
+ case NJS_TOKEN_PROPERTY_DELETE:
+ case NJS_TOKEN_PROPERTY:
+ return njs_generate_3addr_operation(vm, parser, node);
+
+ case NJS_TOKEN_DELETE:
+ return njs_generate_delete(vm, parser, node);
+
+ case NJS_TOKEN_VOID:
+ case NJS_TOKEN_TYPEOF:
+ case NJS_TOKEN_UNARY_PLUS:
+ case NJS_TOKEN_UNARY_NEGATION:
+ case NJS_TOKEN_LOGICAL_NOT:
+ case NJS_TOKEN_BITWISE_NOT:
+ return njs_generate_2addr_operation(vm, parser, node);
+
+ case NJS_TOKEN_INCREMENT:
+ case NJS_TOKEN_DECREMENT:
+ return njs_generate_inc_dec_operation(vm, parser, node, 0);
+
+ case NJS_TOKEN_POST_INCREMENT:
+ case NJS_TOKEN_POST_DECREMENT:
+ return njs_generate_inc_dec_operation(vm, parser, node, 1);
+
+ case NJS_TOKEN_UNDEFINED:
+ case NJS_TOKEN_NULL:
+ case NJS_TOKEN_BOOLEAN:
+ case NJS_TOKEN_NUMBER:
+ case NJS_TOKEN_STRING:
+ node->index = njs_value_index(vm, parser, &node->u.value);
+
+ if (nxt_fast_path(node->index != NJS_INDEX_NONE)) {
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+
+ case NJS_TOKEN_OBJECT_LITERAL:
+ node->index = node->u.object->index;
+ return NXT_OK;
+
+ case NJS_TOKEN_OBJECT_CREATE:
+ return njs_generate_object(vm, parser, node);
+
+ case NJS_TOKEN_ARRAY_CREATE:
+ return njs_generate_array(vm, parser, node);
+
+ case NJS_TOKEN_FUNCTION_CREATE:
+ return njs_generate_function(vm, parser, node);
+
+ case NJS_TOKEN_REGEXP_LITERAL:
+ return njs_generate_regexp(vm, parser, node);
+
+ case NJS_TOKEN_THIS:
+ case NJS_TOKEN_OBJECT_FUNCTION:
+ case NJS_TOKEN_ARRAY_FUNCTION:
+ case NJS_TOKEN_NUMBER_FUNCTION:
+ case NJS_TOKEN_BOOLEAN_FUNCTION:
+ case NJS_TOKEN_STRING_FUNCTION:
+ case NJS_TOKEN_FUNCTION_FUNCTION:
+ case NJS_TOKEN_REGEXP_FUNCTION:
+ case NJS_TOKEN_EVAL:
+ case NJS_TOKEN_EXTERNAL:
+ return NXT_OK;
+
+ case NJS_TOKEN_NAME:
+ return njs_generate_variable(parser, node);
+
+ case NJS_TOKEN_FUNCTION:
+ return njs_generate_function_statement(vm, parser, node);
+
+ case NJS_TOKEN_FUNCTION_CALL:
+ return njs_generate_function_call(vm, parser, node);
+
+ case NJS_TOKEN_RETURN:
+ return njs_generate_return_statement(vm, parser, node);
+
+ case NJS_TOKEN_METHOD_CALL:
+ return njs_generate_method_call(vm, parser, node);
+
+ case NJS_TOKEN_TRY:
+ return njs_generate_try_statement(vm, parser, node);
+
+ case NJS_TOKEN_THROW:
+ return njs_generate_throw_statement(vm, parser, node);
+
+ default:
+ nxt_thread_log_debug("unknown token: %d", node->token);
+ vm->exception = &njs_exception_syntax_error;
+ return NXT_ERROR;
+ }
+}
+
+
+static nxt_int_t
+njs_generate_variable(njs_parser_t *parser, njs_parser_node_t *node)
+{
+ njs_value_t *value;
+ njs_vmcode_validate_t *validate;
+
+ node->index = node->u.variable->index;
+
+ if (node->state == NJS_VARIABLE_NORMAL
+ && node->u.variable->state < NJS_VARIABLE_SET)
+ {
+ njs_generate_code(parser, njs_vmcode_validate_t, validate);
+ validate->code.operation = njs_vmcode_validate;
+ validate->code.operands = NJS_VMCODE_NO_OPERAND;
+ validate->code.retval = NJS_VMCODE_NO_RETVAL;
+ validate->index = node->index;
+
+ value = njs_variable_value(parser, node->index);
+ njs_set_invalid(value);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ u_char *start;
+ nxt_int_t ret;
+ njs_ret_t *label;
+ njs_vmcode_jump_t *jump;
+ njs_vmcode_cond_jump_t *cond_jump;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_cond_jump_t, cond_jump);
+ cond_jump->code.operation = njs_vmcode_if_false_jump;
+ cond_jump->code.operands = NJS_VMCODE_2OPERANDS;
+ cond_jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ cond_jump->cond = node->left->index;
+
+ start = (u_char *) cond_jump;
+ label = &cond_jump->offset;
+
+ if (node->right->token == NJS_TOKEN_ELSE) {
+
+ node = node->right;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ *label = parser->code_last - start;
+ start = (u_char *) jump;
+ label = &jump->offset;
+ }
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ *label = parser->code_last - start;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_cond_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index, *left;
+ njs_parser_node_t *branch;
+ njs_vmcode_move_t *move;
+ njs_vmcode_jump_t *jump;
+ njs_vmcode_cond_jump_t *cond_jump;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_cond_jump_t, cond_jump);
+ cond_jump->code.operation = njs_vmcode_if_false_jump;
+ cond_jump->code.operands = NJS_VMCODE_2OPERANDS;
+ cond_jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ cond_jump->cond = node->left->index;
+
+ branch = node->right;
+
+ ret = njs_generator(vm, parser, branch->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->src = branch->left->index;
+
+ left = &move->dst;
+
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ cond_jump->offset = parser->code_last - (u_char *) cond_jump;
+
+ ret = njs_generator(vm, parser, branch->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->src = branch->right->index;
+
+ jump->offset = parser->code_last - (u_char *) jump;
+
+ index = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ node->temporary = 1;
+
+ if (node->left->temporary) {
+ index = node->left->index;
+
+ ret = njs_generator_children_indexes_release(vm, parser, branch);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ } else if (branch->left->temporary) {
+ index = branch->left->index;
+
+ ret = njs_generator_node_index_release(vm, parser, branch->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ } else if (branch->right->temporary) {
+ index = branch->right->index;
+
+ } else {
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ } else {
+ ret = njs_generator_children_indexes_release(vm, parser, branch);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ *left = index;
+ move->dst = index;
+ node->index = index;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_while_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ u_char *loop;
+ nxt_int_t ret;
+ njs_vmcode_jump_t *jump;
+ njs_vmcode_cond_jump_t *cond_jump;
+
+ /*
+ * Set a jump to the loop condition. This jump is executed once just on
+ * the loop enter and eliminates execution of one additional jump inside
+ * the loop per each iteration.
+ */
+
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ /* The loop body. */
+
+ loop = parser->code_last;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* The loop condition. */
+
+ jump->offset = parser->code_last - (u_char *) jump;
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_cond_jump_t, cond_jump);
+ cond_jump->code.operation = njs_vmcode_if_true_jump;
+ cond_jump->code.operands = NJS_VMCODE_2OPERANDS;
+ cond_jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ cond_jump->cond = node->right->index;
+ cond_jump->offset = loop - (u_char *) cond_jump;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_do_while_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ u_char *loop;
+ nxt_int_t ret;
+ njs_vmcode_cond_jump_t *cond_jump;
+
+ /* The loop body. */
+
+ loop = parser->code_last;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* The loop condition. */
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_cond_jump_t, cond_jump);
+ cond_jump->code.operation = njs_vmcode_if_true_jump;
+ cond_jump->code.operands = NJS_VMCODE_2OPERANDS;
+ cond_jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ cond_jump->cond = node->right->index;
+ cond_jump->offset = loop - (u_char *) cond_jump;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_for_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ u_char *loop;
+ nxt_int_t ret;
+ njs_parser_node_t *condition;
+ njs_vmcode_jump_t *jump;
+ njs_vmcode_cond_jump_t *cond_jump;
+
+ jump = NULL;
+
+ /* The loop initialization. */
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ node = node->right;
+ condition = node->left;
+
+ if (condition != NULL) {
+ /*
+ * The loop condition presents so set a jump to it. This jump is
+ * executed once just after the loop initialization and eliminates
+ * execution of one additional jump inside the loop per each iteration.
+ */
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ }
+
+ /* The loop body. */
+
+ loop = parser->code_last;
+
+ node = node->right;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* The loop update. */
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* The loop condition. */
+
+ if (condition != NULL) {
+ jump->offset = parser->code_last - (u_char *) jump;
+
+ ret = njs_generator(vm, parser, condition);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_cond_jump_t, cond_jump);
+ cond_jump->code.operation = njs_vmcode_if_true_jump;
+ cond_jump->code.operands = NJS_VMCODE_2OPERANDS;
+ cond_jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ cond_jump->cond = condition->index;
+ cond_jump->offset = loop - (u_char *) cond_jump;
+
+ } else {
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ jump->offset = loop - (u_char *) jump;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_for_in_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ u_char *loop;
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_vmcode_prop_each_t *prop_each;
+ njs_vmcode_prop_start_t *start;
+
+ ret = njs_generator(vm, parser, node->left->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_prop_start_t, start);
+ start->code.operation = njs_vmcode_property_each_start;
+ start->code.operands = NJS_VMCODE_2OPERANDS;
+ start->code.retval = NJS_VMCODE_RETVAL;
+ index = njs_generator_temp_index_get(parser);
+ start->each = index;
+ start->object = node->left->right->index;
+
+ /* The loop body. */
+
+ loop = parser->code_last;
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* The loop iterator. */
+
+ start->offset = parser->code_last - (u_char *) start;
+
+ ret = njs_generator(vm, parser, node->left->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_prop_each_t, prop_each);
+ prop_each->code.operation = njs_vmcode_property_each;
+ prop_each->code.operands = NJS_VMCODE_3OPERANDS;
+ prop_each->code.retval = NJS_VMCODE_RETVAL;
+ prop_each->retval = node->left->left->index;
+ prop_each->object = node->left->right->index;
+ prop_each->each = index;
+ prop_each->offset = loop - (u_char *) prop_each;
+
+ return njs_generator_index_release(vm, parser, index);
+}
+
+
+static nxt_int_t
+njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_value_t *value;
+ njs_parser_node_t *lvalue, *expr, *obj, *prop;
+ njs_vmcode_move_t *move;
+ njs_vmcode_prop_set_t *prop_set;
+
+ lvalue = node->left;
+ expr = node->right;
+ expr->dest = NULL;
+
+ if (lvalue->token == NJS_TOKEN_NAME) {
+
+ lvalue->index = lvalue->u.variable->index;
+
+ if (expr->token >= NJS_TOKEN_FIRST_CONST
+ && expr->token <= NJS_TOKEN_LAST_CONST)
+ {
+ ret = njs_generator(vm, parser, expr);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (lvalue->state == NJS_VARIABLE_FIRST_ASSIGNMENT) {
+ /* Use a constant value is stored as variable initial value. */
+ lvalue->lvalue = NJS_LVALUE_ASSIGNED;
+
+ value = njs_variable_value(parser, lvalue->index);
+ *value = expr->u.value;
+ node->index = expr->index;
+
+ return NXT_OK;
+ }
+ }
+
+ expr->dest = lvalue;
+
+ ret = njs_generator(vm, parser, expr);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /*
+ * lvalue and expression indexes are equal if the expression is an
+ * empty object or expression result is stored directly in variable.
+ */
+ if (lvalue->index != expr->index) {
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = lvalue->index;
+ move->src = expr->index;
+ }
+
+ node->index = expr->index;
+
+ return njs_generator_node_index_release(vm, parser, expr);
+ }
+
+ /* lvalue->token == NJS_TOKEN_PROPERTY */
+
+ /* Object. */
+
+ ret = njs_generator(vm, parser, lvalue->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* Property. */
+
+ ret = njs_generator(vm, parser, lvalue->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (nxt_slow_path(njs_parser_has_side_effect(expr))) {
+ /* Preserve object and property if they can be changed by expression. */
+
+ obj = lvalue->left;
+
+ if (obj->token == NJS_TOKEN_NAME) {
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = njs_generator_temp_index_get(parser);
+ move->src = obj->index;
+ obj->index = move->dst;
+ obj->temporary = 1;
+ }
+
+ prop = lvalue->right;
+
+ if (prop->token == NJS_TOKEN_NAME) {
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = njs_generator_temp_index_get(parser);
+ move->src = prop->index;
+ prop->index = move->dst;
+ prop->temporary = 1;
+ }
+ }
+
+ ret = njs_generator(vm, parser, expr);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_prop_set_t, prop_set);
+ prop_set->code.operation = njs_vmcode_property_set;
+ prop_set->code.operands = NJS_VMCODE_3OPERANDS;
+ prop_set->code.retval = NJS_VMCODE_NO_RETVAL;
+ node->index = expr->index;
+ prop_set->value = expr->index;
+ prop_set->object = lvalue->left->index;
+ prop_set->property = lvalue->right->index;
+
+ return njs_generator_children_indexes_release(vm, parser, node);
+}
+
+
+static nxt_int_t
+njs_generate_operation_assignment(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_parser_node_t *lvalue, *expr;
+ njs_vmcode_move_t *move;
+ njs_vmcode_3addr_t *code;
+ njs_vmcode_prop_get_t *prop_get;
+ njs_vmcode_prop_set_t *prop_set;
+
+ lvalue = node->left;
+
+ if (lvalue->token == NJS_TOKEN_NAME) {
+ ret = njs_generate_variable(parser, lvalue);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ index = lvalue->index;
+ node->index = index;
+ expr = node->right;
+
+ if (nxt_slow_path(njs_parser_has_side_effect(expr))) {
+ /* Preserve variable value if it may be changed by expression. */
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ index = njs_generator_temp_index_get(parser);
+ move->dst = index;
+ move->src = lvalue->index;
+ lvalue->temporary = 1;
+ }
+
+ ret = njs_generator(vm, parser, expr);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_3addr_t, code);
+ code->code.operation = node->u.operation;
+ code->code.operands = NJS_VMCODE_3OPERANDS;
+ code->code.retval = NJS_VMCODE_RETVAL;
+ code->dst = lvalue->index;
+ code->src1 = index;
+ code->src2 = expr->index;
+
+ return njs_generator_node_index_release(vm, parser, expr);
+ }
+
+ /* lvalue->token == NJS_TOKEN_PROPERTY */
+
+ /* Object. */
+
+ ret = njs_generator(vm, parser, lvalue->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* Property. */
+
+ ret = njs_generator(vm, parser, lvalue->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_prop_get_t, prop_get);
+ prop_get->code.operation = njs_vmcode_property_get;
+ prop_get->code.operands = NJS_VMCODE_3OPERANDS;
+ prop_get->code.retval = NJS_VMCODE_RETVAL;
+ index = njs_generator_temp_index_get(parser);
+ prop_get->value = index;
+ node->index = index;
+ prop_get->object = lvalue->left->index;
+ prop_get->property = lvalue->right->index;
+
+ expr = node->right;
+
+ ret = njs_generator(vm, parser, expr);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_3addr_t, code);
+ code->code.operation = node->u.operation;
+ code->code.operands = NJS_VMCODE_3OPERANDS;
+ code->code.retval = NJS_VMCODE_RETVAL;
+ code->dst = index;
+ code->src1 = index;
+ code->src2 = expr->index;
+
+ njs_generate_code(parser, njs_vmcode_prop_set_t, prop_set);
+ prop_set->code.operation = njs_vmcode_property_set;
+ prop_set->code.operands = NJS_VMCODE_3OPERANDS;
+ prop_set->code.retval = NJS_VMCODE_NO_RETVAL;
+ prop_set->value = index;
+ prop_set->object = lvalue->left->index;
+ prop_set->property = lvalue->right->index;
+
+ ret = njs_generator_node_index_release(vm, parser, expr);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ return njs_generator_children_indexes_release(vm, parser, lvalue);
+}
+
+
+static nxt_int_t
+njs_generate_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_vmcode_object_t *obj;
+
+ index = NJS_INDEX_NONE;
+
+ if (node->left == NULL) {
+ /* An empty object, try to assign directly to variable. */
+
+ index = njs_generator_dest_index(vm, parser, node);
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ njs_generate_code(parser, njs_vmcode_object_t, obj);
+ obj->code.operation = njs_vmcode_object_create;
+ obj->code.operands = NJS_VMCODE_1OPERAND;
+ obj->code.retval = NJS_VMCODE_RETVAL;
+ obj->retval = index;
+
+ node->index = index;
+
+ if (node->left != NULL) {
+ /* Initialize object. */
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /*
+ * Mark the node as a node with temporary result to allow reuse
+ * its index. The node can not be marked earlier because this
+ * index may be used during initialization of the object.
+ */
+ node->temporary = 1;
+ }
+
+ nxt_thread_log_debug("OBJECT %p", node->index);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_vmcode_array_t *array;
+
+ index = NJS_INDEX_NONE;
+
+ if (node->left == NULL) {
+ /* An empty object, try to assign directly to variable. */
+
+ index = njs_generator_dest_index(vm, parser, node);
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ njs_generate_code(parser, njs_vmcode_array_t, array);
+ array->code.operation = njs_vmcode_array_create;
+ array->code.operands = NJS_VMCODE_1OPERAND;
+ array->code.retval = NJS_VMCODE_RETVAL;
+ array->retval = index;
+ array->length = node->u.length;
+
+ node->index = index;
+
+ if (node->left != NULL) {
+ /* Initialize object. */
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /*
+ * Mark the node as a node with temporary result to allow reuse
+ * its index. The node can not be marked earlier because this
+ * index may be used during initialization of the object.
+ */
+ node->temporary = 1;
+ }
+
+ nxt_thread_log_debug("ARRAY %p", node->index);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_function(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_parser_node_t *body;
+ njs_function_script_t *func;
+ njs_vmcode_operation_t last;
+ njs_vmcode_function_create_t *function;
+
+ body = node->right;
+
+ if (body != NULL
+ && body->right != NULL
+ && body->right->token == NJS_TOKEN_RETURN)
+ {
+ last = NULL;
+
+ } else {
+ last = njs_vmcode_return;
+ }
+
+ func = node->u.value.data.u.data;
+
+ ret = njs_generate_scope(vm, func->u.parser, body, last);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ func->local_size = func->u.parser->scope_size;
+ func->spare_size = func->u.parser->method_arguments_size;
+ func->local_scope = func->u.parser->local_scope;
+ func->u.code = func->u.parser->code_start;
+
+ /* Try to assign directly to variable. */
+
+ index = njs_generator_dest_index(vm, parser, node);
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ njs_generate_code(parser, njs_vmcode_function_create_t, function);
+ function->code.operation = njs_vmcode_function_create;
+ function->code.operands = NJS_VMCODE_1OPERAND;
+ function->code.retval = NJS_VMCODE_RETVAL;
+ function->retval = index;
+ function->function = func;
+
+ node->index = index;
+
+ nxt_thread_log_debug("FUNCTION %p", node->index);
+ }
+
+ return ret;
+}
+
+
+static nxt_int_t
+njs_generate_regexp(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ njs_index_t index;
+ njs_vmcode_regexp_t *regexp;
+
+ /* Try to assign directly to variable. */
+
+ index = njs_generator_dest_index(vm, parser, node);
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ njs_generate_code(parser, njs_vmcode_regexp_t, regexp);
+ regexp->code.operation = njs_vmcode_regexp_create;
+ regexp->code.operands = NJS_VMCODE_1OPERAND;
+ regexp->code.retval = NJS_VMCODE_RETVAL;
+ regexp->retval = index;
+ regexp->pattern = node->u.value.data.u.data;
+
+ node->index = index;
+
+ nxt_thread_log_debug("REGEXP %p", node->index);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_delete(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
+{
+ double n;
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_parser_node_t *left;
+ njs_vmcode_2addr_t *delete;
+
+ left = node->left;
+
+ /*
+ * The "delete" operator returns "false" for "undefined", "NaN", and
+ * "Infinity" constants but not for values, expressions and "-Infinity".
+ */
+
+ switch (left->token) {
+
+ case NJS_TOKEN_NAME:
+ if (left->u.variable->state == NJS_VARIABLE_DECLARED) {
+ index = njs_value_index(vm, parser, &njs_value_false);
+ goto done;
+ }
+
+ /* A property of the global object. */
+
+ node->temporary = 1;
+ node->index = njs_generator_temp_index_get(parser);
+
+ njs_generate_code(parser, njs_vmcode_2addr_t, delete);
+ delete->code.operation = njs_vmcode_delete;
+ delete->code.operands = NJS_VMCODE_2OPERANDS;
+ delete->code.retval = NJS_VMCODE_RETVAL;
+ delete->dst = node->index;
+ delete->src = left->u.variable->index;
+
+ return NXT_OK;
+
+ case NJS_TOKEN_NUMBER:
+ n = left->u.value.data.u.number;
+
+ if (!njs_is_nan(n) && !(njs_is_infinity(n) && n > 0.0)) {
+ break;
+ }
+
+ /* Fall through. */
+
+ case NJS_TOKEN_UNDEFINED:
+ index = njs_value_index(vm, parser, &njs_value_false);
+ goto done;
+
+ default:
+ ret = njs_generator(vm, parser, left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ ret = njs_generator_node_index_release(vm, parser, left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ break;
+ }
+
+ index = njs_value_index(vm, parser, &njs_value_true);
+
+done:
+
+ node->index = index;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_3addr_operation(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_parser_node_t *left, *right;
+ njs_vmcode_move_t *move;
+ njs_vmcode_3addr_t *code;
+
+ left = node->left;
+
+ ret = njs_generator(vm, parser, left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ right = node->right;
+
+ if (left->token == NJS_TOKEN_NAME) {
+
+ if (nxt_slow_path(njs_parser_has_side_effect(right))) {
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->src = left->index;
+ left->index = njs_generator_temp_index_get(parser);
+ move->dst = left->index;
+
+ left->temporary = 1;
+ }
+ }
+
+ ret = njs_generator(vm, parser, right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_3addr_t, code);
+ code->code.operation = node->u.operation;
+ code->code.operands = NJS_VMCODE_3OPERANDS;
+ code->code.retval = NJS_VMCODE_RETVAL;
+ code->src1 = left->index;
+ code->src2 = right->index;
+
+ index = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ node->temporary = 1;
+
+ if (left->temporary) {
+ index = left->index;
+
+ ret = njs_generator_node_index_release(vm, parser, right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ } else if (right->temporary) {
+ index = right->index;
+
+ } else {
+ index = njs_generator_temp_index_get(parser);
+ }
+ }
+
+ node->index = index;
+ code->dst = index;
+
+ nxt_thread_log_debug("CODE3 %p, %p, %p",
+ code->dst, code->src1, code->src2);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_2addr_operation(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_vmcode_2addr_t *code;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_2addr_t, code);
+ code->code.operation = node->u.operation;
+ code->code.operands = NJS_VMCODE_2OPERANDS;
+ code->code.retval = NJS_VMCODE_RETVAL;
+ code->src = node->left->index;
+
+ index = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ node->temporary = 1;
+
+ if (node->left->temporary) {
+ index = node->left->index;
+
+ } else {
+ index = njs_generator_temp_index_get(parser);
+ }
+ }
+
+ node->index = index;
+ code->dst = index;
+
+ nxt_thread_log_debug("CODE2 %p, %p", code->dst, code->src);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_inc_dec_operation(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node, nxt_bool_t post)
+{
+ nxt_int_t ret;
+ njs_index_t index, dest_index;
+ njs_parser_node_t *lvalue;
+ njs_vmcode_3addr_t *code;
+ njs_vmcode_prop_get_t *prop_get;
+ njs_vmcode_prop_set_t *prop_set;
+
+ lvalue = node->left;
+
+ if (lvalue->token == NJS_TOKEN_NAME) {
+
+ ret = njs_generate_variable(parser, lvalue);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ index = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ return index;
+ }
+
+ if (index == NJS_INDEX_NONE) {
+ node->temporary = 1;
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ node->index = index;
+
+ njs_generate_code(parser, njs_vmcode_3addr_t, code);
+ code->code.operation = node->u.operation;
+ code->code.operands = NJS_VMCODE_3OPERANDS;
+ code->code.retval = NJS_VMCODE_RETVAL;
+ code->dst = index;
+ code->src1 = lvalue->index;
+ code->src2 = lvalue->index;
+
+ return NXT_OK;
+ }
+
+ /* lvalue->token == NJS_TOKEN_PROPERTY */
+
+ /* Object. */
+
+ ret = njs_generator(vm, parser, lvalue->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* Property. */
+
+ ret = njs_generator(vm, parser, lvalue->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ dest_index = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(dest_index == NJS_INDEX_ERROR)) {
+ return dest_index;
+ }
+
+ if (dest_index == NJS_INDEX_NONE
+ || dest_index == lvalue->left->index
+ || dest_index == lvalue->right->index)
+ {
+ node->temporary = 1;
+ dest_index = njs_generator_temp_index_get(parser);
+ }
+
+ node->index = dest_index;
+
+ index = post ? njs_generator_temp_index_get(parser) : dest_index;
+
+ njs_generate_code(parser, njs_vmcode_prop_get_t, prop_get);
+ prop_get->code.operation = njs_vmcode_property_get;
+ prop_get->code.operands = NJS_VMCODE_3OPERANDS;
+ prop_get->code.retval = NJS_VMCODE_RETVAL;
+ prop_get->value = index;
+ prop_get->object = lvalue->left->index;
+ prop_get->property = lvalue->right->index;
+
+ njs_generate_code(parser, njs_vmcode_3addr_t, code);
+ code->code.operation = node->u.operation;
+ code->code.operands = NJS_VMCODE_3OPERANDS;
+ code->code.retval = NJS_VMCODE_RETVAL;
+ code->dst = dest_index;
+ code->src1 = index;
+ code->src2 = index;
+
+ njs_generate_code(parser, njs_vmcode_prop_set_t, prop_set);
+ prop_set->code.operation = njs_vmcode_property_set;
+ prop_set->code.operands = NJS_VMCODE_3OPERANDS;
+ prop_set->code.retval = NJS_VMCODE_NO_RETVAL;
+ prop_set->value = index;
+ prop_set->object = lvalue->left->index;
+ prop_set->property = lvalue->right->index;
+
+ if (post) {
+ ret = njs_generator_index_release(vm, parser, index);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ return njs_generator_children_indexes_release(vm, parser, lvalue);
+}
+
+
+static nxt_int_t
+njs_generate_function_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_value_t *value;
+ njs_function_t *func;
+ njs_parser_node_t *body;
+ njs_vmcode_operation_t last;
+
+ value = njs_variable_value(parser, node->index);
+ func = value->data.u.function;
+
+ body = node->right;
+
+ if (body != NULL
+ && body->right != NULL
+ && body->right->token == NJS_TOKEN_RETURN)
+ {
+ last = NULL;
+
+ } else {
+ last = njs_vmcode_return;
+ }
+
+ ret = njs_generate_scope(vm, func->code.script->u.parser, body, last);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ parser = func->code.script->u.parser;
+
+ func->code.script->local_size = parser->scope_size;
+ func->code.script->spare_size = parser->method_arguments_size;
+ func->code.script->local_scope = parser->local_scope;
+ func->code.script->u.code = parser->code_start;
+ node->u.value = *value;
+ }
+
+ return ret;
+}
+
+
+nxt_int_t
+njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node,
+ njs_vmcode_operation_t last)
+{
+ size_t code_size, size;
+ u_char *p;
+ uintptr_t scope_size;
+ nxt_uint_t n;
+ njs_index_t index;
+ njs_value_t *value;
+ njs_vmcode_stop_t *stop;
+
+ p = nxt_mem_cache_alloc(vm->mem_cache_pool, parser->code_size);
+ if (nxt_slow_path(p == NULL)) {
+ return NXT_ERROR;
+ }
+
+ parser->code_start = p;
+ parser->code_last = p;
+
+ if (node != NULL) {
+ if (nxt_slow_path(njs_generator(vm, parser, node) != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ if (last != NULL) {
+ njs_generate_code(parser, njs_vmcode_stop_t, stop);
+ stop->code.operation = last;
+ stop->code.operands = NJS_VMCODE_1OPERAND;
+ stop->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ index = njs_value_index(vm, parser, &njs_value_void);
+
+ if (last == njs_vmcode_stop && node->index != 0) {
+ index = node->index;
+ }
+
+ stop->retval = index;
+ }
+
+ code_size = parser->code_last - parser->code_start;
+
+ nxt_thread_log_debug("SCOPE CODE SIZE: %uz %uz",
+ parser->code_size, code_size);
+
+ if (nxt_slow_path(parser->code_size < code_size)) {
+ return NXT_ERROR;
+ }
+
+ scope_size = parser->index[parser->scope - NJS_INDEX_CACHE]
+ - parser->scope_offset;
+
+ parser->local_scope = nxt_mem_cache_alloc(vm->mem_cache_pool, scope_size);
+ if (nxt_slow_path(parser->local_scope == NULL)) {
+ return NXT_ERROR;
+ }
+
+ parser->scope_size = scope_size;
+
+ size = parser->scope_values->items * sizeof(njs_value_t);
+
+ nxt_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);
+
+ p = memcpy(parser->local_scope, parser->scope_values->start, size);
+ value = (njs_value_t *) (p + size);
+
+ for (n = scope_size - size; n != 0; n -= sizeof(njs_value_t)) {
+ *value++ = njs_value_void;
+ }
+
+ nxt_thread_log_debug("SCOPE CODE:");
+
+ njs_disassembler(parser->code_start, parser->code_last, NULL);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_return_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_vmcode_stop_t *code;
+
+ ret = njs_generator(vm, parser, node->right);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ njs_generate_code(parser, njs_vmcode_stop_t, code);
+ code->code.operation = njs_vmcode_return;
+ code->code.operands = NJS_VMCODE_1OPERAND;
+ code->code.retval = NJS_VMCODE_NO_RETVAL;
+ code->retval = node->right->index;
+ node->index = node->right->index;
+ }
+
+ return ret;
+}
+
+
+static nxt_int_t
+njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ uintptr_t nargs;
+ njs_index_t retval, index, name;
+ njs_parser_node_t *arg;
+ njs_vmcode_call_t *call;
+ njs_vmcode_move_t *move;
+ njs_vmcode_function_t *func;
+
+ if (node->left != NULL) {
+ /* Generate function code in function expression. */
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ name = node->left->index;
+
+ } else {
+ /* njs_generate_variable() always returns NXT_OK. */
+ (void) njs_generate_variable(parser, node);
+ name = node->index;
+ }
+
+ njs_generate_code(parser, njs_vmcode_function_t, func);
+ func->code.operation = njs_vmcode_function;
+ func->code.operands = NJS_VMCODE_2OPERANDS;
+ func->code.retval = NJS_VMCODE_RETVAL;
+ func->code.ctor = node->ctor;
+ func->name = name;
+
+ index = njs_generator_temp_index_get(parser);
+ func->function = index;
+
+ nargs = 1;
+
+ for (arg = node->right; arg != NULL; arg = arg->right) {
+ nargs++;
+
+ ret = njs_generator(vm, parser, arg->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (arg->index != arg->left->index) {
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = arg->index;
+ move->src = arg->left->index;
+ }
+ }
+
+ func->code.nargs = nargs;
+
+ retval = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(retval == NJS_INDEX_ERROR)) {
+ return retval;
+ }
+
+ if (retval == NJS_INDEX_NONE) {
+ node->temporary = 1;
+ retval = index;
+ }
+
+ node->index = retval;
+
+ njs_generate_code(parser, njs_vmcode_call_t, call);
+ call->code.operation = njs_vmcode_call;
+ call->code.operands = NJS_VMCODE_2OPERANDS;
+ call->code.retval = NJS_VMCODE_NO_RETVAL;
+ call->code.nargs = nargs;
+ call->function = index;
+ call->retval = retval;
+
+ if (retval == index) {
+ return NXT_OK;
+ }
+
+ return njs_generator_index_release(vm, parser, index);
+}
+
+
+static nxt_int_t
+njs_generate_method_call(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ uintptr_t nargs;
+ njs_index_t retval, index;
+ njs_parser_node_t *arg, *prop;
+ njs_vmcode_call_t *call;
+ njs_vmcode_move_t *move;
+ njs_vmcode_method_t *method;
+
+ prop = node->left;
+
+ /* Object. */
+
+ ret = njs_generator(vm, parser, prop->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* Method name. */
+
+ ret = njs_generator(vm, parser, prop->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (prop->left->temporary) {
+ index = prop->left->index;
+
+ ret = njs_generator_node_index_release(vm, parser, prop->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ } else if (prop->right->temporary) {
+ index = prop->right->index;
+
+ } else {
+ index = njs_generator_temp_index_get(parser);
+ }
+
+ njs_generate_code(parser, njs_vmcode_method_t, method);
+ method->code.operation = njs_vmcode_method;
+ method->code.operands = NJS_VMCODE_3OPERANDS;
+ method->code.retval = NJS_VMCODE_RETVAL;
+ method->code.ctor = node->ctor;
+ method->function = index;
+ method->object = prop->left->index;
+ method->method = prop->right->index;
+
+ nargs = 1;
+
+ for (arg = node->right; arg != NULL; arg = arg->right) {
+ nargs++;
+
+ ret = njs_generator(vm, parser, arg->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (arg->index != arg->left->index) {
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = arg->index;
+ move->src = arg->left->index;
+ }
+ }
+
+ method->code.nargs = nargs;
+
+ retval = njs_generator_dest_index(vm, parser, node);
+
+ if (nxt_slow_path(retval == NJS_INDEX_ERROR)) {
+ return retval;
+ }
+
+ if (retval == NJS_INDEX_NONE) {
+ node->temporary = 1;
+ retval = index;
+ }
+
+ node->index = retval;
+
+ njs_generate_code(parser, njs_vmcode_call_t, call);
+ call->code.operation = njs_vmcode_call;
+ call->code.operands = NJS_VMCODE_2OPERANDS;
+ call->code.retval = NJS_VMCODE_NO_RETVAL;
+ call->code.nargs = nargs;
+ call->function = index;
+ call->retval = retval;
+
+ if (retval == index) {
+ return NXT_OK;
+ }
+
+ return njs_generator_index_release(vm, parser, index);
+}
+
+
+static nxt_int_t
+njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_vmcode_catch_t *catch;
+ njs_vmcode_finally_t *finally;
+ njs_vmcode_try_end_t *try_end, *catch_end;
+ njs_vmcode_try_start_t *try_start;
+
+ njs_generate_code(parser, njs_vmcode_try_start_t, try_start);
+ try_start->code.operation = njs_vmcode_try_start;
+ try_start->code.operands = NJS_VMCODE_2OPERANDS;
+ try_start->code.retval = NJS_VMCODE_NO_RETVAL;
+ index = njs_generator_temp_index_get(parser);
+ try_start->value = index;
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_try_end_t, try_end);
+ try_end->code.operation = njs_vmcode_try_end;
+ try_end->code.operands = NJS_VMCODE_NO_OPERAND;
+ try_end->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ try_start->offset = parser->code_last - (u_char *) try_start;
+
+ node = node->right;
+
+ if (node->token == NJS_TOKEN_CATCH) {
+ /* A try/catch case. */
+
+ ret = njs_generator(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_catch_t, catch);
+ catch->code.operation = njs_vmcode_catch;
+ catch->code.operands = NJS_VMCODE_2OPERANDS;
+ catch->code.retval = NJS_VMCODE_NO_RETVAL;
+ catch->offset = sizeof(njs_vmcode_catch_t);
+ catch->exception = node->left->index;
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ try_end->offset = parser->code_last - (u_char *) try_end;
+
+ /* TODO: release exception variable index. */
+
+ } else {
+ if (node->left != NULL) {
+ /* A try/catch/finally case. */
+
+ ret = njs_generator(vm, parser, node->left->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_catch_t, catch);
+ catch->code.operation = njs_vmcode_catch;
+ catch->code.operands = NJS_VMCODE_2OPERANDS;
+ catch->code.retval = NJS_VMCODE_NO_RETVAL;
+ catch->exception = node->left->left->index;
+
+ ret = njs_generator(vm, parser, node->left->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_try_end_t, catch_end);
+ catch_end->code.operation = njs_vmcode_try_end;
+ catch_end->code.operands = NJS_VMCODE_NO_OPERAND;
+ catch_end->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ catch->offset = parser->code_last - (u_char *) catch;
+
+ /* TODO: release exception variable index. */
+
+ njs_generate_code(parser, njs_vmcode_catch_t, catch);
+ catch->code.operation = njs_vmcode_catch;
+ catch->code.operands = NJS_VMCODE_2OPERANDS;
+ catch->code.retval = NJS_VMCODE_NO_RETVAL;
+ catch->offset = sizeof(njs_vmcode_catch_t);
+ catch->exception = index;
+
+ catch_end->offset = parser->code_last - (u_char *) catch_end;
+
+ } else {
+ /* A try/finally case. */
+
+ njs_generate_code(parser, njs_vmcode_catch_t, catch);
+ catch->code.operation = njs_vmcode_catch;
+ catch->code.operands = NJS_VMCODE_2OPERANDS;
+ catch->code.retval = NJS_VMCODE_NO_RETVAL;
+ catch->offset = sizeof(njs_vmcode_catch_t);
+ catch->exception = index;
+ }
+
+ try_end->offset = parser->code_last - (u_char *) try_end;
+
+ ret = njs_generator(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(parser, njs_vmcode_finally_t, finally);
+ finally->code.operation = njs_vmcode_finally;
+ finally->code.operands = NJS_VMCODE_1OPERAND;
+ finally->code.retval = NJS_VMCODE_NO_RETVAL;
+ finally->retval = index;
+ }
+
+ return njs_generator_index_release(vm, parser, index);
+}
+
+
+static nxt_int_t
+njs_generate_throw_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_vmcode_throw_t *code;
+
+ ret = njs_generator(vm, parser, node->right);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ njs_generate_code(parser, njs_vmcode_throw_t, code);
+ code->code.operation = njs_vmcode_throw;
+ code->code.operands = NJS_VMCODE_1OPERAND;
+ code->code.retval = NJS_VMCODE_NO_RETVAL;
+ code->retval = node->right->index;
+ node->index = node->right->index;
+ }
+
+ return ret;
+}
+
+
+static njs_index_t
+njs_generator_dest_index(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ njs_index_t ret;
+ njs_parser_node_t *dest;
+
+ dest = node->dest;
+
+ if (dest == NULL) {
+ return NJS_INDEX_NONE;
+ }
+
+ if (dest->token == NJS_TOKEN_PROPERTY) {
+ ret = njs_generator_index_release(vm, parser, dest->index);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ if (node->left != NULL) {
+ ret = njs_generator_node_index_release(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ if (node->right != NULL) {
+ ret = njs_generator_node_index_release(vm, parser, node->right);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ dest->lvalue = NJS_LVALUE_ASSIGNED;
+
+ return dest->index;
+}
+
+
+static njs_index_t
+njs_generator_temp_index_get(njs_parser_t *parser)
+{
+ nxt_uint_t n;
+ njs_index_t index, *last;
+ nxt_vector_t *cache;
+
+ cache = parser->index_cache;
+
+ if (cache != NULL && cache->items != 0) {
+ last = nxt_vector_remove_last(cache);
+
+ nxt_thread_log_debug("CACHE %p", *last);
+
+ return *last;
+ }
+
+ /* Skip absolute and propery scopes. */
+ n = parser->scope - NJS_INDEX_CACHE;
+
+ index = parser->index[n];
+ parser->index[n] += sizeof(njs_value_t);
+
+ index |= parser->scope;
+
+ nxt_thread_log_debug("GET %p", index);
+
+ return index;
+}
+
+
+static nxt_int_t
+njs_generator_children_indexes_release(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+
+ ret = njs_generator_node_index_release(vm, parser, node->left);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ return njs_generator_node_index_release(vm, parser, node->right);
+}
+
+
+static nxt_int_t
+njs_generator_node_index_release(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ if (node->temporary) {
+ return njs_generator_index_release(vm, parser, node->index);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generator_index_release(njs_vm_t *vm, njs_parser_t *parser,
+ njs_index_t index)
+{
+ njs_index_t *last;
+ nxt_vector_t *cache;
+
+ nxt_thread_log_debug("RELEASE %p", index);
+
+ cache = parser->index_cache;
+
+ if (cache == NULL) {
+ cache = nxt_vector_create(4, sizeof(njs_value_t *), &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(cache == NULL)) {
+ return NXT_ERROR;
+ }
+
+ parser->index_cache = cache;
+ }
+
+ last = nxt_vector_add(cache, &njs_array_mem_proto, vm->mem_cache_pool);
+ if (nxt_fast_path(last != NULL)) {
+ *last = index;
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+u_char *
+njs_number_trap_create(njs_vm_t *vm)
+{
+ u_char *p, *code;
+ size_t size;
+ njs_vmcode_restart_t *restart;
+ njs_vmcode_to_number_t *to_number;
+
+ size = 2 * sizeof(njs_vmcode_to_number_t) + sizeof(njs_vmcode_restart_t);
+
+ code = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
+
+ if (nxt_fast_path(code != NULL)) {
+ p = code;
+
+ to_number = (njs_vmcode_to_number_t *) p;
+ p += sizeof(njs_vmcode_to_number_t);
+ to_number->code.operation = njs_vmcode_to_number;
+ to_number->code.operands = NJS_VMCODE_1OPERAND;
+ to_number->code.retval = NJS_VMCODE_NO_RETVAL;
+ to_number->narg = 1;
+
+ to_number = (njs_vmcode_to_number_t *) p;
+ p += sizeof(njs_vmcode_to_number_t);
+ to_number->code.operation = njs_vmcode_to_number;
+ to_number->code.operands = NJS_VMCODE_1OPERAND;
+ to_number->code.retval = NJS_VMCODE_NO_RETVAL;
+ to_number->narg = 0;
+
+ restart = (njs_vmcode_restart_t *) p;
+ p += sizeof(njs_vmcode_restart_t);
+ restart->code.operation = njs_vmcode_restart;
+ restart->code.operands = NJS_VMCODE_NO_OPERAND;
+ restart->code.retval = NJS_VMCODE_NO_RETVAL;
+ }
+
+ return code;
+}
+
+
+u_char *
+njs_string_trap_create(njs_vm_t *vm)
+{
+ u_char *p, *code;
+ size_t size;
+ njs_vmcode_restart_t *restart;
+ njs_vmcode_to_string_t *to_string;
+
+ size = 2 * sizeof(njs_vmcode_to_string_t) + sizeof(njs_vmcode_restart_t);
+
+ code = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
+
+ if (nxt_fast_path(code != NULL)) {
+ p = code;
+
+ to_string = (njs_vmcode_to_string_t *) p;
+ p += sizeof(njs_vmcode_to_string_t);
+ to_string->code.operation = njs_vmcode_to_string;
+ to_string->code.operands = NJS_VMCODE_1OPERAND;
+ to_string->code.retval = NJS_VMCODE_NO_RETVAL;
+ to_string->narg = 0;
+
+ to_string = (njs_vmcode_to_string_t *) p;
+ p += sizeof(njs_vmcode_to_string_t);
+ to_string->code.operation = njs_vmcode_to_string;
+ to_string->code.operands = NJS_VMCODE_1OPERAND;
+ to_string->code.retval = NJS_VMCODE_NO_RETVAL;
+ to_string->narg = 1;
+
+ restart = (njs_vmcode_restart_t *) p;
+ p += sizeof(njs_vmcode_restart_t);
+ restart->code.operation = njs_vmcode_restart;
+ restart->code.operands = NJS_VMCODE_NO_OPERAND;
+ restart->code.retval = NJS_VMCODE_NO_RETVAL;
+ }
+
+ return code;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+typedef struct njs_lexer_multi_s njs_lexer_multi_t;
+
+struct njs_lexer_multi_s {
+ uint8_t symbol;
+ uint8_t token;
+ uint8_t count;
+ const njs_lexer_multi_t *next;
+};
+
+
+static njs_token_t njs_lexer_next_token(njs_lexer_t *lexer);
+static njs_token_t njs_lexer_word(njs_lexer_t *lexer, u_char c);
+static njs_token_t njs_lexer_string(njs_lexer_t *lexer,
+ u_char quote);
+static njs_token_t njs_lexer_number(njs_lexer_t *lexer);
+static njs_token_t njs_lexer_multi(njs_lexer_t *lexer,
+ njs_token_t token, nxt_uint_t n, const njs_lexer_multi_t *multi);
+static njs_token_t njs_lexer_division(njs_lexer_t *lexer,
+ njs_token_t token);
+
+
+static const uint8_t njs_tokens[256] nxt_aligned(64) = {
+
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ /* \t */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_SPACE,
+ /* \n */ NJS_TOKEN_LINE_END, NJS_TOKEN_ILLEGAL,
+ /* \r */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_LINE_END,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0x10 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* ! */ NJS_TOKEN_SPACE, NJS_TOKEN_LOGICAL_NOT,
+ /* " # */ NJS_TOKEN_DOUBLE_QUOTE, NJS_TOKEN_ILLEGAL,
+ /* $ % */ NJS_TOKEN_LETTER, NJS_TOKEN_REMAINDER,
+ /* & ' */ NJS_TOKEN_BITWISE_AND, NJS_TOKEN_SINGLE_QUOTE,
+ /* ( ) */ NJS_TOKEN_OPEN_PARENTHESIS, NJS_TOKEN_CLOSE_PARENTHESIS,
+ /* * + */ NJS_TOKEN_MULTIPLICATION, NJS_TOKEN_ADDITION,
+ /* , - */ NJS_TOKEN_COMMA, NJS_TOKEN_SUBSTRACTION,
+ /* . / */ NJS_TOKEN_DOT, NJS_TOKEN_DIVISION,
+
+ /* 0 1 */ NJS_TOKEN_DIGIT, NJS_TOKEN_DIGIT,
+ /* 2 3 */ NJS_TOKEN_DIGIT, NJS_TOKEN_DIGIT,
+ /* 4 5 */ NJS_TOKEN_DIGIT, NJS_TOKEN_DIGIT,
+ /* 6 7 */ NJS_TOKEN_DIGIT, NJS_TOKEN_DIGIT,
+ /* 8 9 */ NJS_TOKEN_DIGIT, NJS_TOKEN_DIGIT,
+ /* : ; */ NJS_TOKEN_COLON, NJS_TOKEN_SEMICOLON,
+ /* < = */ NJS_TOKEN_LESS, NJS_TOKEN_ASSIGNMENT,
+ /* > ? */ NJS_TOKEN_GREATER, NJS_TOKEN_CONDITIONAL,
+
+ /* @ A */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_LETTER,
+ /* B C */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* D E */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* F G */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* H I */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* J K */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* L M */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* N O */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+
+ /* P Q */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* R S */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* T U */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* V W */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* X Y */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* Z [ */ NJS_TOKEN_LETTER, NJS_TOKEN_OPEN_BRACKET,
+ /* \ ] */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_CLOSE_BRACKET,
+ /* ^ _ */ NJS_TOKEN_BITWISE_XOR, NJS_TOKEN_LETTER,
+
+ /* ` a */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_LETTER,
+ /* b c */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* d e */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* f g */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* h i */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* j k */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* l m */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* n o */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+
+ /* p q */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* r s */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* t u */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* v w */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* x y */ NJS_TOKEN_LETTER, NJS_TOKEN_LETTER,
+ /* z { */ NJS_TOKEN_LETTER, NJS_TOKEN_OPEN_BRACE,
+ /* | } */ NJS_TOKEN_BITWISE_OR, NJS_TOKEN_CLOSE_BRACE,
+ /* ~ */ NJS_TOKEN_BITWISE_NOT, NJS_TOKEN_ILLEGAL,
+
+ /* 0x80 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0x90 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0xA0 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0xB0 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* TODO: the first byte of valid UTF-8: 0xC2 - 0xF4. */
+
+ /* 0xC0 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0xD0 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0xE0 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+
+ /* 0xF0 */ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+ NJS_TOKEN_ILLEGAL, NJS_TOKEN_ILLEGAL,
+};
+
+
+static const njs_lexer_multi_t njs_addition_token[] = {
+ { '+', NJS_TOKEN_INCREMENT, 0, NULL },
+ { '=', NJS_TOKEN_ADDITION_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_substraction_token[] = {
+ { '-', NJS_TOKEN_DECREMENT, 0, NULL },
+ { '=', NJS_TOKEN_SUBSTRACTION_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_multiplication_token[] = {
+ { '=', NJS_TOKEN_MULTIPLICATION_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_remainder_token[] = {
+ { '=', NJS_TOKEN_REMAINDER_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_bitwise_and_token[] = {
+ { '&', NJS_TOKEN_LOGICAL_AND, 0, NULL },
+ { '=', NJS_TOKEN_BITWISE_AND_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_bitwise_xor_token[] = {
+ { '=', NJS_TOKEN_BITWISE_XOR_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_bitwise_or_token[] = {
+ { '|', NJS_TOKEN_LOGICAL_OR, 0, NULL },
+ { '=', NJS_TOKEN_BITWISE_OR_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_strict_not_equal_token[] = {
+ { '=', NJS_TOKEN_STRICT_NOT_EQUAL, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_logical_not_token[] = {
+ { '=', NJS_TOKEN_NOT_EQUAL, 1, njs_strict_not_equal_token },
+};
+
+
+static const njs_lexer_multi_t njs_less_shift_token[] = {
+ { '=', NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_less_token[] = {
+ { '=', NJS_TOKEN_LESS_OR_EQUAL, 0, NULL },
+ { '<', NJS_TOKEN_LEFT_SHIFT, 1, njs_less_shift_token },
+};
+
+
+static const njs_lexer_multi_t njs_less_equal_token[] = {
+ { '=', NJS_TOKEN_STRICT_EQUAL, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_unsigned_right_shift_token[] = {
+ { '=', NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT, 0, NULL },
+};
+
+
+static const njs_lexer_multi_t njs_right_shift_token[] = {
+ { '=', NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT, 0, NULL },
+ { '>', NJS_TOKEN_UNSIGNED_RIGHT_SHIFT, 1,
+ njs_unsigned_right_shift_token },
+};
+
+
+static const njs_lexer_multi_t njs_greater_token[] = {
+ { '=', NJS_TOKEN_GREATER_OR_EQUAL, 0, NULL },
+ { '>', NJS_TOKEN_RIGHT_SHIFT, 2, njs_right_shift_token },
+};
+
+
+static const njs_lexer_multi_t njs_assignment_token[] = {
+ { '=', NJS_TOKEN_EQUAL, 1, njs_less_equal_token },
+};
+
+
+njs_token_t
+njs_lexer_token(njs_lexer_t *lexer)
+{
+ njs_token_t token;
+
+ lexer->prev_token = lexer->token;
+
+ token = njs_lexer_next_token(lexer);
+
+ lexer->token = token;
+
+ return token;
+}
+
+
+static njs_token_t
+njs_lexer_next_token(njs_lexer_t *lexer)
+{
+ u_char c;
+ nxt_uint_t n;
+ njs_token_t token;
+ const njs_lexer_multi_t *multi;
+
+ while (lexer->start < lexer->end) {
+ c = *lexer->start++;
+
+ token = njs_tokens[c];
+
+ switch (token) {
+
+ case NJS_TOKEN_SPACE:
+ continue;
+
+ case NJS_TOKEN_LETTER:
+ return njs_lexer_word(lexer, c);
+
+ case NJS_TOKEN_DOUBLE_QUOTE:
+ case NJS_TOKEN_SINGLE_QUOTE:
+ return njs_lexer_string(lexer, c);
+
+ case NJS_TOKEN_DIGIT:
+ return njs_lexer_number(lexer);
+
+ case NJS_TOKEN_ASSIGNMENT:
+ n = nxt_nitems(njs_assignment_token),
+ multi = njs_assignment_token;
+
+ goto multi;
+
+ case NJS_TOKEN_ADDITION:
+ n = nxt_nitems(njs_addition_token),
+ multi = njs_addition_token;
+
+ goto multi;
+
+ case NJS_TOKEN_SUBSTRACTION:
+ n = nxt_nitems(njs_substraction_token),
+ multi = njs_substraction_token;
+
+ goto multi;
+
+ case NJS_TOKEN_MULTIPLICATION:
+ n = nxt_nitems(njs_multiplication_token),
+ multi = njs_multiplication_token;
+
+ goto multi;
+
+ case NJS_TOKEN_DIVISION:
+ token = njs_lexer_division(lexer, token);
+
+ if (token != NJS_TOKEN_AGAIN) {
+ return token;
+ }
+
+ continue;
+
+ case NJS_TOKEN_REMAINDER:
+ n = nxt_nitems(njs_remainder_token),
+ multi = njs_remainder_token;
+
+ goto multi;
+
+ case NJS_TOKEN_BITWISE_AND:
+ n = nxt_nitems(njs_bitwise_and_token),
+ multi = njs_bitwise_and_token;
+
+ goto multi;
+
+ case NJS_TOKEN_BITWISE_XOR:
+ n = nxt_nitems(njs_bitwise_xor_token),
+ multi = njs_bitwise_xor_token;
+
+ goto multi;
+
+ case NJS_TOKEN_BITWISE_OR:
+ n = nxt_nitems(njs_bitwise_or_token),
+ multi = njs_bitwise_or_token;
+
+ goto multi;
+
+ case NJS_TOKEN_LOGICAL_NOT:
+ n = nxt_nitems(njs_logical_not_token),
+ multi = njs_logical_not_token;
+
+ goto multi;
+
+ case NJS_TOKEN_LESS:
+ n = nxt_nitems(njs_less_token),
+ multi = njs_less_token;
+
+ goto multi;
+
+ case NJS_TOKEN_GREATER:
+ n = nxt_nitems(njs_greater_token),
+ multi = njs_greater_token;
+
+ goto multi;
+
+ case NJS_TOKEN_LINE_END:
+ case NJS_TOKEN_BITWISE_NOT:
+ case NJS_TOKEN_OPEN_PARENTHESIS:
+ case NJS_TOKEN_CLOSE_PARENTHESIS:
+ case NJS_TOKEN_OPEN_BRACKET:
+ case NJS_TOKEN_CLOSE_BRACKET:
+ case NJS_TOKEN_OPEN_BRACE:
+ case NJS_TOKEN_CLOSE_BRACE:
+ case NJS_TOKEN_DOT:
+ case NJS_TOKEN_COMMA:
+ case NJS_TOKEN_COLON:
+ case NJS_TOKEN_SEMICOLON:
+ case NJS_TOKEN_CONDITIONAL:
+ return token;
+
+ default: /* NJS_TOKEN_ILLEGAL */
+ lexer->start--;
+ return token;
+ }
+
+ multi:
+
+ return njs_lexer_multi(lexer, token, n, multi);
+ }
+
+ return NJS_TOKEN_END;
+}
+
+
+static njs_token_t
+njs_lexer_word(njs_lexer_t *lexer, u_char c)
+{
+ u_char *p;
+
+ /* TODO: UTF-8 */
+
+ static const uint8_t letter_digit[32] nxt_aligned(32) = {
+ 0x00, 0x00, 0x00, 0x00, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* '&%$ #"! /.-, |*)( 7654 3210 ?>=< ;:98 */
+ 0x10, 0x00, 0xff, 0x03, /* 0001 0000 0000 0000 1111 1111 0000 0011 */
+
+ /* GFED CBA@ ONML KJIH WVUT SRQP _^]\ [ZYX */
+ 0xfe, 0xff, 0xff, 0x87, /* 1111 1110 1111 1111 1111 1111 1000 0111 */
+
+ /* gfed cba` onml kjih wvut srqp ~}| {zyx */
+ 0xfe, 0xff, 0xff, 0x07, /* 1111 1110 1111 1111 1111 1111 0000 0111 */
+
+ 0x00, 0x00, 0x00, 0x00, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00, 0x00, 0x00, 0x00, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00, 0x00, 0x00, 0x00, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00, 0x00, 0x00, 0x00, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ };
+
+ lexer->key_hash = nxt_djb_hash_add(NXT_DJB_HASH_INIT, c);
+ lexer->text.data = lexer->start - 1;
+
+ for (p = lexer->start; p < lexer->end; p++) {
+ c = *p;
+
+ if ((letter_digit[c / 8] & (1 << (c & 7))) == 0) {
+ break;
+ }
+
+ lexer->key_hash = nxt_djb_hash_add(lexer->key_hash, c);
+ }
+
+ lexer->start = p;
+ lexer->text.len = p - lexer->text.data;
+
+ return njs_lexer_keyword(lexer);
+}
+
+
+static njs_token_t
+njs_lexer_string(njs_lexer_t *lexer, u_char quote)
+{
+ u_char *p, ch;
+
+ lexer->text.data = lexer->start;
+ p = lexer->start;
+
+ while (p < lexer->end) {
+
+ /* TODO: end of line, backslash. */
+
+ ch = *p++;
+
+ if (ch == '\\') {
+ if (p == lexer->end) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ /* STUB: reallocate. */
+
+ p++;
+ continue;
+ }
+
+ if (ch == quote) {
+ lexer->start = p;
+ lexer->text.len = (p - 1) - lexer->text.data;
+
+ return NJS_TOKEN_STRING;
+ }
+ }
+
+ return NJS_TOKEN_ILLEGAL;
+}
+
+
+static njs_token_t
+njs_lexer_number(njs_lexer_t *lexer)
+{
+ u_char c, *p;
+ double num, frac, scale;
+
+ /* TODO: "1e2" */
+
+ p = lexer->start;
+ c = p[-1];
+
+ /* Values below '0' become >= 208. */
+ c = c - '0';
+
+ num = c;
+
+ if (c != 0) {
+
+ while (p < lexer->end) {
+ c = *p;
+
+ /* Values below '0' become >= 208. */
+ c = c - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ num = num * 10 + c;
+ p++;
+ }
+ }
+
+ if (*p == '.') {
+
+ frac = 0;
+ scale = 1;
+
+ for (p++; p < lexer->end; p++) {
+ c = *p;
+
+ /* Values below '0' become >= 208. */
+ c = c - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ frac = frac * 10 + c;
+ scale *= 10;
+ }
+
+ num += frac / scale;
+ }
+
+ lexer->number = num;
+ lexer->start = p;
+
+ return NJS_TOKEN_NUMBER;
+}
+
+
+static njs_token_t
+njs_lexer_multi(njs_lexer_t *lexer, njs_token_t token, nxt_uint_t n,
+ const njs_lexer_multi_t *multi)
+{
+ u_char c;
+
+ if (lexer->start < lexer->end) {
+ c = lexer->start[0];
+
+ do {
+ if (c == multi->symbol) {
+ lexer->start++;
+
+ if (multi->count == 0) {
+ return multi->token;
+ }
+
+ return njs_lexer_multi(lexer, multi->token,
+ multi->count, multi->next);
+ }
+
+ multi++;
+ n--;
+
+ } while (n != 0);
+ }
+
+ return token;
+}
+
+
+static njs_token_t
+njs_lexer_division(njs_lexer_t *lexer, njs_token_t token)
+{
+ u_char c, *p;
+
+ if (lexer->start < lexer->end) {
+ c = lexer->start[0];
+
+ if (c == '/') {
+ token = NJS_TOKEN_END;
+ lexer->start++;
+
+ for (p = lexer->start; p < lexer->end; p++) {
+
+ if (*p == '\r' || *p == '\n') {
+ lexer->start = p + 1;
+ return NJS_TOKEN_LINE_END;
+ }
+ }
+
+ } else if (c == '*') {
+ lexer->start++;
+
+ for (p = lexer->start; p < lexer->end; p++) {
+
+ if (*p == '*') {
+ p++;
+
+ if (p < lexer->end && *p == '/') {
+ lexer->start = p + 1;
+ return NJS_TOKEN_AGAIN;
+ }
+ }
+ }
+
+ return NJS_TOKEN_ILLEGAL;
+
+ } else if (c == '=') {
+ lexer->start++;
+ token = NJS_TOKEN_DIVISION_ASSIGNMENT;
+ }
+ }
+
+ return token;
+}
+
+
+njs_token_t
+njs_lexer_regexp(njs_lexer_t *lexer, njs_regexp_flags_t *flags)
+{
+ u_char *p;
+ njs_regexp_flags_t _flags, flag;
+
+ for (p = lexer->start; p < lexer->end; p++) {
+
+ if (*p == '\\') {
+ p++;
+ continue;
+ }
+
+ if (*p == '/') {
+
+ lexer->text.data = lexer->start;
+ lexer->text.len = p - lexer->text.data;
+ p++;
+
+ _flags = 0;
+
+ while (p < lexer->end) {
+ switch (*p) {
+
+ case 'i':
+ flag = NJS_REGEXP_IGNORE_CASE;
+ break;
+
+ case 'g':
+ flag = NJS_REGEXP_GLOBAL;
+ break;
+
+ case 'm':
+ flag = NJS_REGEXP_MULTILINE;
+ break;
+
+ default:
+ goto done;
+ }
+
+ if (nxt_slow_path((_flags & flag) != 0)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ _flags |= flag;
+ p++;
+ }
+
+ done:
+
+ *flags = _flags;
+ lexer->start = p;
+
+ return NJS_TOKEN_REGEXP_LITERAL;
+ }
+ }
+
+ return NJS_TOKEN_ILLEGAL;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#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. */
+
+ { nxt_string("undefined"), NJS_TOKEN_UNDEFINED, 0 },
+ { nxt_string("null"), NJS_TOKEN_NULL, 0 },
+ { nxt_string("false"), NJS_TOKEN_BOOLEAN, 0 },
+ { nxt_string("true"), NJS_TOKEN_BOOLEAN, 1 },
+ { nxt_string("NaN"), NJS_TOKEN_NUMBER, NJS_NAN },
+ { nxt_string("Infinity"), NJS_TOKEN_NUMBER, NJS_INFINITY },
+
+ /* Operators. */
+
+ { nxt_string("in"), NJS_TOKEN_IN, 0 },
+ { nxt_string("typeof"), NJS_TOKEN_TYPEOF, 0 },
+ { nxt_string("instanceof"), NJS_TOKEN_INSTANCEOF, 0 },
+ { nxt_string("void"), NJS_TOKEN_VOID, 0 },
+ { nxt_string("new"), NJS_TOKEN_NEW, 0 },
+ { nxt_string("delete"), NJS_TOKEN_DELETE, 0 },
+ { nxt_string("yield"), NJS_TOKEN_YIELD, 0 },
+
+ /* Statements. */
+
+ { nxt_string("var"), NJS_TOKEN_VAR, 0 },
+ { nxt_string("if"), NJS_TOKEN_IF, 0 },
+ { nxt_string("else"), NJS_TOKEN_ELSE, 0 },
+ { nxt_string("while"), NJS_TOKEN_WHILE, 0 },
+ { nxt_string("do"), NJS_TOKEN_DO, 0 },
+ { nxt_string("for"), NJS_TOKEN_FOR, 0 },
+ { nxt_string("break"), NJS_TOKEN_BREAK, 0 },
+ { nxt_string("continue"), NJS_TOKEN_CONTINUE, 0 },
+ { nxt_string("switch"), NJS_TOKEN_SWITCH, 0 },
+ { nxt_string("case"), NJS_TOKEN_CASE, 0 },
+ { nxt_string("default"), NJS_TOKEN_DEFAULT, 0 },
+ { nxt_string("function"), NJS_TOKEN_FUNCTION, 0 },
+ { nxt_string("return"), NJS_TOKEN_RETURN, 0 },
+ { nxt_string("with"), NJS_TOKEN_WITH, 0 },
+ { nxt_string("try"), NJS_TOKEN_TRY, 0 },
+ { nxt_string("catch"), NJS_TOKEN_CATCH, 0 },
+ { nxt_string("finally"), NJS_TOKEN_FINALLY, 0 },
+ { nxt_string("throw"), NJS_TOKEN_THROW, 0 },
+
+ /* Builtin objects. */
+
+ { nxt_string("this"), NJS_TOKEN_THIS, 0 },
+
+ /* Builtin functions. */
+
+ { nxt_string("Object"), NJS_TOKEN_OBJECT_FUNCTION, 0 },
+ { nxt_string("Array"), NJS_TOKEN_ARRAY_FUNCTION, 0 },
+ { nxt_string("Boolean"), NJS_TOKEN_BOOLEAN_FUNCTION, 0 },
+ { nxt_string("Number"), NJS_TOKEN_NUMBER_FUNCTION, 0 },
+ { nxt_string("String"), NJS_TOKEN_STRING_FUNCTION, 0 },
+ { nxt_string("Function"), NJS_TOKEN_FUNCTION_FUNCTION, 0 },
+ { nxt_string("RegExp"), NJS_TOKEN_REGEXP_FUNCTION, 0 },
+ { nxt_string("eval"), NJS_TOKEN_EVAL, 0 },
+
+ /* Reserved words. */
+
+ { nxt_string("abstract"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("boolean"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("byte"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("char"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("class"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("const"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("debugger"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("double"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("enum"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("export"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("extends"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("final"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("float"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("goto"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("implements"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("import"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("int"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("interface"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("long"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("native"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("package"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("private"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("protected"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("public"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("short"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("static"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("super"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("synchronized"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("throws"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("transient"), NJS_TOKEN_RESERVED, 0 },
+ { nxt_string("volatile"), NJS_TOKEN_RESERVED, 0 },
+};
+
+
+static nxt_int_t
+njs_keyword_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_keyword_t *keyword;
+
+ keyword = data;
+
+ if (nxt_strstr_eq(&lhq->key, &keyword->name)) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static const nxt_lvlhsh_proto_t njs_keyword_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_keyword_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+nxt_int_t
+njs_lexer_keywords_init(nxt_mem_cache_pool_t *mcp, nxt_lvlhsh_t *hash)
+{
+ nxt_uint_t n;
+ nxt_lvlhsh_query_t lhq;
+ const njs_keyword_t *keyword;
+
+ keyword = njs_keywords;
+ n = nxt_nitems(njs_keywords);
+
+ lhq.replace = 0;
+ lhq.proto = &njs_keyword_hash_proto;
+ lhq.pool = mcp;
+
+ do {
+ lhq.key_hash = nxt_djb_hash(keyword->name.data, keyword->name.len);
+ lhq.key = keyword->name;
+ lhq.value = (void *) keyword;
+
+ if (nxt_slow_path(nxt_lvlhsh_insert(hash, &lhq) != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ keyword++;
+ n--;
+
+ } while (n != 0);
+
+ return NXT_OK;
+}
+
+
+njs_token_t
+njs_lexer_keyword(njs_lexer_t *lexer)
+{
+ njs_keyword_t *keyword;
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = lexer->key_hash;
+ lhq.key = lexer->text;
+ lhq.proto = &njs_keyword_hash_proto;
+
+ if (nxt_lvlhsh_find(&lexer->keywords_hash, &lhq) == NXT_OK) {
+ keyword = lhq.value;
+ lexer->number = keyword->number;
+
+ return keyword->token;
+ }
+
+ return NJS_TOKEN_NAME;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+typedef nxt_int_t (*njs_parser_operation_t)(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token, const void *data);
+
+typedef nxt_int_t (*njs_parser_stack_operation_t)(njs_vm_t *vm,
+ njs_parser_t *parser, const void *data);
+
+
+#define NJS_TOKEN_ANY NJS_TOKEN_ILLEGAL
+#define NJS_PARSER_NODE ((void *) -1)
+#define NJS_PARSER_VOID ((void *) -2)
+
+
+typedef struct {
+ njs_token_t token;
+ njs_parser_operation_t operation;
+ const void *data;
+ const void *primed;
+} njs_parser_terminal_t;
+
+
+#define NJS_PARSER_IGNORE_LINE_END 0
+#define NJS_PARSER_TEST_LINE_END 1
+
+
+typedef struct {
+ uint8_t take_line_end; /* 1 bit */
+ uint8_t count;
+#if (NXT_SUNC)
+ /*
+ * SunC supports C99 flexible array members but does not allow
+ * static struct's initialization with arbitrary number of members.
+ */
+ const njs_parser_terminal_t terminal[9];
+#else
+ const njs_parser_terminal_t terminal[];
+#endif
+} njs_parser_switch_t;
+
+
+njs_token_t njs_parser_token(njs_parser_t *parser);
+static void *njs_parser_stack_pop(njs_parser_t *parser);
+static nxt_int_t njs_parser_stack_push(njs_vm_t *vm, njs_parser_t *parser,
+ const void *data);
+
+static const void *const njs_parser_statement[];
+static const void *const njs_parser_expression0[];
+
+
+/* STUB */
+static nxt_int_t top = -1;
+static void *stack[1024];
+/**/
+
+
+njs_parser_node_t *
+njs_nonrecursive_parser(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_int_t ret;
+ njs_token_t token;
+ njs_parser_stack_operation_t operation;
+
+ if (top < 0) {
+ njs_parser_stack_push(vm, parser, njs_parser_statement);
+ }
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ /* TODO: NJS_TOKEN_AGAIN */
+ return NULL;
+ }
+
+ do {
+ operation = (njs_parser_stack_operation_t) njs_parser_stack_pop(parser);
+
+ if (operation == NULL) {
+
+ if (parser->lexer->token == NJS_TOKEN_END) {
+ return parser->node;
+ }
+
+ break;
+ }
+
+ ret = operation(vm, parser, njs_parser_stack_pop(parser));
+
+ } while (ret == NXT_OK);
+
+ nxt_thread_log_error(NXT_LOG_ERR, "unexpected token");
+
+ return NULL;
+}
+
+
+njs_token_t
+njs_parser_token(njs_parser_t *parser)
+{
+ njs_token_t token;
+
+ do {
+ token = njs_lexer_token(parser->lexer);
+
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ } while (nxt_slow_path(token == NJS_TOKEN_LINE_END));
+
+ return token;
+}
+
+
+static void *
+njs_parser_stack_pop(njs_parser_t *parser)
+{
+ if (top < 0) {
+ return NULL;
+ }
+
+ return stack[top--];
+}
+
+
+static nxt_int_t
+njs_parser_stack_push(njs_vm_t *vm, njs_parser_t *parser, const void *data)
+{
+ void *const *next;
+
+ next = data;
+
+ if (next != NULL) {
+
+ do {
+ top++;
+
+ if (*next != NJS_PARSER_NODE) {
+ stack[top] = *next;
+
+ } else {
+ stack[top] = parser->node;
+ }
+
+ next++;
+
+ } while (*next != NULL);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_switch(njs_vm_t *vm, njs_parser_t *parser, void *data)
+{
+ nxt_int_t ret;
+ nxt_uint_t n;
+ njs_token_t token;
+ njs_parser_switch_t *swtch;
+ const njs_parser_terminal_t *term;
+
+ swtch = data;
+ token = parser->lexer->token;
+
+ n = swtch->count;
+ term = swtch->terminal;
+
+ do {
+ if (token == term->token || term->token == NJS_TOKEN_ANY) {
+ ret = term->operation(vm, parser, token, term->data);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ ret = njs_parser_stack_push(vm, parser, term->primed);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ if (term->token != NJS_TOKEN_ANY) {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ /* TODO: NJS_TOKEN_AGAIN */
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+ }
+
+ term++;
+ n--;
+
+ } while (n != 0);
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_statement_semicolon(njs_vm_t *vm, njs_parser_t *parser,
+ void *data)
+{
+ njs_token_t token;
+ njs_parser_node_t *node;
+
+ node = data;
+
+ switch (parser->lexer->token) {
+
+ case NJS_TOKEN_SEMICOLON:
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ /* TODO: NJS_TOKEN_AGAIN */
+ return NXT_ERROR;
+ }
+
+ /* Fall through. */
+
+ case NJS_TOKEN_END:
+
+ node->right = parser->node;
+ parser->node = node;
+
+ return NXT_OK;
+
+ default:
+ break;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_test_token(njs_vm_t *vm, njs_parser_t *parser, void *data)
+{
+ njs_token_t token;
+
+ token = (njs_token_t) data;
+
+ if (parser->lexer->token == token) {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ /* TODO: NJS_TOKEN_AGAIN */
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ vm->exception = &njs_exception_syntax_error;
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_node(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
+ const void *data)
+{
+ njs_parser_node_t *node;
+
+ token = (njs_token_t) data;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->left = parser->node;
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_noop(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
+ const void *data)
+{
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_link_left(njs_vm_t *vm, njs_parser_t *parser, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = (njs_parser_node_t *) data;
+
+ node->left = parser->node;
+ parser->node = node;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_link_right(njs_vm_t *vm, njs_parser_t *parser, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = (njs_parser_node_t *) data;
+
+ node->right = parser->node;
+ parser->node = node;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_condition_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->left = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_cond_jump_t)
+ + sizeof(njs_vmcode_move_t)
+ + sizeof(njs_vmcode_jump_t)
+ + sizeof(njs_vmcode_move_t);
+ parser->branch = 1;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ operation = (njs_vmcode_operation_t) data;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_post_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ operation = (njs_vmcode_operation_t) data;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_unary_expression0(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ operation = (njs_vmcode_operation_t) data;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.operation = operation;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ operation = (njs_vmcode_operation_t) data;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.operation = operation;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_2addr_t);
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_unary_plus_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ return njs_parser_unary_expression(vm, parser,
+ NJS_TOKEN_UNARY_PLUS, data);
+}
+
+
+static nxt_int_t
+njs_parser_unary_plus_link(njs_vm_t *vm, njs_parser_t *parser,
+ const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = (njs_parser_node_t *) data;
+
+ /* Skip the unary plus of number. */
+
+ if (parser->node->token != NJS_TOKEN_NUMBER) {
+ node->left = parser->node;
+ parser->node = node;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_unary_negation_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ return njs_parser_unary_expression(vm, parser,
+ NJS_TOKEN_UNARY_NEGATION, data);
+}
+
+
+static nxt_int_t
+njs_parser_unary_negative_link(njs_vm_t *vm, njs_parser_t *parser,
+ void *data)
+{
+ double num;
+ njs_parser_node_t *node;
+
+ node = data;
+
+ if (parser->node->token == NJS_TOKEN_NUMBER) {
+ /* Optimization of common negative number. */
+ node = parser->node;
+ num = -node->u.value.data.u.number;
+ node->u.value.data.u.number = num;
+ node->u.value.data.truth = njs_is_number_true(num);
+
+ } else {
+ node->left = parser->node;
+ parser->node = node;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_name_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ nxt_uint_t level;
+ njs_extern_t *ext;
+ njs_variable_t *var;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ ext = njs_parser_external(vm, parser);
+
+ if (ext != NULL) {
+ node->token = NJS_TOKEN_EXTERNAL;
+ node->u.value.type = NJS_EXTERNAL;
+ node->u.value.data.truth = 1;
+ node->index = (njs_index_t) ext;
+
+ } else {
+ node->token = token;
+
+ var = njs_parser_variable(vm, parser, &level);
+ if (nxt_slow_path(var == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ switch (var->state) {
+
+ case NJS_VARIABLE_CREATED:
+ var->state = NJS_VARIABLE_PENDING;
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+ break;
+
+ case NJS_VARIABLE_PENDING:
+ var->state = NJS_VARIABLE_USED;
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+ break;
+
+ case NJS_VARIABLE_USED:
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+ break;
+
+ case NJS_VARIABLE_SET:
+ case NJS_VARIABLE_DECLARED:
+ break;
+ }
+
+ node->lvalue = NJS_LVALUE_ENABLED;
+ node->u.variable = var;
+ }
+ }
+
+ parser->node = node;
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_parser_var_name(njs_vm_t *vm, njs_parser_t *parser, void *data)
+{
+ /* TODO disable NJS_TOKEN_EXTERNAL */
+ return njs_parser_name_expression(vm, parser, NJS_TOKEN_NAME, data);
+}
+
+
+static nxt_int_t
+njs_parser_this_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->index = NJS_INDEX_THIS;
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_string_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ nxt_int_t ret;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+
+ ret = njs_parser_string_create(vm, &node->u.value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_number_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ double num;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ num = parser->lexer->number;
+ node->u.value.data.u.number = num;
+ node->u.value.type = NJS_NUMBER;
+ node->u.value.data.truth = njs_is_number_true(num);
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_boolean_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.value = (parser->lexer->number == 0.0) ? njs_value_false:
+ njs_value_true;
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_undefined_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.value = njs_value_void;
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_null_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+
+ if (nxt_fast_path(node != NULL)) {
+ node->token = token;
+ node->u.value = njs_value_null;
+ parser->node = node;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_parser_syntax_error(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token, const void *data)
+{
+ vm->exception = &njs_exception_syntax_error;
+
+ return NXT_ERROR;
+}
+
+
+/*
+ * The variables and literal values.
+ *
+ * VALUE = "(" EXPRESSION ")"
+ * [ NAME create_node ]
+ * [ "this" create_node ]
+ * [ STRING create_node ]
+ * [ NUMBER create_node ]
+ * [ BOOLEAN create_node ]
+ * [ "undefined" create_node ]
+ * [ "null" create_node ]
+ * ERROR
+ */
+
+static const void *const njs_parser_grouping_expression[] = {
+ (void *) NJS_TOKEN_CLOSE_PARENTHESIS, (void *) njs_parser_test_token,
+ &njs_parser_expression0, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_value_expression_switch = {
+ NJS_PARSER_IGNORE_LINE_END,
+ 9, {
+ { NJS_TOKEN_OPEN_PARENTHESIS, njs_parser_noop, NULL,
+ &njs_parser_grouping_expression },
+
+ { NJS_TOKEN_NAME, njs_parser_name_expression, NULL, NULL },
+ { NJS_TOKEN_THIS, njs_parser_this_expression, NULL, NULL },
+ { NJS_TOKEN_STRING, njs_parser_string_expression, NULL, NULL },
+ { NJS_TOKEN_NUMBER, njs_parser_number_expression, NULL, NULL },
+ { NJS_TOKEN_BOOLEAN, njs_parser_boolean_expression, NULL, NULL },
+
+ { NJS_TOKEN_UNDEFINED,
+ njs_parser_undefined_expression, NULL, NULL },
+ { NJS_TOKEN_NULL, njs_parser_null_expression, NULL, NULL },
+
+ { NJS_TOKEN_ANY, njs_parser_syntax_error, NULL, NULL },
+ }
+};
+
+
+#if 0
+
+static const void *const njs_parser_value_expression[] = {
+ &njs_parser_value_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+#endif
+
+
+/*
+ * The postfix increment and decrement operations.
+ *
+ * POSTFIX_INC_DEC = VALUE [ "++" create_node ]
+ * VALUE [ "--" create_node ]
+ * <>
+ */
+
+
+static const njs_parser_switch_t njs_parser_post_inc_dec_expression_switch = {
+ NJS_PARSER_IGNORE_LINE_END,
+ 2, {
+ { NJS_TOKEN_INCREMENT,
+ njs_parser_post_unary_expression,
+ (void *) njs_vmcode_post_increment, NULL },
+
+ { NJS_TOKEN_DECREMENT,
+ njs_parser_post_unary_expression,
+ (void *) njs_vmcode_post_decrement, NULL },
+ }
+};
+
+
+static const void *const njs_parser_post_inc_dec_expression[] = {
+ &njs_parser_post_inc_dec_expression_switch, (void *) njs_parser_switch,
+ &njs_parser_value_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+/*
+ * The prefix increment and decrement operations.
+ *
+ * PREFIX_INC_DEC = [ "++" create_node ] POSTFIX_INC_DEC link_left
+ * [ "--" create_node ] POSTFIX_INC_DEC link_left
+ * <> POSTFIX_INC_DEC
+ */
+
+static const void *const njs_parser_inc_dec_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_link_left,
+ &njs_parser_value_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_inc_dec_expression_switch = {
+ NJS_PARSER_IGNORE_LINE_END,
+ 3, {
+ { NJS_TOKEN_INCREMENT,
+ njs_parser_unary_expression0, (void *) njs_vmcode_increment,
+ njs_parser_inc_dec_expression_primed },
+
+ { NJS_TOKEN_DECREMENT,
+ njs_parser_unary_expression0, (void *) njs_vmcode_decrement,
+ njs_parser_inc_dec_expression_primed },
+
+ { NJS_TOKEN_ANY, njs_parser_noop, NULL,
+ njs_parser_post_inc_dec_expression },
+ }
+};
+
+
+static const void *const njs_parser_inc_dec_expression[] = {
+ &njs_parser_inc_dec_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+/*
+ * The unary operations.
+ *
+ * UNARY = [ "+" create_node ] UNARY plus_link_left
+ * [ "-" create_node ] UNARY negation_link_left
+ * [ "!" create_node ] UNARY link_left
+ * [ "~" create_node ] UNARY link_left
+ * [ "typeof" create_node ] UNARY link_left
+ * [ "void" create_node ] UNARY link_left
+ * [ "delete" create_node ] UNARY link_left
+ * <> INC_DEC
+ */
+
+static const njs_parser_switch_t njs_parser_unary_expression_switch;
+
+static const void *const njs_parser_unary_plus_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_unary_plus_link,
+ &njs_parser_unary_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+static const void *const njs_parser_unary_negative_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_unary_negative_link,
+ &njs_parser_unary_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+static const void *const njs_parser_unary_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_link_left,
+ &njs_parser_unary_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_unary_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 8, {
+ { NJS_TOKEN_ADDITION,
+ njs_parser_unary_plus_expression, (void *) njs_vmcode_unary_plus,
+ njs_parser_unary_plus_expression_primed },
+
+ { NJS_TOKEN_SUBSTRACTION,
+ njs_parser_unary_negation_expression,
+ (void *) njs_vmcode_unary_negation,
+ njs_parser_unary_negative_expression_primed },
+
+ { NJS_TOKEN_LOGICAL_NOT,
+ njs_parser_unary_expression, (void *) njs_vmcode_logical_not,
+ njs_parser_unary_expression_primed },
+
+ { NJS_TOKEN_BITWISE_NOT,
+ njs_parser_unary_expression, (void *) njs_vmcode_bitwise_not,
+ njs_parser_unary_expression_primed },
+
+ { NJS_TOKEN_TYPEOF,
+ njs_parser_unary_expression, (void *) njs_vmcode_typeof,
+ njs_parser_unary_expression_primed },
+
+ { NJS_TOKEN_VOID,
+ njs_parser_unary_expression, (void *) njs_vmcode_void,
+ njs_parser_unary_expression_primed },
+
+ { NJS_TOKEN_DELETE,
+ njs_parser_unary_expression, (void *) njs_vmcode_delete,
+ njs_parser_unary_expression_primed },
+
+ { NJS_TOKEN_ANY, njs_parser_noop, NULL,
+ njs_parser_inc_dec_expression },
+ }
+};
+
+
+/*
+ * The left associative multiplication, division and remainder operations.
+ *
+ * MULTIPLICATION = UNARY MULTIPLICATION'
+ * MULTIPLICATION' = [ "*" create_node ] UNARY link_right MULTIPLICATION'
+ * [ "/" create_node ] UNARY link_right MULTIPLICATION'
+ * [ "%" create_node ] UNARY link_right MULTIPLICATION'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_multiplicative_expression_switch;
+
+static const void *const njs_parser_multiplicative_expression_primed[] = {
+ &njs_parser_multiplicative_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_unary_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+static const njs_parser_switch_t
+ njs_parser_multiplicative_expression_switch =
+{
+ NJS_PARSER_TEST_LINE_END,
+ 3, {
+ { NJS_TOKEN_MULTIPLICATION,
+ njs_parser_binary_expression, (void *) njs_vmcode_multiplication,
+ njs_parser_multiplicative_expression_primed },
+
+ { NJS_TOKEN_DIVISION,
+ njs_parser_binary_expression, (void *) njs_vmcode_division,
+ njs_parser_multiplicative_expression_primed },
+
+ { NJS_TOKEN_REMAINDER,
+ njs_parser_binary_expression, (void *) njs_vmcode_remainder,
+ njs_parser_multiplicative_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_multiplicative_expression[] = {
+ &njs_parser_multiplicative_expression_switch, (void *) njs_parser_switch,
+ &njs_parser_unary_expression_switch, (void *) njs_parser_switch,
+ NULL,
+};
+
+
+/*
+ * The left associative addition and substraction operations.
+ *
+ * ADDITION = MULTIPLICATION ADDITION'
+ * ADDITION' = [ "+" create_node ] MULTIPLICATION link_right ADDITION'
+ * [ "-" create_node ] MULTIPLICATION link_right ADDITION'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_additive_expression_switch;
+
+static const void *const njs_parser_additive_expression_primed[] = {
+ &njs_parser_additive_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ njs_parser_multiplicative_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_additive_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 2, {
+ { NJS_TOKEN_ADDITION,
+ njs_parser_binary_expression, (void *) njs_vmcode_addition,
+ njs_parser_additive_expression_primed },
+
+ { NJS_TOKEN_SUBSTRACTION,
+ njs_parser_binary_expression, (void *) njs_vmcode_substraction,
+ njs_parser_additive_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_additive_expression[] = {
+ &njs_parser_additive_expression_switch, (void *) njs_parser_switch,
+ njs_parser_multiplicative_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative bitwise shift operations.
+ *
+ * BITWISE_SHIFT = ADDITION BITWISE_SHIFT'
+ * BITWISE_SHIFT' = [ "<<" create_node ] ADDITION link_right BITWISE_SHIFT'
+ * [ ">>" create_node ] ADDITION link_right BITWISE_SHIFT'
+ * [ ">>>" create_node ] ADDITION link_right BITWISE_SHIFT'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_bitwise_shift_expression_switch;
+
+static const void *const njs_parser_bitwise_shift_expression_primed[] = {
+ &njs_parser_bitwise_shift_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_additive_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_bitwise_shift_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 3, {
+ { NJS_TOKEN_LEFT_SHIFT,
+ njs_parser_binary_expression, (void *) njs_vmcode_left_shift,
+ njs_parser_bitwise_shift_expression_primed },
+
+ { NJS_TOKEN_RIGHT_SHIFT,
+ njs_parser_binary_expression, (void *) njs_vmcode_right_shift,
+ njs_parser_bitwise_shift_expression_primed },
+
+ { NJS_TOKEN_UNSIGNED_RIGHT_SHIFT,
+ njs_parser_binary_expression,
+ (void *) njs_vmcode_unsigned_right_shift,
+ njs_parser_bitwise_shift_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_bitwise_shift_expression[] = {
+ &njs_parser_bitwise_shift_expression_switch, (void *) njs_parser_switch,
+ njs_parser_additive_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative relational operations.
+ *
+ * RELATIONAL = BITWISE_SHIFT RELATIONAL'
+ * RELATIONAL' = [ "< create_node ] BITWISE_SHIFT link_right RELATIONAL'
+ * [ "<=" create_node ] BITWISE_SHIFT link_right RELATIONAL'
+ * [ ">" create_node ] BITWISE_SHIFT link_right RELATIONAL'
+ * [ ">=" create_node ] BITWISE_SHIFT link_right RELATIONAL'
+ * [ "in" create_node ] BITWISE_SHIFT link_right RELATIONAL'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_relational_expression_switch;
+
+static const void *const njs_parser_relational_expression_primed[] = {
+ &njs_parser_relational_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_bitwise_shift_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_relational_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 6, {
+ { NJS_TOKEN_LESS,
+ njs_parser_binary_expression, (void *) njs_vmcode_less,
+ njs_parser_relational_expression_primed },
+
+ { NJS_TOKEN_LESS_OR_EQUAL,
+ njs_parser_binary_expression, (void *) njs_vmcode_less_or_equal,
+ njs_parser_relational_expression_primed },
+
+ { NJS_TOKEN_GREATER,
+ njs_parser_binary_expression, (void *) njs_vmcode_greater,
+ njs_parser_relational_expression_primed },
+
+ { NJS_TOKEN_GREATER_OR_EQUAL,
+ njs_parser_binary_expression, (void *) njs_vmcode_greater_or_equal,
+ njs_parser_relational_expression_primed },
+
+ { NJS_TOKEN_IN,
+ njs_parser_binary_expression, (void *) njs_vmcode_property_in,
+ njs_parser_relational_expression_primed },
+
+ { NJS_TOKEN_INSTANCEOF,
+ njs_parser_binary_expression, (void *) njs_vmcode_instance_of,
+ njs_parser_relational_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_relational_expression[] = {
+ &njs_parser_relational_expression_switch, (void *) njs_parser_switch,
+ njs_parser_bitwise_shift_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative equality operations.
+ *
+ * EQUALITY = RELATION EQUALITY'
+ * EQUALITY' = [ "==" create_node ] RELATION link_right EQUALITY'
+ * [ "!=" create_node ] RELATION link_right EQUALITY'
+ * [ "===" create_node ] RELATION link_right EQUALITY'
+ * [ "!==" create_node ] RELATION link_right EQUALITY'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_equality_expression_switch;
+
+static const void *const njs_parser_equality_expression_primed[] = {
+ &njs_parser_equality_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_relational_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_equality_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 4, {
+ { NJS_TOKEN_EQUAL,
+ njs_parser_binary_expression, (void *) njs_vmcode_equal,
+ njs_parser_equality_expression_primed },
+
+ { NJS_TOKEN_NOT_EQUAL,
+ njs_parser_binary_expression, (void *) njs_vmcode_not_equal,
+ njs_parser_equality_expression_primed },
+
+ { NJS_TOKEN_STRICT_EQUAL,
+ njs_parser_binary_expression, (void *) njs_vmcode_strict_equal,
+ njs_parser_equality_expression_primed },
+
+ { NJS_TOKEN_STRICT_NOT_EQUAL,
+ njs_parser_binary_expression, (void *) njs_vmcode_strict_not_equal,
+ njs_parser_equality_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_equality_expression[] = {
+ &njs_parser_equality_expression_switch, (void *) njs_parser_switch,
+ njs_parser_relational_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative bitwise AND.
+ *
+ * BITWISE_AND = EQUALITY BITWISE_AND'
+ * BITWISE_AND' = [ "&" create_node ] EQUALITY link_right BITWISE_AND'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_bitwise_and_expression_switch;
+
+static const void *const njs_parser_bitwise_and_expression_primed[] = {
+ &njs_parser_bitwise_and_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_bitwise_shift_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_bitwise_and_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_BITWISE_AND,
+ njs_parser_binary_expression, (void *) njs_vmcode_bitwise_and,
+ njs_parser_bitwise_and_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_bitwise_and_expression[] = {
+ &njs_parser_bitwise_and_expression_switch, (void *) njs_parser_switch,
+ njs_parser_equality_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative bitwise XOR.
+ *
+ * BITWISE_XOR = BITWISE_AND BITWISE_XOR'
+ * BITWISE_XOR' = [ "^" create_node ] BITWISE_AND link_right BITWISE_XOR'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_bitwise_xor_expression_switch;
+
+static const void *const njs_parser_bitwise_xor_expression_primed[] = {
+ &njs_parser_bitwise_xor_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_bitwise_and_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_bitwise_xor_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_BITWISE_XOR,
+ njs_parser_binary_expression, (void *) njs_vmcode_bitwise_xor,
+ njs_parser_bitwise_xor_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_bitwise_xor_expression[] = {
+ &njs_parser_bitwise_xor_expression_switch, (void *) njs_parser_switch,
+ njs_parser_bitwise_and_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative bitwise OR.
+ *
+ * BITWISE_OR = BITWISE_XOR BITWISE_OR'
+ * BITWISE_OR' = [ "|" create_node ] BITWISE_XOR link_right BITWISE_OR'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_bitwise_or_expression_switch;
+
+static const void *const njs_parser_bitwise_or_expression_primed[] = {
+ &njs_parser_bitwise_or_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_bitwise_xor_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_bitwise_or_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_BITWISE_OR,
+ njs_parser_binary_expression, (void *) njs_vmcode_bitwise_or,
+ njs_parser_bitwise_or_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_bitwise_or_expression[] = {
+ &njs_parser_bitwise_or_expression_switch, (void *) njs_parser_switch,
+ njs_parser_bitwise_xor_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative logical AND.
+ *
+ * LOGICAL_AND = BITWISE_OR LOGICAL_AND'
+ * LOGICAL_AND' = [ "&&" create_node ] BITWISE_OR link_right LOGICAL_AND'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_logical_and_expression_switch;
+
+static const void *const njs_parser_logical_and_expression_primed[] = {
+ &njs_parser_logical_and_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_bitwise_or_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_logical_and_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_LOGICAL_AND,
+ njs_parser_binary_expression, (void *) njs_vmcode_logical_and,
+ njs_parser_logical_and_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_logical_and_expression[] = {
+ &njs_parser_logical_and_expression_switch, (void *) njs_parser_switch,
+ njs_parser_bitwise_or_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative logical OR.
+ *
+ * LOGICAL_OR = LOGICAL_AND LOGICAL_OR'
+ * LOGICAL_OR' = [ "||" create_node ] LOGICAL_AND link_right LOGICAL_OR'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_logical_or_expression_switch;
+
+static const void *const njs_parser_logical_or_expression_primed[] = {
+ &njs_parser_logical_or_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_logical_and_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_logical_or_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_LOGICAL_OR,
+ njs_parser_binary_expression, (void *) njs_vmcode_logical_or,
+ njs_parser_logical_or_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_logical_or_expression[] = {
+ &njs_parser_logical_or_expression_switch, (void *) njs_parser_switch,
+ njs_parser_logical_and_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The right associative condition operation.
+ *
+ * CONDITION = LOGICAL_OR CONDITION'
+ * CONDITION' = [ "?" create_node ] ASSIGNMENT COLON link_right
+ * <>
+ * COLON = [ ":" create_node ] ASSIGNMENT link_right
+ * ERROR
+ */
+
+static const void *const njs_parser_assignment_expression[];
+
+static const void *const njs_parser_colon_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_assignment_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_colon_expression_switch = {
+ NJS_PARSER_IGNORE_LINE_END,
+ 2, {
+ { NJS_TOKEN_COLON, njs_parser_node, (void *) NJS_TOKEN_ELSE,
+ njs_parser_colon_expression_primed },
+
+ { NJS_TOKEN_ANY, njs_parser_syntax_error, NULL, NULL },
+ }
+};
+
+
+static const void *const njs_parser_conditional_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_colon_expression_switch, (void *) njs_parser_switch,
+ &njs_parser_assignment_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_conditional_expression_switch = {
+ NJS_PARSER_IGNORE_LINE_END,
+ 1, {
+ { NJS_TOKEN_CONDITIONAL,
+ njs_parser_condition_expression, (void *) NJS_TOKEN_CONDITIONAL,
+ njs_parser_conditional_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_conditional_expression0[] = {
+ &njs_parser_conditional_expression_switch, (void *) njs_parser_switch,
+ njs_parser_logical_or_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The right associative assignment operations.
+ *
+ * ASSIGNMENT = CONDITION ASSIGNMENT'
+ * ASSIGNMENT' = [ "=" create_node ] CONDITION ASSIGNMENT' link_right
+ * <>
+ */
+
+static const void *const njs_parser_assignment_expression_primed[] = {
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_assignment_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_assignment_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_ASSIGNMENT,
+ /* STUB */ njs_parser_binary_expression, (void *) njs_vmcode_move,
+ njs_parser_assignment_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_assignment_expression[] = {
+ &njs_parser_assignment_expression_switch, (void *) njs_parser_switch,
+ njs_parser_conditional_expression0, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The left associative comma.
+ *
+ * EXPRESSION = ASSIGNMENT EXPRESSION'
+ * EXPRESSION' = [ "," create_node ] ASSIGNMENT link_right EXPRESSION'
+ * <>
+ */
+
+static const njs_parser_switch_t njs_parser_comma_expression_switch;
+
+static const void *const njs_parser_expression_primed[] = {
+ &njs_parser_comma_expression_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_assignment_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_comma_expression_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_COMMA,
+ njs_parser_binary_expression, NULL, njs_parser_expression_primed },
+ }
+};
+
+
+static const void *const njs_parser_expression0[] = {
+ &njs_parser_comma_expression_switch, (void *) njs_parser_switch,
+ njs_parser_assignment_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+/*
+ * The variable declarations and initializations.
+ *
+ * VAR = [ NAME create_node ] VAR_INIT
+ * VAR_INIT = "=" ASSIGNMENT [ link_right ] VAR_NEXT
+ * "," VAR
+ * <>
+ * VAR_NEXT = "," VAR
+ * <>
+ */
+
+static const void *const njs_parser_var_statement[];
+
+static const njs_parser_switch_t njs_parser_var_next_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 1, {
+ { NJS_TOKEN_COMMA,
+ njs_parser_noop, NULL, njs_parser_var_statement },
+ }
+};
+
+
+static const void *const njs_parser_var_init_expression[] = {
+ &njs_parser_var_next_switch, (void *) njs_parser_switch,
+ NJS_PARSER_NODE, (void *) njs_parser_link_right,
+ &njs_parser_assignment_expression, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_var_init_switch = {
+ NJS_PARSER_TEST_LINE_END,
+ 2, {
+ { NJS_TOKEN_ASSIGNMENT,
+ /* TODO */ njs_parser_binary_expression, (void *) njs_vmcode_move,
+ njs_parser_var_init_expression },
+
+ { NJS_TOKEN_COMMA, /* TODO: free node */
+ njs_parser_noop, NULL, njs_parser_var_statement },
+ }
+};
+
+
+static const void *const njs_parser_var_statement[] = {
+ &njs_parser_var_init_switch, (void *) njs_parser_switch,
+ NJS_PARSER_VOID, (void *) njs_parser_var_name,
+ NULL,
+};
+
+
+/*
+ * The statements.
+ *
+ * STATEMENT = <END>
+ * "var" VAR
+ * ";" STATEMENT
+ * <*> create_node EXPRESSION [ ";" link_right ] STATEMENT
+ */
+
+static const void *const njs_parser_statement[];
+
+static const void *const njs_parser_expression_statement[] = {
+ &njs_parser_statement, (void *) njs_parser_stack_push,
+ NJS_PARSER_NODE, (void *) njs_parser_statement_semicolon,
+ &njs_parser_expression0, (void *) njs_parser_stack_push,
+ NULL,
+};
+
+
+static const njs_parser_switch_t njs_parser_statement_switch = {
+ NJS_PARSER_IGNORE_LINE_END,
+// 4, {
+ 3, {
+ { NJS_TOKEN_VAR, njs_parser_noop, NULL, njs_parser_var_statement },
+ { NJS_TOKEN_END, njs_parser_noop, NULL, NULL },
+#if 0
+ { NJS_TOKEN_SEMICOLON,
+ njs_parser_node, (void *) NJS_TOKEN_STATEMENT, NULL },
+#endif
+
+ { NJS_TOKEN_ANY, njs_parser_node, (void *) NJS_TOKEN_STATEMENT,
+ njs_parser_expression_statement },
+ }
+};
+
+
+static const void *const njs_parser_statement[] = {
+ &njs_parser_statement_switch, (void *) njs_parser_switch,
+ NULL,
+};
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <string.h>
+#include <stdio.h>
+
+
+double
+njs_value_to_number(njs_value_t *value)
+{
+ njs_array_t *array;
+
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ return value->data.u.number;
+ }
+
+ if (njs_is_string(value)) {
+ return njs_string_to_number(value);
+ }
+
+ if (njs_is_array(value)) {
+
+ array = value->data.u.array;
+
+ if (nxt_lvlhsh_is_empty(&array->object.hash)) {
+
+ if (array->length == 0) {
+ /* An empty array value is zero. */
+ return 0.0;
+ }
+
+ if (array->length == 1 && njs_is_valid(&array->start[0])) {
+ /* A single value array is the zeroth array value. */
+ return njs_value_to_number(&array->start[0]);
+ }
+ }
+ }
+
+ return NJS_NAN;
+}
+
+
+double
+njs_number_parse(const u_char **start, const u_char *end)
+{
+ u_char c;
+ double num, frac, scale;
+ const u_char *p;
+
+ /* TODO: "1e2" */
+
+ p = *start;
+ c = *p++;
+
+ /* Values below '0' become >= 208. */
+ c = c - '0';
+
+ num = c;
+
+ while (p < end) {
+ c = *p;
+
+ /* Values below '0' become >= 208. */
+ c = c - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ num = num * 10 + c;
+ p++;
+ }
+
+ if (*p == '.') {
+
+ frac = 0;
+ scale = 1;
+
+ for (p++; p < end; p++) {
+ c = *p;
+
+ /* Values below '0' become >= 208. */
+ c = c - '0';
+
+ if (nxt_slow_path(c > 9)) {
+ break;
+ }
+
+ frac = frac * 10 + c;
+ scale *= 10;
+ }
+
+ num += frac / scale;
+ }
+
+ *start = p;
+
+ return num;
+}
+
+
+njs_ret_t
+njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
+ const njs_value_t *number)
+{
+ u_char *p;
+ double num;
+ size_t size;
+ const char *fmt;
+ const njs_value_t *value;
+ char buf[128];
+
+ num = number->data.u.number;
+
+ if (njs_is_nan(num)) {
+ value = &njs_string_nan;
+
+ } else if (njs_is_infinity(num)) {
+
+ if (num < 0) {
+ value = &njs_string_minus_infinity;
+
+ } else {
+ value = &njs_string_plus_infinity;
+ }
+
+ } else {
+ if (fabs(num) < 1000000) {
+ fmt = "%g";
+
+ } else if (fabs(num) < 1e20) {
+ fmt = "%1.f";
+
+ } else {
+ fmt = "%1.e";
+ }
+
+ size = snprintf(buf, sizeof(buf), fmt, num);
+
+ p = njs_string_alloc(vm, string, size, size);
+
+ if (nxt_fast_path(p != NULL)) {
+ memcpy(p, buf, size);
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+ }
+
+ *string = *value;
+
+ return NXT_OK;
+}
+
+
+njs_ret_t
+njs_number_function(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_object_t *object;
+ const njs_value_t *value;
+
+ if (param->nargs == 0) {
+ value = &njs_value_zero;
+
+ } else {
+ /* TODO: to_number. */
+ value = ¶m->args[0];
+ }
+
+ if (vm->frame->ctor) {
+ /* value->type is the same as prototype offset. */
+ object = njs_object_value_alloc(vm, value, value->type);
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.object = object;
+ vm->retval.type = NJS_OBJECT_NUMBER;
+ vm->retval.data.truth = 1;
+
+ } else {
+ vm->retval = *value;
+ }
+
+ return NXT_OK;
+}
+
+
+static const njs_object_prop_t njs_number_function_properties[] =
+{
+ { njs_string("Number"),
+ njs_string("name"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_value(NJS_NUMBER, 1, 1.0),
+ njs_string("length"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_prototype),
+ njs_string("prototype"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_number_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_number_function_properties,
+ nxt_nitems(njs_number_function_properties));
+}
+
+
+static njs_ret_t
+njs_number_prototype_value_of(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_value_t *value;
+
+ value = param->object;
+
+ if (value->type != NJS_NUMBER) {
+
+ if (value->type == NJS_OBJECT_NUMBER) {
+ value = &value->data.u.object_value->value;
+
+ } else {
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+ }
+
+ vm->retval = *value;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_number_prototype_to_string(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_value_t *value;
+
+ value = param->object;
+
+ if (value->type != NJS_NUMBER) {
+
+ if (value->type == NJS_OBJECT_NUMBER) {
+ value = &value->data.u.object_value->value;
+
+ } else {
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+ }
+
+ return njs_number_to_string(vm, &vm->retval, value);
+}
+
+
+static const njs_object_prop_t njs_number_prototype_properties[] =
+{
+ { njs_native_function(njs_number_prototype_value_of, 0),
+ njs_string("valueOf"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_number_prototype_to_string, 0),
+ njs_string("toString"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_number_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_number_prototype_properties,
+ nxt_nitems(njs_number_prototype_properties));
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_NUMBER_H_INCLUDED_
+#define _NJS_NUMBER_H_INCLUDED_
+
+
+#include <math.h>
+
+
+#define NJS_NAN NAN
+#define NJS_INFINITY INFINITY
+
+
+#define njs_is_infinity(n) \
+ isinf(n)
+
+
+#define njs_is_nan(n) \
+ isnan(n)
+
+
+double njs_value_to_number(njs_value_t *value);
+double njs_number_parse(const u_char **start, const u_char *end);
+njs_ret_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
+ const njs_value_t *number);
+njs_ret_t njs_number_function(njs_vm_t *vm, njs_param_t *param);
+nxt_int_t njs_number_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+nxt_int_t njs_number_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+#endif /* _NJS_NUMBER_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <string.h>
+
+
+static nxt_int_t njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
+static nxt_int_t njs_object_null_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+nxt_noinline njs_object_t *
+njs_object_alloc(njs_vm_t *vm)
+{
+ njs_object_t *obj;
+
+ obj = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_object_t));
+
+ if (nxt_fast_path(obj != NULL)) {
+ nxt_lvlhsh_init(&obj->hash);
+ nxt_lvlhsh_init(&obj->shared_hash);
+ obj->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT];
+ }
+
+ return obj;
+}
+
+
+nxt_noinline njs_object_t *
+njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
+ nxt_uint_t prototype)
+{
+ njs_object_value_t *obj;
+
+ obj = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_object_value_t));
+
+ if (nxt_fast_path(obj != NULL)) {
+ nxt_lvlhsh_init(&obj->object.hash);
+ nxt_lvlhsh_init(&obj->object.shared_hash);
+ obj->object.__proto__ = &vm->prototypes[prototype];
+ obj->value = *value;
+ }
+
+ return &obj->object;
+}
+
+
+nxt_int_t
+njs_object_hash_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
+ const njs_object_prop_t *prop, nxt_uint_t n)
+{
+ nxt_int_t ret;
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.replace = 0;
+ lhq.proto = &njs_object_hash_proto;
+ lhq.pool = vm->mem_cache_pool;
+
+ do {
+ lhq.key.len = prop->name.short_string.size;
+
+ if (lhq.key.len != NJS_STRING_LONG) {
+ lhq.key.data = (u_char *) prop->name.short_string.start;
+
+ } else {
+ lhq.key.len = prop->name.data.string_size;
+ lhq.key.data = prop->name.data.u.string->start;
+ }
+
+ lhq.key_hash = nxt_djb_hash(lhq.key.data, lhq.key.len);
+ lhq.value = (void *) prop;
+
+ ret = nxt_lvlhsh_insert(hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ prop++;
+ n--;
+ } while (n != 0);
+
+ return NXT_OK;
+}
+
+
+const nxt_lvlhsh_proto_t njs_object_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_object_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+static nxt_int_t
+njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ size_t size;
+ u_char *start;
+ njs_object_prop_t *prop;
+
+ prop = data;
+
+ size = prop->name.short_string.size;
+
+ if (size != NJS_STRING_LONG) {
+ if (lhq->key.len != size) {
+ return NXT_DECLINED;
+ }
+
+ start = prop->name.short_string.start;
+
+ } else {
+ if (lhq->key.len != prop->name.data.string_size) {
+ return NXT_DECLINED;
+ }
+
+ start = prop->name.data.u.string->start;
+ }
+
+ if (memcmp(start, lhq->key.data, lhq->key.len) == 0) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+njs_object_prop_t *
+njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name)
+{
+ njs_object_prop_t *prop;
+
+ prop = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_object_prop_t));
+
+ if (nxt_fast_path(prop != NULL)) {
+ prop->value = njs_value_void;
+
+ /* GC: retain. */
+ prop->name = *name;
+
+ prop->type = NJS_PROPERTY;
+ prop->enumerable = 1;
+ prop->writable = 1;
+ prop->configurable = 1;
+ }
+
+ return prop;
+}
+
+
+nxt_noinline njs_ret_t
+njs_object_method(njs_vm_t *vm, njs_param_t *param, nxt_lvlhsh_query_t *lhq)
+{
+ njs_object_prop_t *prop;
+
+ prop = njs_object_property(vm, param->object->data.u.object, lhq);
+
+ if (nxt_fast_path(prop != NULL)) {
+ return njs_function_apply(vm, &prop->value, param);
+ }
+
+ return NXT_ERROR;
+}
+
+
+nxt_noinline njs_object_prop_t *
+njs_object_property(njs_vm_t *vm, njs_object_t *obj, nxt_lvlhsh_query_t *lhq)
+{
+ nxt_int_t ret;
+
+ lhq->proto = &njs_object_hash_proto;
+
+ do {
+ ret = nxt_lvlhsh_find(&obj->hash, lhq);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return lhq->value;
+ }
+
+ ret = nxt_lvlhsh_find(&obj->shared_hash, lhq);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return lhq->value;
+ }
+
+ obj = obj->__proto__;
+
+ } while (obj != NULL);
+
+ vm->exception = &njs_exception_type_error;
+
+ return NULL;
+}
+
+
+njs_ret_t
+njs_object_function(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_uint_t type;
+ njs_value_t *value;
+ njs_object_t *object;
+
+ type = NJS_OBJECT;
+
+ if (param->nargs == 0 || njs_is_null_or_void(¶m->args[0])) {
+
+ object = njs_object_alloc(vm);
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ } else {
+ value = ¶m->args[0];
+
+ if (njs_is_object(value)) {
+ object = value->data.u.object;
+
+ } else if (njs_is_primitive(value)) {
+
+ /* value->type is the same as prototype offset. */
+ object = njs_object_value_alloc(vm, value, value->type);
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ type = NJS_OBJECT + value->type;
+
+ } else {
+ vm->exception = &njs_exception_type_error;
+
+ return NXT_ERROR;
+ }
+ }
+
+ vm->retval.data.u.object = object;
+ vm->retval.type = type;
+ vm->retval.data.truth = 1;
+
+ return NXT_OK;
+}
+
+
+/* TODO: properties with attributes. */
+
+static njs_ret_t
+njs_object_create(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_value_t *args;
+ njs_object_t *obj;
+
+ /* STUB: move to shared create to avoid threads locks. */
+ static nxt_lvlhsh_t njs_null_proto_shared_hash;
+
+ if (param->nargs != 0) {
+ args = param->args;
+
+ if (njs_is_object(&args[0]) || njs_is_null(&args[0])) {
+
+ obj = njs_object_alloc(vm);
+ if (nxt_slow_path(obj == NULL)) {
+ return NXT_ERROR;
+ }
+
+ if (njs_is_null(&args[0])) {
+ if (nxt_lvlhsh_is_empty(&njs_null_proto_shared_hash)) {
+ /* STUB */
+ njs_object_null_hash(vm, &njs_null_proto_shared_hash);
+ }
+
+ obj->shared_hash = njs_null_proto_shared_hash;
+ obj->__proto__ = NULL;
+
+ } else {
+ /* GC */
+ obj->__proto__ = args[0].data.u.object;
+ }
+
+ vm->retval.data.u.object = obj;
+ vm->retval.type = NJS_OBJECT;
+ vm->retval.data.truth = 1;
+
+ return NXT_OK;
+ }
+ }
+
+ vm->exception = &njs_exception_type_error;
+
+ return NXT_ERROR;
+}
+
+
+static const njs_object_prop_t njs_object_null_properties[] =
+{
+ { njs_value(NJS_NULL, 0, 0.0),
+ njs_string("__proto__"),
+ NJS_WHITEOUT, 0, 0, 0, },
+};
+
+
+static nxt_int_t
+njs_object_null_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_object_null_properties,
+ nxt_nitems(njs_object_null_properties));
+}
+
+
+/*
+ * The "prototype" property of Object(), Array() and other functions is
+ * created on demand in the functions' private hash by the "prototype"
+ * getter. The properties are set to appropriate prototype.
+ */
+
+njs_ret_t
+njs_object_prototype_create_prototype(njs_vm_t *vm, njs_value_t *value)
+{
+ int32_t index;
+ nxt_int_t ret;
+ njs_object_t *prototype;
+ njs_function_t *function;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ function = value->data.u.function;
+ index = function - vm->functions;
+
+ if (index < 0 && index > NJS_PROTOTYPE_MAX) {
+ vm->retval = njs_value_void;
+ return NXT_OK;
+ }
+
+ prop = njs_object_prop_alloc(vm, &njs_string_prototype);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ prototype = &vm->prototypes[index];
+
+ prop->value.data.u.object = prototype;
+ prop->value.type = NJS_OBJECT;
+ prop->value.data.truth = 1;
+
+ prop->enumerable = 0;
+ prop->writable = 0;
+ prop->configurable = 0;
+
+ lhq.value = prop;
+ lhq.key_hash = NJS_PROTOTYPE_HASH;
+ lhq.key.len = sizeof("prototype") - 1;
+ lhq.key.data = (u_char *) "prototype";
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&function->object.hash, &lhq);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ vm->retval = prop->value;
+ }
+
+ /* TODO: exception NXT_ERROR. */
+
+ return ret;
+}
+
+
+static const njs_object_prop_t njs_object_function_properties[] =
+{
+ { njs_string("Object"),
+ njs_string("name"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_prototype),
+ njs_string("prototype"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_native_function(njs_object_create, 0),
+ njs_string("create"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_object_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_object_function_properties,
+ nxt_nitems(njs_object_function_properties));
+}
+
+
+static njs_ret_t
+njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value)
+{
+ njs_object_t *proto;
+
+ proto = value->data.u.object->__proto__;
+
+ if (nxt_fast_path(proto != NULL)) {
+ vm->retval.data.u.object = proto;
+ vm->retval.type = NJS_OBJECT;
+ vm->retval.data.truth = 1;
+
+ } else {
+ vm->retval = njs_value_null;
+ }
+
+ return NXT_OK;
+}
+
+
+/*
+ * The "constructor" property of Object(), Array() and other functions
+ * prototypes is created on demand in the prototypes' private hash by the
+ * "constructor" getter. The properties are set to appropriate function.
+ */
+
+static njs_ret_t
+njs_object_prototype_create_constructor(njs_vm_t *vm, njs_value_t *value)
+{
+ int32_t index;
+ nxt_int_t ret;
+ njs_value_t *constructor;
+ njs_object_t *prototype;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ if (njs_is_object(value)) {
+ prototype = value->data.u.object;
+
+ do {
+ index = prototype - vm->prototypes;
+
+ if (index >= 0 && index < NJS_PROTOTYPE_MAX) {
+ goto found;
+ }
+
+ prototype = prototype->__proto__;
+
+ } while (prototype != NULL);
+
+ nxt_thread_log_alert("prototype not found");
+
+ return NXT_ERROR;
+
+ } else {
+ index = NJS_PROTOTYPE_BOOLEAN + (value->type - NJS_BOOLEAN);
+ prototype = &vm->prototypes[index];
+ }
+
+found:
+
+ prop = njs_object_prop_alloc(vm, &njs_string_constructor);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ /* GC */
+
+ constructor = &vm->scopes[NJS_SCOPE_GLOBAL][index];
+ prop->value = *constructor;
+
+ prop->enumerable = 0;
+
+ lhq.value = prop;
+ lhq.key_hash = NJS_CONSTRUCTOR_HASH;
+ lhq.key.len = sizeof("constructor") - 1;
+ lhq.key.data = (u_char *) "constructor";
+ lhq.replace = 0;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&prototype->hash, &lhq);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ vm->retval = *constructor;
+ }
+
+ return ret;
+}
+
+
+static njs_ret_t
+njs_object_prototype_value_of(njs_vm_t *vm, njs_param_t *param)
+{
+ vm->retval = *param->object;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_object_prototype_to_string(njs_vm_t *vm, njs_param_t *param)
+{
+ int32_t index;
+ njs_value_t *value;
+ njs_object_t *prototype;
+
+ static const njs_value_t *class_name[] = {
+ /* Primitives. */
+ &njs_string_object_null,
+ &njs_string_object_undefined,
+ &njs_string_object_boolean,
+ &njs_string_object_number,
+ &njs_string_object_string,
+
+ &njs_string_object_function,
+ &njs_string_object_function,
+ &njs_string_empty,
+
+ /* Objects. */
+ &njs_string_object_object,
+ &njs_string_object_array,
+ &njs_string_object_boolean,
+ &njs_string_object_number,
+ &njs_string_object_string,
+ &njs_string_object_function,
+ &njs_string_object_regexp,
+ };
+
+ value = param->object;
+ index = value->type;
+
+ if (njs_is_object(value)) {
+ prototype = value->data.u.object;
+
+ do {
+ index = prototype - vm->prototypes;
+
+ if (index >= 0 && index < NJS_PROTOTYPE_MAX) {
+ index += NJS_OBJECT;
+ goto found;
+ }
+
+ prototype = prototype->__proto__;
+
+ } while (prototype != NULL);
+
+ nxt_thread_log_alert("prototype not found");
+
+ return NXT_ERROR;
+ }
+
+found:
+
+ vm->retval = *class_name[index];
+
+ return NXT_OK;
+}
+
+
+static const njs_object_prop_t njs_object_prototype_properties[] =
+{
+ { njs_getter(njs_object_prototype_get_proto),
+ njs_string("__proto__"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_constructor),
+ njs_string("constructor"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_native_function(njs_object_prototype_value_of, 0),
+ njs_string("valueOf"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_object_prototype_to_string, 0),
+ njs_string("toString"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_object_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_object_prototype_properties,
+ nxt_nitems(njs_object_prototype_properties));
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_OBJECT_H_INCLUDED_
+#define _NJS_OBJECT_H_INCLUDED_
+
+
+struct njs_object_s {
+ /* A private hash of njs_object_prop_t. */
+ nxt_lvlhsh_t hash;
+
+ /* A shared hash of njs_object_prop_t. */
+ nxt_lvlhsh_t shared_hash;
+
+ /* An object __proto__. */
+ njs_object_t *__proto__;
+};
+
+
+struct njs_object_value_s {
+ njs_object_t object;
+ njs_value_t value;
+};
+
+
+typedef enum {
+ NJS_PROPERTY = 0,
+ NJS_GETTER,
+ NJS_SETTER,
+ NJS_METHOD,
+ NJS_NATIVE_GETTER,
+ NJS_NATIVE_SETTER,
+ NJS_WHITEOUT,
+} njs_ojbect_property_type_t;
+
+
+typedef struct {
+ /* Must be aligned to njs_value_t. */
+ njs_value_t value;
+ njs_value_t name;
+
+ njs_ojbect_property_type_t type:8; /* 3 bits */
+ uint8_t enumerable; /* 1 bit */
+ uint8_t writable; /* 1 bit */
+ uint8_t configurable; /* 1 bit */
+} njs_object_prop_t;
+
+
+njs_object_t *njs_object_alloc(njs_vm_t *vm);
+njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
+ nxt_uint_t prototype);
+njs_ret_t njs_object_method(njs_vm_t *vm, njs_param_t *param,
+ nxt_lvlhsh_query_t *lhq);
+njs_object_prop_t *njs_object_property(njs_vm_t *vm, njs_object_t *obj,
+ nxt_lvlhsh_query_t *lhq);
+nxt_int_t njs_object_hash_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
+ const njs_object_prop_t *prop, nxt_uint_t n);
+njs_ret_t njs_object_function(njs_vm_t *vm, njs_param_t *param);
+njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name);
+njs_ret_t njs_object_prototype_create_prototype(njs_vm_t *vm,
+ njs_value_t *value);
+nxt_int_t njs_object_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+nxt_int_t njs_object_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+#endif /* _NJS_OBJECT_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_OBJECT_HASH_H_INCLUDED_
+#define _NJS_OBJECT_HASH_H_INCLUDED_
+
+
+#define NJS_CONSTRUCTOR_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'c'), 'o'), 'n'), 's'), 't'), 'r'), 'u'), 'c'), 't'), 'o'), 'r')
+
+
+#define NJS_INDEX_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'i'), 'n'), 'd'), 'e'), 'x')
+
+
+#define NJS_INPUT_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'i'), 'n'), 'p'), 'u'), 't')
+
+
+#define NJS_JOIN_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'j'), 'o'), 'i'), 'n')
+
+
+#define NJS_PROTOTYPE_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'p'), 'r'), 'o'), 't'), 'o'), 't'), 'y'), 'p'), 'e')
+
+
+#define NJS_TO_STRING_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 't'), 'o'), 'S'), 't'), 'r'), 'i'), 'n'), 'g')
+
+
+#define NJS_VALUE_OF_HASH \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add( \
+ nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
+ 'v'), 'a'), 'l'), 'u'), 'e'), 'O'), 'f')
+
+
+#endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_utf8.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+/*
+ * The LL(2) parser. The two lookahead tokens are required because
+ * JavaScript inserts automatically semicolon at the end of line in
+ * a = 1
+ * b = 2
+ * whilst
+ * a = 1
+ * + b
+ * is treated as a single expiression.
+ */
+
+static njs_token_t njs_parser_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_block(njs_vm_t *vm, njs_parser_t *parser);
+static njs_token_t njs_parser_function_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_return_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_var_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_if_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_while_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_do_while_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_for_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_for_in_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_try_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_block0(njs_vm_t *vm, njs_parser_t *parser);
+static njs_token_t njs_parser_throw_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_grouping_expression(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_object(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *obj);
+static njs_token_t njs_parser_array(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *obj);
+
+
+njs_parser_node_t *
+njs_parser(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node, *left;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return NULL;
+ }
+
+ left = NULL;
+
+ for ( ;; ) {
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+
+ if (vm->exception == NULL) {
+ vm->exception = &njs_exception_syntax_error;
+ }
+
+ nxt_thread_log_error(NXT_LOG_ERR, "ERROR");
+ return NULL;
+ }
+
+ if (parser->node != NULL && parser->node != left) {
+ /*
+ * The statement is not empty block, not just semicolon,
+ * and not a "var" declaration without initialization.
+ */
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NULL;
+ }
+
+ node->token = NJS_TOKEN_STATEMENT;
+ node->left = left;
+ node->right = parser->node;
+ parser->node = node;
+
+ left = node;
+ }
+
+ if (token == NJS_TOKEN_CLOSE_BRACE) {
+ parser->lexer->start--;
+ break;
+ }
+
+ if (token == NJS_TOKEN_END) {
+ break;
+ }
+ }
+
+ return parser->node;
+}
+
+
+static njs_token_t
+njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ switch (token) {
+
+ case NJS_TOKEN_FUNCTION:
+ return njs_parser_function_statement(vm, parser);
+
+ case NJS_TOKEN_RETURN:
+ return njs_parser_return_statement(vm, parser);
+
+ case NJS_TOKEN_VAR:
+ return njs_parser_var_statement(vm, parser);
+
+ case NJS_TOKEN_IF:
+ return njs_parser_if_statement(vm, parser);
+
+ case NJS_TOKEN_WHILE:
+ return njs_parser_while_statement(vm, parser);
+
+ case NJS_TOKEN_DO:
+ return njs_parser_do_while_statement(vm, parser);
+
+ case NJS_TOKEN_FOR:
+ return njs_parser_for_statement(vm, parser);
+
+ case NJS_TOKEN_TRY:
+ return njs_parser_try_statement(vm, parser);
+
+ case NJS_TOKEN_THROW:
+ return njs_parser_throw_statement(vm, parser);
+
+ case NJS_TOKEN_SEMICOLON:
+ parser->node = NULL;
+ return njs_parser_token(parser);
+
+ case NJS_TOKEN_OPEN_BRACE:
+ return njs_parser_block(vm, parser);
+
+ case NJS_TOKEN_CLOSE_BRACE:
+ parser->node = NULL;
+ nxt_thread_log_debug("BLOCK END");
+ return token;
+
+ default:
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ /*
+ * An expression must be terminated by semicolon,
+ * or by a close curly brace or by the end of line.
+ */
+ switch (token) {
+
+ case NJS_TOKEN_SEMICOLON:
+ return njs_parser_token(parser);
+
+ case NJS_TOKEN_CLOSE_BRACE:
+ case NJS_TOKEN_END:
+ return token;
+
+ default:
+ if (parser->lexer->prev_token == NJS_TOKEN_LINE_END) {
+ return token;
+ }
+
+ return NJS_TOKEN_ILLEGAL;
+ }
+ }
+}
+
+
+static njs_token_t
+njs_parser_block(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node, *left;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ left = NULL;
+
+ while (token != NJS_TOKEN_CLOSE_BRACE) {
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (parser->node != NULL) {
+ /* The statement is not empty block and is not just semicolon. */
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_STATEMENT;
+ node->left = left;
+ node->right = parser->node;
+ parser->node = node;
+
+ left = node;
+ }
+ }
+
+ return njs_parser_token(parser);
+}
+
+
+nxt_inline njs_token_t
+njs_parser_match(njs_parser_t *parser, njs_token_t token,
+ njs_token_t match)
+{
+ if (nxt_fast_path(token == match)) {
+ return njs_parser_token(parser);
+ }
+
+ return NJS_TOKEN_ILLEGAL;
+}
+
+
+static njs_token_t
+njs_parser_function_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_str_t *name;
+ nxt_uint_t level;
+ njs_index_t index;
+ njs_value_t *value;
+ njs_token_t token;
+ njs_parser_t *fn_parser;
+ njs_variable_t *arg, *var;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_FUNCTION;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_NAME) {
+ nxt_thread_log_debug("function: %V", &parser->lexer->text);
+
+ var = njs_parser_variable(vm, parser, &level);
+ if (nxt_slow_path(var == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ var->state = NJS_VARIABLE_DECLARED;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ index = var->index;
+ value = njs_variable_value(parser, index);
+
+ } else {
+ /* Anonymous function. */
+ value = nxt_vector_add(parser->scope_values, &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(value == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ index = njs_parser_index(parser, parser->scope);
+ }
+
+ value->type = NJS_FUNCTION;
+ node->index = index;
+
+ token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ value->data.u.function = njs_function_alloc(vm);
+ if (nxt_slow_path(value->data.u.function == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ fn_parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t));
+ if (nxt_slow_path(fn_parser == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ value->data.u.function->code.script->u.parser = fn_parser;
+
+ fn_parser->lexer = parser->lexer;
+ fn_parser->values_hash = vm->shared->values_hash;
+
+ /* njs_return() size. */
+ fn_parser->code_size = sizeof(njs_vmcode_stop_t);
+
+ fn_parser->arguments = nxt_vector_create(4, sizeof(njs_variable_t),
+ &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(fn_parser->arguments == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ fn_parser->parent = parser;
+
+ vm->parser = fn_parser;
+
+ index = NJS_SCOPE_ARGUMENTS;
+
+ /* A "this" reservation. */
+ index += sizeof(njs_value_t);
+
+ while (token != NJS_TOKEN_CLOSE_PARENTHESIS) {
+
+ if (nxt_slow_path(token != NJS_TOKEN_NAME)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ name = &fn_parser->lexer->text;
+
+ nxt_thread_log_debug("arg: %V", name);
+
+ arg = nxt_vector_add(fn_parser->arguments, &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(arg == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ arg->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->len);
+ if (nxt_slow_path(arg->name_start == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ memcpy(arg->name_start, name->data, name->len);
+ arg->name_len = name->len;
+
+ arg->state = NJS_VARIABLE_DECLARED;
+ arg->index = index;
+ index += sizeof(njs_value_t);
+
+ token = njs_parser_token(fn_parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_COMMA) {
+ token = njs_parser_token(fn_parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+ }
+ }
+
+ value->data.u.function->code.script->nargs =
+ njs_index_size(index) / sizeof(njs_value_t);
+
+ token = njs_parser_token(fn_parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_OPEN_BRACE)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ fn_parser->scope_values = nxt_vector_create(4, sizeof(njs_value_t),
+ &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(fn_parser->scope_values == NULL)) {
+ return NXT_ERROR;
+ }
+
+ fn_parser->scope = NJS_SCOPE_LOCAL;
+
+ token = njs_parser_block(vm, fn_parser);
+
+ vm->parser = parser;
+ node->right = fn_parser->node;
+
+ parser->node = node;
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_str_t *name;
+ nxt_uint_t level;
+ njs_index_t index;
+ njs_value_t *value;
+ njs_token_t token;
+ njs_parser_t *fn_parser;
+ njs_variable_t *arg, *var;
+ njs_parser_node_t *node;
+ njs_function_script_t *func;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_FUNCTION_CREATE;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_NAME) {
+ nxt_thread_log_debug("function: %V", &parser->lexer->text);
+
+ var = njs_parser_variable(vm, parser, &level);
+ if (nxt_slow_path(var == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+ }
+
+ value = &node->u.value;
+
+ token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ func = nxt_mem_cache_zalloc(vm->mem_cache_pool,
+ sizeof(njs_function_script_t));
+ if (nxt_slow_path(func == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ value->data.u.data = func;
+
+ fn_parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t));
+ if (nxt_slow_path(fn_parser == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ func->u.parser = fn_parser;
+
+ fn_parser->lexer = parser->lexer;
+ fn_parser->values_hash = vm->shared->values_hash;
+
+ /* njs_return() size. */
+ fn_parser->code_size = sizeof(njs_vmcode_stop_t);
+
+ fn_parser->arguments = nxt_vector_create(4, sizeof(njs_variable_t),
+ &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(fn_parser->arguments == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ fn_parser->parent = parser;
+
+ vm->parser = fn_parser;
+
+ index = NJS_SCOPE_ARGUMENTS;
+
+ /* A "this" reservation. */
+ index += sizeof(njs_value_t);
+
+ while (token != NJS_TOKEN_CLOSE_PARENTHESIS) {
+
+ if (nxt_slow_path(token != NJS_TOKEN_NAME)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ name = &fn_parser->lexer->text;
+
+ nxt_thread_log_debug("arg: %V", name);
+
+ arg = nxt_vector_add(fn_parser->arguments, &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(arg == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ arg->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->len);
+ if (nxt_slow_path(arg->name_start == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ memcpy(arg->name_start, name->data, name->len);
+ arg->name_len = name->len;
+
+ arg->state = NJS_VARIABLE_DECLARED;
+ arg->index = index;
+ index += sizeof(njs_value_t);
+
+ token = njs_parser_token(fn_parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_COMMA) {
+ token = njs_parser_token(fn_parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+ }
+ }
+
+ func->nargs = njs_index_size(index) / sizeof(njs_value_t);
+
+ token = njs_parser_token(fn_parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_OPEN_BRACE)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ fn_parser->scope_values = nxt_vector_create(4, sizeof(njs_value_t),
+ &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(fn_parser->scope_values == NULL)) {
+ return NXT_ERROR;
+ }
+
+ fn_parser->scope = NJS_SCOPE_LOCAL;
+
+ token = njs_parser_block(vm, fn_parser);
+
+ vm->parser = parser;
+ node->right = fn_parser->node;
+
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_function_create_t);
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_return_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_RETURN;
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (parser->node->token == NJS_TOKEN_FUNCTION) {
+ /* TODO: test closure */
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+
+ parser->code_size += sizeof(njs_vmcode_stop_t);
+
+ if (token != NJS_TOKEN_SEMICOLON) {
+ return token;
+ }
+
+ return njs_parser_token(parser);
+}
+
+
+static njs_token_t
+njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_bool_t first;
+ nxt_uint_t level;
+ njs_token_t token;
+ njs_variable_t *var;
+ njs_parser_node_t *left, *stmt, *name, *assign;
+
+ left = NULL;
+
+ do {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token != NJS_TOKEN_NAME) {
+ return token;
+ }
+
+ nxt_thread_log_debug("JS: %V", &parser->lexer->text);
+
+ var = njs_parser_variable(vm, parser, &level);
+ if (nxt_slow_path(var == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ first = (var->state == NJS_VARIABLE_CREATED);
+
+ var->state = NJS_VARIABLE_DECLARED;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_ASSIGNMENT) {
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_var_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ name = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(name == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ name->token = NJS_TOKEN_NAME;
+ name->lvalue = NJS_LVALUE_ENABLED;
+ name->u.variable = var;
+
+ if (first) {
+ name->state = NJS_VARIABLE_FIRST_ASSIGNMENT;
+ }
+
+ assign = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(assign == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ assign->token = NJS_TOKEN_ASSIGNMENT;
+ assign->u.operation = njs_vmcode_move;
+ assign->left = name;
+ assign->right = parser->node;
+
+ stmt = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(stmt == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ stmt->token = NJS_TOKEN_STATEMENT;
+ stmt->left = left;
+ stmt->right = assign;
+ parser->node = stmt;
+ parser->code_size += sizeof(njs_vmcode_2addr_t);
+
+ left = stmt;
+ }
+
+ } while (token == NJS_TOKEN_COMMA);
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_if_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node, *cond, *stmt;
+
+ parser->branch = 1;
+
+ token = njs_parser_grouping_expression(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ cond = parser->node;
+
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_ELSE) {
+
+ stmt = parser->node;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_ELSE;
+ node->left = stmt;
+ node->right = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_jump_t);
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_IF;
+ node->left = cond;
+ node->right = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_cond_jump_t);
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_while_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node, *cond;
+
+ parser->branch = 1;
+
+ token = njs_parser_grouping_expression(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ cond = parser->node;
+
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_WHILE;
+ node->left = parser->node;
+ node->right = cond;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_jump_t)
+ + sizeof(njs_vmcode_cond_jump_t);
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_do_while_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node, *stmt;
+
+ parser->branch = 1;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ stmt = parser->node;
+
+ if (nxt_slow_path(token != NJS_TOKEN_WHILE)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ token = njs_parser_grouping_expression(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ node->token = NJS_TOKEN_DO;
+ node->left = stmt;
+ node->right = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_cond_jump_t);
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_for_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node, *init, *condition, *update, *cond, *body;
+
+ parser->branch = 1;
+
+ init = NULL;
+ condition = NULL;
+ update = NULL;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_match(parser, token,
+ NJS_TOKEN_OPEN_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token != NJS_TOKEN_SEMICOLON) {
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ init = parser->node;
+
+ if (init->token == NJS_TOKEN_IN) {
+ return njs_parser_for_in_statement(vm, parser, token);
+ }
+ }
+
+ token = njs_parser_match(parser, token, NJS_TOKEN_SEMICOLON);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token != NJS_TOKEN_SEMICOLON) {
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ condition = parser->node;
+ }
+
+ token = njs_parser_match(parser, token, NJS_TOKEN_SEMICOLON);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token != NJS_TOKEN_CLOSE_PARENTHESIS) {
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ update = parser->node;
+ }
+
+ token = njs_parser_match(parser, token,
+ NJS_TOKEN_CLOSE_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ cond = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(cond == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ body = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(body == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_FOR;
+ node->left = init;
+ node->right = cond;
+
+ cond->token = NJS_TOKEN_FOR_CONDITION;
+ cond->left = condition;
+ cond->right = body;
+
+ body->token = NJS_TOKEN_FOR_BODY;
+ body->left = parser->node;
+ body->right = update;
+
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_jump_t)
+ + sizeof(njs_vmcode_cond_jump_t);
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_for_in_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ njs_parser_node_t *node;
+
+ if (parser->node->left->token != NJS_TOKEN_NAME) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ parser->node->left->u.variable->state = NJS_VARIABLE_DECLARED;
+ parser->node->token = NJS_TOKEN_PROPERTY_EACH;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_FOR_IN;
+ node->left = parser->node;
+
+ token = njs_parser_match(parser, token,
+ NJS_TOKEN_CLOSE_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_statement(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_prop_start_t)
+ + sizeof(njs_vmcode_prop_each_t);
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_uint_t level;
+ njs_token_t token;
+ njs_variable_t *var;
+ njs_parser_node_t *node, *try, *catch;
+
+ parser->branch = 1;
+
+ token = njs_parser_block0(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ try = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(try == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ try->token = NJS_TOKEN_TRY;
+ try->left = parser->node;
+ parser->code_size += sizeof(njs_vmcode_try_start_t)
+ + sizeof(njs_vmcode_try_end_t);
+
+ if (token == NJS_TOKEN_CATCH) {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_match(parser, token,
+ NJS_TOKEN_OPEN_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token != NJS_TOKEN_NAME) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ nxt_thread_log_debug("CATCH: %V", &parser->lexer->text);
+
+ catch = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(catch == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ catch->token = NJS_TOKEN_CATCH;
+ try->right = catch;
+
+ var = njs_parser_variable(vm, parser, &level);
+ if (nxt_slow_path(var == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ var->state = NJS_VARIABLE_DECLARED;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_NAME;
+ node->u.variable = var;
+
+ catch->left = node;
+
+ parser->code_size += sizeof(njs_vmcode_catch_t);
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_CLOSE_PARENTHESIS)) {
+ return token;
+ }
+
+ token = njs_parser_block0(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ catch->right = parser->node;
+
+ /* TODO: remove variable from scope. */
+ }
+
+ if (token == NJS_TOKEN_FINALLY) {
+
+ token = njs_parser_block0(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_FINALLY;
+ node->right = parser->node;
+
+ if (try->right != NULL) {
+ node->left = try->right;
+ parser->code_size += sizeof(njs_vmcode_try_end_t);
+ }
+
+ try->right = node;
+ parser->code_size += sizeof(njs_vmcode_catch_t)
+ + sizeof(njs_vmcode_finally_t);
+ }
+
+ if (try->right == NULL) {
+ /* TODO: message */
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ parser->node = try;
+
+ return token;
+
+}
+
+
+static njs_token_t
+njs_parser_block0(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+
+ token = njs_parser_token(parser);
+
+ if (nxt_fast_path(token == NJS_TOKEN_OPEN_BRACE)) {
+ parser->node = NULL;
+ return njs_parser_block(vm, parser);
+ }
+
+ return NJS_TOKEN_ILLEGAL;
+}
+
+
+static njs_token_t
+njs_parser_throw_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_THROW;
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+
+ parser->code_size += sizeof(njs_vmcode_throw_t);
+
+ return token;
+}
+
+
+static njs_token_t
+njs_parser_grouping_expression(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_match(parser, token,
+ NJS_TOKEN_OPEN_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ return njs_parser_match(parser, token,
+ NJS_TOKEN_CLOSE_PARENTHESIS);
+}
+
+
+njs_token_t
+njs_parser_token(njs_parser_t *parser)
+{
+ njs_token_t token;
+
+ do {
+ token = njs_lexer_token(parser->lexer);
+
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ } while (nxt_slow_path(token == NJS_TOKEN_LINE_END));
+
+ return token;
+}
+
+
+njs_token_t
+njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ double num;
+ nxt_int_t ret;
+ nxt_uint_t level;
+ njs_extern_t *ext;
+ njs_variable_t *var;
+ njs_parser_node_t *node;
+ njs_regexp_flags_t flags;
+ njs_regexp_pattern_t *pattern;
+
+ if (token == NJS_TOKEN_OPEN_PARENTHESIS) {
+
+ token = njs_lexer_token(parser->lexer);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ return njs_parser_match(parser, token,
+ NJS_TOKEN_CLOSE_PARENTHESIS);
+ }
+
+ if (token == NJS_TOKEN_FUNCTION) {
+ return njs_parser_function_expression(vm, parser);
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+
+ switch (token) {
+
+ case NJS_TOKEN_NAME:
+ nxt_thread_log_debug("JS: %V", &parser->lexer->text);
+
+ ext = njs_parser_external(vm, parser);
+
+ if (ext != NULL) {
+ node->token = NJS_TOKEN_EXTERNAL;
+ node->u.value.type = NJS_EXTERNAL;
+ node->u.value.data.truth = 1;
+ node->index = (njs_index_t) ext;
+ break;
+ }
+
+ var = njs_parser_variable(vm, parser, &level);
+ if (nxt_slow_path(var == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ switch (var->state) {
+
+ case NJS_VARIABLE_CREATED:
+ var->state = NJS_VARIABLE_PENDING;
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+ break;
+
+ case NJS_VARIABLE_PENDING:
+ var->state = NJS_VARIABLE_USED;
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+ break;
+
+ case NJS_VARIABLE_USED:
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+ break;
+
+ case NJS_VARIABLE_SET:
+ case NJS_VARIABLE_DECLARED:
+ break;
+ }
+
+ node->lvalue = NJS_LVALUE_ENABLED;
+ node->u.variable = var;
+ break;
+
+ case NJS_TOKEN_OPEN_BRACE:
+ node->token = NJS_TOKEN_OBJECT_CREATE;
+
+ nxt_thread_log_debug("JS: OBJECT");
+
+ parser->node = node;
+
+ token = njs_parser_object(vm, parser, node);
+
+ if (parser->node != node) {
+ /* The object is not empty. */
+ node->left = parser->node;
+ parser->node = node;
+ }
+
+ parser->code_size += sizeof(njs_vmcode_1addr_t);
+
+ return token;
+
+ case NJS_TOKEN_OPEN_BRACKET:
+ node->token = NJS_TOKEN_ARRAY_CREATE;
+
+ nxt_thread_log_debug("JS: ARRAY");
+
+ parser->node = node;
+
+ token = njs_parser_array(vm, parser, node);
+
+ if (parser->node != node) {
+ /* The array is not empty. */
+ node->left = parser->node;
+ parser->node = node;
+ }
+
+ parser->code_size += sizeof(njs_vmcode_2addr_t);
+
+ return token;
+
+ case NJS_TOKEN_DIVISION:
+ token = njs_lexer_regexp(parser->lexer, &flags);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->token = token;
+
+ nxt_thread_log_debug("REGEX: '%V'", &parser->lexer->text);
+
+ pattern = njs_regexp_pattern_create(vm, &parser->lexer->text, flags);
+ if (nxt_slow_path(pattern == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->u.value.data.u.data = pattern;
+ parser->code_size += sizeof(njs_vmcode_regexp_t);
+
+ break;
+
+ case NJS_TOKEN_STRING:
+ nxt_thread_log_debug("JS: '%V'", &parser->lexer->text);
+
+ ret = njs_parser_string_create(vm, &node->u.value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ break;
+
+ case NJS_TOKEN_NUMBER:
+ nxt_thread_log_debug("JS: %f", parser->lexer->number);
+
+ num = parser->lexer->number;
+ node->u.value.data.u.number = num;
+ node->u.value.type = NJS_NUMBER;
+ node->u.value.data.truth = njs_is_number_true(num);
+
+ break;
+
+ case NJS_TOKEN_BOOLEAN:
+ nxt_thread_log_debug("JS: boolean: %V", &parser->lexer->text);
+
+ if (parser->lexer->number == 0) {
+ node->u.value = njs_value_false;
+
+ } else {
+ node->u.value = njs_value_true;
+ }
+
+ break;
+
+ case NJS_TOKEN_NULL:
+ nxt_thread_log_debug("JS: null");
+
+ node->u.value = njs_value_null;
+ break;
+
+ case NJS_TOKEN_UNDEFINED:
+ nxt_thread_log_debug("JS: undefined");
+
+ node->u.value = njs_value_void;
+ break;
+
+ case NJS_TOKEN_THIS:
+ nxt_thread_log_debug("JS: this");
+
+ node->index = NJS_INDEX_THIS;
+ break;
+
+ case NJS_TOKEN_OBJECT_FUNCTION:
+ node->index = NJS_INDEX_OBJECT;
+ break;
+
+ case NJS_TOKEN_ARRAY_FUNCTION:
+ node->index = NJS_INDEX_ARRAY;
+ break;
+
+ case NJS_TOKEN_BOOLEAN_FUNCTION:
+ node->index = NJS_INDEX_BOOLEAN;
+ break;
+
+ case NJS_TOKEN_NUMBER_FUNCTION:
+ node->index = NJS_INDEX_NUMBER;
+ break;
+
+ case NJS_TOKEN_STRING_FUNCTION:
+ node->index = NJS_INDEX_STRING;
+ break;
+
+ case NJS_TOKEN_FUNCTION_FUNCTION:
+ node->index = NJS_INDEX_FUNCTION;
+ break;
+
+ case NJS_TOKEN_REGEXP_FUNCTION:
+ node->index = NJS_INDEX_REGEXP;
+ break;
+
+ case NJS_TOKEN_EVAL:
+ node->index = NJS_INDEX_EVAL;
+ break;
+
+ default:
+ vm->exception = &njs_exception_syntax_error;
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ parser->node = node;
+
+ return njs_lexer_token(parser->lexer);
+}
+
+
+static njs_token_t
+njs_parser_object(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *obj)
+{
+ njs_token_t token;
+ njs_parser_node_t *stmt, *assign, *object, *propref, *left;
+
+ left = NULL;
+
+ for ( ;; ) {
+ token = njs_parser_token(parser);
+
+ switch (token) {
+
+ case NJS_TOKEN_CLOSE_BRACE:
+ return njs_parser_token(parser);
+
+ case NJS_TOKEN_NAME:
+ token = njs_parser_property_name(vm, parser, token);
+ break;
+
+ default:
+ token = njs_parser_terminal(vm, parser, token);
+ break;
+ }
+
+ object = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(object == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ object->token = NJS_TOKEN_OBJECT_LITERAL;
+ object->u.object = obj;
+
+ propref = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(propref == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ propref->token = NJS_TOKEN_PROPERTY;
+ propref->left = object;
+ propref->right = parser->node;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_match(parser, token, NJS_TOKEN_COLON);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_conditional_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ assign = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(assign == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ assign->token = NJS_TOKEN_ASSIGNMENT;
+ assign->u.operation = njs_vmcode_move;
+ assign->left = propref;
+ assign->right = parser->node;
+
+ stmt = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(stmt == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ stmt->token = NJS_TOKEN_STATEMENT;
+ stmt->left = left;
+ stmt->right = assign;
+
+ parser->code_size += sizeof(njs_vmcode_2addr_t);
+ parser->node = stmt;
+
+ left = stmt;
+
+ if (token == NJS_TOKEN_CLOSE_BRACE) {
+ return njs_parser_token(parser);
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_COMMA)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+ }
+}
+
+
+static njs_token_t
+njs_parser_array(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *obj)
+{
+ nxt_uint_t index;
+ njs_token_t token;
+ njs_parser_node_t *stmt, *assign, *object, *propref, *left, *node;
+
+ index = 0;
+ left = NULL;
+
+ for ( ;; ) {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_CLOSE_BRACKET) {
+ break;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_NUMBER;
+ node->u.value.data.u.number = index;
+ node->u.value.type = NJS_NUMBER;
+ node->u.value.data.truth = (index != 0);
+ index++;
+
+ object = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(object == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ object->token = NJS_TOKEN_OBJECT_LITERAL;
+ object->u.object = obj;
+
+ propref = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(propref == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ propref->token = NJS_TOKEN_PROPERTY;
+ propref->left = object;
+ propref->right = node;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ token = njs_parser_conditional_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ assign = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(assign == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ assign->token = NJS_TOKEN_ASSIGNMENT;
+ assign->u.operation = njs_vmcode_move;
+ assign->left = propref;
+ assign->right = parser->node;
+
+ stmt = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(stmt == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ stmt->token = NJS_TOKEN_STATEMENT;
+ stmt->left = left;
+ stmt->right = assign;
+
+ parser->code_size += sizeof(njs_vmcode_2addr_t);
+ parser->node = stmt;
+
+ left = stmt;
+
+ if (token == NJS_TOKEN_CLOSE_BRACKET) {
+ break;
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_COMMA)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+ }
+
+ obj->u.length = index;
+
+ return njs_parser_token(parser);
+}
+
+
+nxt_int_t
+njs_parser_string_create(njs_vm_t *vm, njs_value_t *value)
+{
+ u_char *p;
+ ssize_t length;
+ nxt_str_t *src;
+
+ src = &vm->parser->lexer->text;
+
+ length = nxt_utf8_length(src->data, src->len);
+
+ if (nxt_slow_path(length < 0)) {
+ length = 0;
+ }
+
+ p = njs_string_alloc(vm, value, src->len, length);
+
+ if (nxt_fast_path(p != NULL)) {
+ memcpy(p, src->data, src->len);
+
+ if (length > NJS_STRING_MAP_OFFSET && (size_t) length != src->len) {
+ njs_string_offset_map_init(p, src->len);
+ }
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_index_t
+njs_parser_index(njs_parser_t *parser, uint32_t scope)
+{
+ nxt_uint_t n;
+ njs_index_t index;
+
+ /* Skip absolute scope. */
+ n = scope - NJS_INDEX_CACHE;
+
+ index = parser->index[n];
+ parser->index[n] += sizeof(njs_value_t);
+
+ index |= scope;
+
+ nxt_thread_log_debug("GET %p", index);
+
+ return index;
+}
+
+
+nxt_bool_t
+njs_parser_has_side_effect(njs_parser_node_t *node)
+{
+ nxt_bool_t side_effect;
+
+ if (node == NULL) {
+ return 0;
+ }
+
+ if (node->token >= NJS_TOKEN_ASSIGNMENT
+ && node->token <= NJS_TOKEN_LAST_ASSIGNMENT)
+ {
+ return 1;
+ }
+
+ if (node->token == NJS_TOKEN_FUNCTION_CALL
+ || node->token == NJS_TOKEN_METHOD_CALL)
+ {
+ return 1;
+ }
+
+ side_effect = njs_parser_has_side_effect(node->left);
+
+ if (nxt_fast_path(!side_effect)) {
+ return njs_parser_has_side_effect(node->right);
+ }
+
+ return side_effect;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_PARSER_H_INCLUDED_
+#define _NJS_PARSER_H_INCLUDED_
+
+
+typedef enum {
+ NJS_TOKEN_AGAIN = -2,
+ NJS_TOKEN_ERROR = -1,
+ NJS_TOKEN_ILLEGAL = 0,
+
+ NJS_TOKEN_END,
+ NJS_TOKEN_SPACE,
+ NJS_TOKEN_LINE_END,
+
+ NJS_TOKEN_DOUBLE_QUOTE,
+ NJS_TOKEN_SINGLE_QUOTE,
+
+ NJS_TOKEN_OPEN_PARENTHESIS,
+ NJS_TOKEN_CLOSE_PARENTHESIS,
+ NJS_TOKEN_OPEN_BRACKET,
+ NJS_TOKEN_CLOSE_BRACKET,
+ NJS_TOKEN_OPEN_BRACE,
+ NJS_TOKEN_CLOSE_BRACE,
+
+ NJS_TOKEN_COMMA,
+ NJS_TOKEN_DOT,
+ NJS_TOKEN_SEMICOLON,
+
+#define NJS_TOKEN_FIRST_OPERATOR NJS_TOKEN_COLON
+
+ NJS_TOKEN_COLON,
+ NJS_TOKEN_CONDITIONAL,
+
+ NJS_TOKEN_ASSIGNMENT,
+ NJS_TOKEN_ADDITION_ASSIGNMENT,
+ NJS_TOKEN_SUBSTRACTION_ASSIGNMENT,
+ NJS_TOKEN_MULTIPLICATION_ASSIGNMENT,
+ NJS_TOKEN_DIVISION_ASSIGNMENT,
+ NJS_TOKEN_REMAINDER_ASSIGNMENT,
+ NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT,
+ NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT,
+ NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT,
+ NJS_TOKEN_BITWISE_OR_ASSIGNMENT,
+ NJS_TOKEN_BITWISE_XOR_ASSIGNMENT,
+ NJS_TOKEN_BITWISE_AND_ASSIGNMENT,
+
+#define NJS_TOKEN_LAST_ASSIGNMENT NJS_TOKEN_BITWISE_AND_ASSIGNMENT
+
+ NJS_TOKEN_EQUAL,
+ NJS_TOKEN_STRICT_EQUAL,
+ NJS_TOKEN_NOT_EQUAL,
+ NJS_TOKEN_STRICT_NOT_EQUAL,
+
+ NJS_TOKEN_ADDITION,
+ NJS_TOKEN_UNARY_PLUS,
+ NJS_TOKEN_INCREMENT,
+ NJS_TOKEN_POST_INCREMENT,
+
+ NJS_TOKEN_SUBSTRACTION,
+ NJS_TOKEN_UNARY_NEGATION,
+ NJS_TOKEN_DECREMENT,
+ NJS_TOKEN_POST_DECREMENT,
+
+ NJS_TOKEN_MULTIPLICATION,
+
+ NJS_TOKEN_DIVISION,
+
+ NJS_TOKEN_REMAINDER,
+
+ NJS_TOKEN_LESS,
+ NJS_TOKEN_LESS_OR_EQUAL,
+ NJS_TOKEN_LEFT_SHIFT,
+
+ NJS_TOKEN_GREATER,
+ NJS_TOKEN_GREATER_OR_EQUAL,
+ NJS_TOKEN_RIGHT_SHIFT,
+ NJS_TOKEN_UNSIGNED_RIGHT_SHIFT,
+
+ NJS_TOKEN_BITWISE_OR,
+ NJS_TOKEN_LOGICAL_OR,
+
+ NJS_TOKEN_BITWISE_XOR,
+
+ NJS_TOKEN_BITWISE_AND,
+ NJS_TOKEN_LOGICAL_AND,
+
+ NJS_TOKEN_BITWISE_NOT,
+ NJS_TOKEN_LOGICAL_NOT,
+
+ NJS_TOKEN_IN,
+ NJS_TOKEN_INSTANCEOF,
+ NJS_TOKEN_TYPEOF,
+ NJS_TOKEN_VOID,
+ NJS_TOKEN_NEW,
+ NJS_TOKEN_DELETE,
+ NJS_TOKEN_YIELD,
+
+#define NJS_TOKEN_LAST_OPERATOR NJS_TOKEN_YIELD
+
+ NJS_TOKEN_DIGIT,
+ NJS_TOKEN_LETTER,
+
+#define NJS_TOKEN_FIRST_CONST NJS_TOKEN_UNDEFINED
+
+ NJS_TOKEN_UNDEFINED,
+ NJS_TOKEN_NULL,
+ NJS_TOKEN_NUMBER,
+ NJS_TOKEN_BOOLEAN,
+ NJS_TOKEN_STRING,
+
+#define NJS_TOKEN_LAST_CONST NJS_TOKEN_STRING
+
+ NJS_TOKEN_NAME,
+
+ NJS_TOKEN_OBJECT_CREATE,
+ NJS_TOKEN_OBJECT_LITERAL,
+ NJS_TOKEN_PROPERTY,
+ NJS_TOKEN_PROPERTY_DELETE,
+
+ NJS_TOKEN_ARRAY_CREATE,
+
+ NJS_TOKEN_FUNCTION_CREATE,
+ NJS_TOKEN_FUNCTION,
+ NJS_TOKEN_FUNCTION_CALL,
+ NJS_TOKEN_METHOD_CALL,
+ NJS_TOKEN_ARGUMENT,
+ NJS_TOKEN_RETURN,
+
+ NJS_TOKEN_REGEXP_LITERAL,
+
+ NJS_TOKEN_EXTERNAL,
+
+ NJS_TOKEN_STATEMENT,
+ NJS_TOKEN_VAR,
+ NJS_TOKEN_IF,
+ NJS_TOKEN_ELSE,
+ NJS_TOKEN_WHILE,
+ NJS_TOKEN_DO,
+ NJS_TOKEN_FOR,
+ NJS_TOKEN_FOR_CONDITION,
+ NJS_TOKEN_FOR_BODY,
+ NJS_TOKEN_FOR_IN,
+ NJS_TOKEN_PROPERTY_EACH,
+ NJS_TOKEN_BREAK,
+ NJS_TOKEN_CONTINUE,
+ NJS_TOKEN_SWITCH,
+ NJS_TOKEN_CASE,
+ NJS_TOKEN_DEFAULT,
+ NJS_TOKEN_WITH,
+ NJS_TOKEN_TRY,
+ NJS_TOKEN_CATCH,
+ NJS_TOKEN_FINALLY,
+ NJS_TOKEN_THROW,
+
+ NJS_TOKEN_THIS,
+
+ NJS_TOKEN_OBJECT_FUNCTION,
+ NJS_TOKEN_ARRAY_FUNCTION,
+ NJS_TOKEN_FUNCTION_FUNCTION,
+ NJS_TOKEN_REGEXP_FUNCTION,
+ NJS_TOKEN_BOOLEAN_FUNCTION,
+ NJS_TOKEN_NUMBER_FUNCTION,
+ NJS_TOKEN_STRING_FUNCTION,
+ NJS_TOKEN_EVAL,
+
+ NJS_TOKEN_RESERVED,
+} njs_token_t;
+
+
+typedef struct {
+ njs_token_t token:8;
+ njs_token_t prev_token:8;
+ uint32_t key_hash;
+
+ nxt_str_t text;
+ double number;
+
+ nxt_lvlhsh_t keywords_hash;
+
+ u_char *start;
+ u_char *end;
+} njs_lexer_t;
+
+
+typedef enum {
+ NJS_VARIABLE_NORMAL = 0,
+ NJS_VARIABLE_FIRST_ASSIGNMENT,
+ NJS_VARIABLE_ASSIGNMENT,
+ NJS_VARIABLE_TYPEOF,
+} njs_variable_node_state_t;
+
+
+typedef enum {
+ NJS_LVALUE_NONE = 0,
+ NJS_LVALUE_ENABLED,
+ NJS_LVALUE_ASSIGNED,
+} njs_lvalue_state_t;
+
+
+typedef struct njs_parser_node_s njs_parser_node_t;
+
+struct njs_parser_node_s {
+ njs_token_t token:8;
+ njs_variable_node_state_t state:8; /* 2 bits */
+ njs_lvalue_state_t lvalue:2; /* 2 bits */
+ uint8_t ctor:1; /* 1 bit */
+ uint8_t temporary; /* 1 bit */
+
+ union {
+ uint32_t length;
+ njs_vmcode_operation_t operation;
+ njs_value_t value;
+ njs_variable_t *variable;
+ njs_parser_node_t *object;
+ njs_extern_t *external;
+ } u;
+
+ njs_index_t index;
+
+ njs_parser_node_t *left;
+ njs_parser_node_t *right;
+ njs_parser_node_t *dest;
+};
+
+
+#define \
+njs_parser_node_alloc(vm) \
+ nxt_mem_cache_zalloc((vm)->mem_cache_pool, sizeof(njs_parser_node_t))
+
+
+struct njs_parser_s {
+ njs_lexer_t *lexer;
+ njs_parser_node_t *node;
+
+ /* Vector of njs_variable_t. */
+ nxt_vector_t *arguments;
+
+ nxt_lvlhsh_t variables_hash;
+ nxt_lvlhsh_t values_hash;
+ nxt_lvlhsh_t functions_hash;
+
+ nxt_vector_t *index_cache;
+ njs_index_t index[NJS_SCOPES - NJS_INDEX_CACHE];
+
+ nxt_vector_t *scope_values;
+
+ uint8_t scope; /* 4 bits */
+ uint8_t branch; /* 1 bit */
+
+ size_t code_size;
+
+ /* Generator. */
+
+ njs_value_t *local_scope;
+ size_t scope_size;
+ size_t scope_offset;
+
+ uint32_t nesting_arguments;
+ uint32_t nesting_arguments_size;
+ uint32_t method_arguments_size;
+
+ u_char *code_start;
+ u_char *code_last;
+
+ njs_parser_t *parent;
+};
+
+
+njs_token_t njs_lexer_token(njs_lexer_t *lexer);
+njs_token_t njs_lexer_regexp(njs_lexer_t *lexer, njs_regexp_flags_t *flags);
+nxt_int_t njs_lexer_keywords_init(nxt_mem_cache_pool_t *mcp,
+ nxt_lvlhsh_t *hash);
+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_nonrecursive_parser(njs_vm_t *vm, njs_parser_t *parser);
+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,
+ njs_token_t token);
+njs_token_t njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token);
+njs_token_t njs_parser_conditional_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+njs_token_t njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token);
+njs_token_t njs_parser_property_name(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token);
+njs_token_t njs_parser_token(njs_parser_t *parser);
+nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
+njs_index_t njs_parser_index(njs_parser_t *parser, uint32_t scope);
+nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
+nxt_int_t njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node, njs_vmcode_operation_t last);
+
+
+#define \
+njs_generate_code(parser, type, code) \
+ do { \
+ code = (type *) parser->code_last; parser->code_last += sizeof(type); \
+ } while (0)
+
+
+#endif /* _NJS_PARSER_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+typedef struct {
+ njs_token_t token;
+ njs_vmcode_operation_t operation;
+} njs_parser_operation_t;
+
+
+typedef struct njs_parser_expression_s
+ njs_parser_expression_t;
+
+struct njs_parser_expression_s {
+ njs_token_t (*next)(njs_vm_t *,
+ njs_parser_t *,
+ const njs_parser_expression_t *,
+ njs_token_t);
+ const njs_parser_expression_t *expression;
+ nxt_uint_t count;
+
+#if (NXT_SUNC)
+ /*
+ * SunC supports C99 flexible array members but does not allow
+ * static struct's initialization with arbitrary number of members.
+ */
+ njs_parser_operation_t op[6];
+#else
+ njs_parser_operation_t op[];
+#endif
+};
+
+
+static njs_token_t njs_parser_assignment_expression(njs_vm_t *vm,
+ njs_parser_t *parser, const njs_parser_expression_t *expr,
+ njs_token_t token);
+static njs_token_t njs_parser_binary_expression(njs_vm_t *vm,
+ njs_parser_t *parser, const njs_parser_expression_t *expr,
+ njs_token_t token);
+static njs_token_t njs_parser_unary_expression(njs_vm_t *vm,
+ njs_parser_t *parser, const njs_parser_expression_t *expr,
+ njs_token_t token);
+static njs_token_t njs_parser_inc_dec_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_post_inc_dec_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_call_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_property_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_property_brackets(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
+
+
+static const njs_parser_expression_t
+ njs_parser_factor_expression =
+{
+ njs_parser_unary_expression,
+ NULL,
+ 3, {
+ { NJS_TOKEN_MULTIPLICATION, njs_vmcode_multiplication },
+ { NJS_TOKEN_DIVISION, njs_vmcode_division },
+ { NJS_TOKEN_REMAINDER, njs_vmcode_remainder },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_addition_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_factor_expression,
+ 2, {
+ { NJS_TOKEN_ADDITION, njs_vmcode_addition },
+ { NJS_TOKEN_SUBSTRACTION, njs_vmcode_substraction },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_bitwise_shift_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_addition_expression,
+ 3, {
+ { NJS_TOKEN_LEFT_SHIFT, njs_vmcode_left_shift },
+ { NJS_TOKEN_RIGHT_SHIFT, njs_vmcode_right_shift },
+ { NJS_TOKEN_UNSIGNED_RIGHT_SHIFT,
+ njs_vmcode_unsigned_right_shift },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_relational_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_bitwise_shift_expression,
+ 6, {
+ { NJS_TOKEN_LESS, njs_vmcode_less },
+ { NJS_TOKEN_LESS_OR_EQUAL, njs_vmcode_less_or_equal },
+ { NJS_TOKEN_GREATER, njs_vmcode_greater },
+ { NJS_TOKEN_GREATER_OR_EQUAL, njs_vmcode_greater_or_equal },
+ { NJS_TOKEN_IN, njs_vmcode_property_in },
+ { NJS_TOKEN_INSTANCEOF, njs_vmcode_instance_of },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_equality_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_relational_expression,
+ 4, {
+ { NJS_TOKEN_EQUAL, njs_vmcode_equal },
+ { NJS_TOKEN_NOT_EQUAL, njs_vmcode_not_equal },
+ { NJS_TOKEN_STRICT_EQUAL, njs_vmcode_strict_equal },
+ { NJS_TOKEN_STRICT_NOT_EQUAL, njs_vmcode_strict_not_equal },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_bitwise_and_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_equality_expression,
+ 1, {
+ { NJS_TOKEN_BITWISE_AND, njs_vmcode_bitwise_and },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_bitwise_xor_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_bitwise_and_expression,
+ 1, {
+ { NJS_TOKEN_BITWISE_XOR, njs_vmcode_bitwise_xor },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_bitwise_or_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_bitwise_xor_expression,
+ 1, {
+ { NJS_TOKEN_BITWISE_OR, njs_vmcode_bitwise_or },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_logical_and_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_bitwise_or_expression,
+ 1, {
+ { NJS_TOKEN_LOGICAL_AND, njs_vmcode_logical_and },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_logical_or_expression =
+{
+ njs_parser_binary_expression,
+ &njs_parser_logical_and_expression,
+ 1, {
+ { NJS_TOKEN_LOGICAL_OR, njs_vmcode_logical_or },
+ }
+};
+
+
+static const njs_parser_expression_t
+ njs_parser_comma_expression =
+{
+ njs_parser_assignment_expression,
+ NULL,
+ 1, {
+ { NJS_TOKEN_COMMA, NULL },
+ }
+};
+
+
+njs_token_t
+njs_parser_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ return njs_parser_binary_expression(vm, parser,
+ &njs_parser_comma_expression,
+ token);
+}
+
+
+nxt_inline nxt_bool_t
+njs_parser_expression_operator(njs_token_t token)
+{
+ return (token >= NJS_TOKEN_FIRST_OPERATOR
+ && token <= NJS_TOKEN_LAST_OPERATOR);
+}
+
+
+njs_token_t
+njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ size_t size;
+ njs_parser_node_t *node, *pending;
+ njs_vmcode_operation_t operation;
+
+ token = njs_parser_conditional_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ for ( ;; ) {
+ size = sizeof(njs_vmcode_3addr_t);
+
+ switch (token) {
+
+ case NJS_TOKEN_ASSIGNMENT:
+ nxt_thread_log_debug("JS: =");
+ operation = njs_vmcode_move;
+ size = sizeof(njs_vmcode_move_t);
+ break;
+
+ case NJS_TOKEN_LINE_END:
+ token = njs_lexer_token(parser->lexer);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (njs_parser_expression_operator(token)) {
+ continue;
+ }
+
+ /* Fall through. */
+
+ default:
+ return token;
+ }
+
+ node = parser->node;
+
+ if (node->lvalue == NJS_LVALUE_NONE) {
+ nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ pending = NULL;
+
+ if (node->token == NJS_TOKEN_NAME) {
+ node->state = NJS_VARIABLE_ASSIGNMENT;
+
+ if (node->u.variable->state == NJS_VARIABLE_PENDING) {
+ pending = node;
+ }
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->code_size += size;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_var_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+
+ if (pending != NULL
+ && pending->u.variable->state == NJS_VARIABLE_PENDING)
+ {
+ pending->u.variable->state = NJS_VARIABLE_SET;
+ parser->code_size -= sizeof(njs_vmcode_1addr_t);
+
+ if (!parser->branch) {
+ pending->state = NJS_VARIABLE_FIRST_ASSIGNMENT;
+ }
+ }
+ }
+}
+
+
+static njs_token_t
+njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser,
+ const njs_parser_expression_t *expr, njs_token_t token)
+{
+ size_t size;
+ njs_parser_node_t *node, *pending;
+ njs_vmcode_operation_t operation;
+
+ token = njs_parser_conditional_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ for ( ;; ) {
+ switch (token) {
+
+ case NJS_TOKEN_ASSIGNMENT:
+ nxt_thread_log_debug("JS: =");
+ operation = njs_vmcode_move;
+ break;
+
+ case NJS_TOKEN_ADDITION_ASSIGNMENT:
+ nxt_thread_log_debug("JS: +=");
+ operation = njs_vmcode_addition;
+ break;
+
+ case NJS_TOKEN_SUBSTRACTION_ASSIGNMENT:
+ nxt_thread_log_debug("JS: -=");
+ operation = njs_vmcode_substraction;
+ break;
+
+ case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT:
+ nxt_thread_log_debug("JS: *=");
+ operation = njs_vmcode_multiplication;
+ break;
+
+ case NJS_TOKEN_DIVISION_ASSIGNMENT:
+ nxt_thread_log_debug("JS: /=");
+ operation = njs_vmcode_division;
+ break;
+
+ case NJS_TOKEN_REMAINDER_ASSIGNMENT:
+ nxt_thread_log_debug("JS: %=");
+ operation = njs_vmcode_remainder;
+ break;
+
+ case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT:
+ nxt_thread_log_debug("JS: <<=");
+ operation = njs_vmcode_left_shift;
+ break;
+
+ case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT:
+ nxt_thread_log_debug("JS: >>=");
+ operation = njs_vmcode_right_shift;
+ break;
+
+ case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+ nxt_thread_log_debug("JS: >>=");
+ operation = njs_vmcode_unsigned_right_shift;
+ break;
+
+ case NJS_TOKEN_BITWISE_AND_ASSIGNMENT:
+ nxt_thread_log_debug("JS: &=");
+ operation = njs_vmcode_bitwise_and;
+ break;
+
+ case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT:
+ nxt_thread_log_debug("JS: ^=");
+ operation = njs_vmcode_bitwise_xor;
+ break;
+
+ case NJS_TOKEN_BITWISE_OR_ASSIGNMENT:
+ nxt_thread_log_debug("JS: |=");
+ operation = njs_vmcode_bitwise_or;
+ break;
+
+ case NJS_TOKEN_LINE_END:
+ token = njs_lexer_token(parser->lexer);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (njs_parser_expression_operator(token)) {
+ continue;
+ }
+
+ /* Fall through. */
+
+ default:
+ return token;
+ }
+
+ node = parser->node;
+
+ if (node->lvalue == NJS_LVALUE_NONE) {
+ nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ pending = NULL;
+
+ if (node->token == NJS_TOKEN_NAME) {
+
+ if (token == NJS_TOKEN_ASSIGNMENT) {
+ node->state = NJS_VARIABLE_ASSIGNMENT;
+
+ if (node->u.variable->state == NJS_VARIABLE_PENDING) {
+ pending = node;
+ }
+
+ } else if (node->u.variable->state == NJS_VARIABLE_PENDING) {
+ node->u.variable->state = NJS_VARIABLE_USED;
+ }
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_assignment_expression(vm, parser, NULL, token);
+
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+
+ if (node->left->token == NJS_TOKEN_NAME) {
+
+ if (node->token == NJS_TOKEN_ASSIGNMENT) {
+ size = sizeof(njs_vmcode_move_t);
+
+ } else {
+ if (njs_parser_has_side_effect(node->right)) {
+ size = sizeof(njs_vmcode_move_t)
+ + sizeof(njs_vmcode_3addr_t);
+ } else {
+ size = sizeof(njs_vmcode_3addr_t);
+ }
+ }
+
+ } else {
+ if (node->token == NJS_TOKEN_ASSIGNMENT) {
+ size = sizeof(njs_vmcode_prop_set_t);
+
+ if (njs_parser_has_side_effect(node->right)) {
+ size += 2 * sizeof(njs_vmcode_move_t);
+ }
+
+ } else {
+ size = sizeof(njs_vmcode_prop_get_t)
+ + sizeof(njs_vmcode_3addr_t)
+ + sizeof(njs_vmcode_prop_set_t);
+ }
+ }
+
+ parser->code_size += size;
+
+ if (pending != NULL
+ && pending->u.variable->state == NJS_VARIABLE_PENDING)
+ {
+ pending->u.variable->state = NJS_VARIABLE_SET;
+
+ if (!parser->branch) {
+ pending->state = NJS_VARIABLE_FIRST_ASSIGNMENT;
+ }
+ }
+ }
+}
+
+
+njs_token_t
+njs_parser_conditional_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token)
+{
+ njs_parser_node_t *node, *cond;
+
+ token = njs_parser_binary_expression(vm, parser,
+ &njs_parser_logical_or_expression,
+ token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ for ( ;; ) {
+ if (token != NJS_TOKEN_CONDITIONAL) {
+ return token;
+ }
+
+ parser->branch = 1;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ cond = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(cond == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ cond->token = NJS_TOKEN_CONDITIONAL;
+ cond->left = parser->node;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ cond->right = node;
+ node->token = NJS_TOKEN_ELSE;
+
+ token = njs_parser_assignment_expression(vm, parser, NULL, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_COLON)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ node->left = parser->node;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_assignment_expression(vm, parser, NULL, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+
+ parser->node = cond;
+ parser->code_size += sizeof(njs_vmcode_cond_jump_t)
+ + sizeof(njs_vmcode_move_t)
+ + sizeof(njs_vmcode_jump_t)
+ + sizeof(njs_vmcode_move_t);
+ }
+}
+
+
+static njs_token_t
+njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
+ const njs_parser_expression_t *expr, njs_token_t token)
+{
+ nxt_int_t n;
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+ const njs_parser_operation_t *op;
+
+ token = expr->next(vm, parser, expr->expression, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ for ( ;; ) {
+ n = expr->count;
+ op = expr->op;
+
+ do {
+ if (op->token == token) {
+ operation = op->operation;
+ goto found;
+ }
+
+ op++;
+ n--;
+
+ } while (n != 0);
+
+ if (token == NJS_TOKEN_LINE_END) {
+
+ token = njs_lexer_token(parser->lexer);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (njs_parser_expression_operator(token)) {
+ continue;
+ }
+ }
+
+ return token;
+
+ found:
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = expr->next(vm, parser, expr->expression, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+ }
+}
+
+
+static njs_token_t
+njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
+ const njs_parser_expression_t *expr, njs_token_t token)
+{
+ double num;
+ njs_token_t next;
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ switch (token) {
+
+ case NJS_TOKEN_ADDITION:
+ token = NJS_TOKEN_UNARY_PLUS;
+ operation = njs_vmcode_unary_plus;
+ break;
+
+ case NJS_TOKEN_SUBSTRACTION:
+ token = NJS_TOKEN_UNARY_NEGATION;
+ operation = njs_vmcode_unary_negation;
+ break;
+
+ case NJS_TOKEN_LOGICAL_NOT:
+ operation = njs_vmcode_logical_not;
+ break;
+
+ case NJS_TOKEN_BITWISE_NOT:
+ operation = njs_vmcode_bitwise_not;
+ break;
+
+ case NJS_TOKEN_TYPEOF:
+ operation = njs_vmcode_typeof;
+ break;
+
+ case NJS_TOKEN_VOID:
+ operation = njs_vmcode_void;
+ break;
+
+ case NJS_TOKEN_DELETE:
+ operation = njs_vmcode_delete;
+ break;
+
+ default:
+ return njs_parser_inc_dec_expression(vm, parser, token);
+ }
+
+ next = njs_parser_token(parser);
+ if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) {
+ return next;
+ }
+
+ next = njs_parser_unary_expression(vm, parser, NULL, next);
+ if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) {
+ return next;
+ }
+
+ if (token == NJS_TOKEN_UNARY_PLUS
+ && parser->node->token == NJS_TOKEN_NUMBER)
+ {
+ /* Skip the unary plus of number. */
+ return next;
+ }
+
+ if (token == NJS_TOKEN_UNARY_NEGATION
+ && parser->node->token == NJS_TOKEN_NUMBER)
+ {
+ /* Optimization of common negative number. */
+
+ node = parser->node;
+ num = -node->u.value.data.u.number;
+ node->u.value.data.u.number = num;
+ node->u.value.data.truth = njs_is_number_true(num);
+
+ return next;
+ }
+
+ if (token == NJS_TOKEN_TYPEOF
+ && parser->node->token == NJS_TOKEN_NAME)
+ {
+ parser->node->state = NJS_VARIABLE_TYPEOF;
+
+ } else if (token == NJS_TOKEN_DELETE
+ && parser->node->token == NJS_TOKEN_PROPERTY)
+ {
+ parser->node->token = NJS_TOKEN_PROPERTY_DELETE;
+ parser->node->u.operation = njs_vmcode_property_delete;
+ parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+ return next;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_2addr_t);
+
+ return next;
+}
+
+
+static njs_token_t
+njs_parser_inc_dec_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ njs_token_t next;
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ switch (token) {
+
+ case NJS_TOKEN_INCREMENT:
+ operation = njs_vmcode_increment;
+ break;
+
+ case NJS_TOKEN_DECREMENT:
+ operation = njs_vmcode_decrement;
+ break;
+
+ default:
+ return njs_parser_post_inc_dec_expression(vm, parser, token);
+ }
+
+ next = njs_parser_token(parser);
+ if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) {
+ return next;
+ }
+
+ next = njs_parser_call_expression(vm, parser, next);
+ if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) {
+ return next;
+ }
+
+ if (parser->node->lvalue == NJS_LVALUE_NONE) {
+ nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->node = node;
+
+ parser->code_size += (parser->node->token == NJS_TOKEN_NAME) ?
+ sizeof(njs_vmcode_3addr_t):
+ sizeof(njs_vmcode_prop_get_t)
+ + sizeof(njs_vmcode_3addr_t)
+ + sizeof(njs_vmcode_prop_set_t);
+
+ return next;
+}
+
+
+static njs_token_t
+njs_parser_post_inc_dec_expression(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token)
+{
+ njs_parser_node_t *node;
+ njs_vmcode_operation_t operation;
+
+ token = njs_parser_call_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ switch (token) {
+
+ case NJS_TOKEN_INCREMENT:
+ token = NJS_TOKEN_POST_INCREMENT;
+ operation = njs_vmcode_post_increment;
+ break;
+
+ case NJS_TOKEN_DECREMENT:
+ token = NJS_TOKEN_POST_DECREMENT;
+ operation = njs_vmcode_post_decrement;
+ break;
+
+ default:
+ return token;
+ }
+
+ if (parser->node->lvalue == NJS_LVALUE_NONE) {
+ nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = token;
+ node->u.operation = operation;
+ node->left = parser->node;
+ parser->node = node;
+
+ parser->code_size += (parser->node->token == NJS_TOKEN_NAME) ?
+ sizeof(njs_vmcode_3addr_t):
+ sizeof(njs_vmcode_prop_get_t)
+ + sizeof(njs_vmcode_3addr_t)
+ + sizeof(njs_vmcode_prop_set_t);
+
+ return njs_parser_token(parser);
+}
+
+
+static njs_token_t
+njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ nxt_bool_t ctor;
+ njs_parser_node_t *func, *node;
+
+ ctor = 0;
+
+ if (token == NJS_TOKEN_NEW) {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ ctor = 1;
+ }
+
+ token = njs_parser_terminal(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ for ( ;; ) {
+
+ token = njs_parser_property_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = parser->node;
+
+ if (token != NJS_TOKEN_OPEN_PARENTHESIS) {
+ /* TODO: var o = Object; var o = new Object() */
+ node->ctor = ctor;
+ return token;
+ }
+
+ switch (node->token) {
+
+ case NJS_TOKEN_NAME:
+ func = node;
+ func->token = NJS_TOKEN_FUNCTION_CALL;
+ parser->code_size += sizeof(njs_vmcode_function_t)
+ + sizeof(njs_vmcode_call_t);
+ break;
+
+ case NJS_TOKEN_FUNCTION_CREATE:
+ func = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(func == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ func->token = NJS_TOKEN_FUNCTION_CALL;
+ func->left = node;
+ func->index = node->index;
+ parser->code_size += sizeof(njs_vmcode_function_t)
+ + sizeof(njs_vmcode_call_t);
+ break;
+
+ case NJS_TOKEN_OBJECT_FUNCTION:
+ case NJS_TOKEN_ARRAY_FUNCTION:
+ case NJS_TOKEN_BOOLEAN_FUNCTION:
+ case NJS_TOKEN_NUMBER_FUNCTION:
+ case NJS_TOKEN_STRING_FUNCTION:
+ case NJS_TOKEN_FUNCTION_FUNCTION:
+ case NJS_TOKEN_REGEXP_FUNCTION:
+ case NJS_TOKEN_EVAL:
+ func = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(func == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ func->token = NJS_TOKEN_FUNCTION_CALL;
+ func->left = node;
+ parser->code_size += sizeof(njs_vmcode_method_t)
+ + sizeof(njs_vmcode_call_t);
+ break;
+
+ default:
+ func = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(func == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ func->token = NJS_TOKEN_METHOD_CALL;
+ func->left = node;
+ parser->code_size += sizeof(njs_vmcode_method_t)
+ + sizeof(njs_vmcode_call_t);
+ }
+
+ token = njs_parser_arguments(vm, parser, func);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ parser->node = func;
+
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+ }
+}
+
+
+static njs_token_t
+njs_parser_property_expression(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ njs_token_t next;
+ njs_parser_node_t *node;
+
+ for ( ;; ) {
+ if (token != NJS_TOKEN_DOT
+ && token != NJS_TOKEN_OPEN_BRACKET)
+ {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_PROPERTY;
+ node->lvalue = NJS_LVALUE_ENABLED;
+ node->u.operation = njs_vmcode_property_get;
+ node->left = parser->node;
+
+ next = njs_parser_token(parser);
+ if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) {
+ return next;
+ }
+
+ if (token == NJS_TOKEN_DOT) {
+
+ if (next != NJS_TOKEN_NAME) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ token = njs_parser_property_name(vm, parser, next);
+
+ } else {
+ token = njs_parser_property_brackets(vm, parser, next);
+ }
+
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node->right = parser->node;
+ parser->node = node;
+
+ parser->code_size += sizeof(njs_vmcode_prop_get_t);
+ }
+}
+
+
+njs_token_t
+njs_parser_property_name(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ nxt_int_t ret;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_STRING;
+
+ ret = njs_parser_string_create(vm, &node->u.value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ parser->node = node;
+
+ return njs_parser_token(parser);
+}
+
+
+static njs_token_t
+njs_parser_property_brackets(njs_vm_t *vm, njs_parser_t *parser,
+ njs_token_t token)
+{
+ token = njs_parser_expression(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (nxt_slow_path(token != NJS_TOKEN_CLOSE_BRACKET)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ return njs_parser_token(parser);
+}
+
+
+njs_token_t
+njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *parent)
+{
+ njs_token_t token;
+ njs_index_t index;
+ njs_parser_node_t *node;
+
+ parser->nesting_arguments++;
+ index = NJS_SCOPE_CALLEE_ARGUMENTS;
+
+ do {
+ token = njs_parser_token(parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token == NJS_TOKEN_CLOSE_PARENTHESIS) {
+ break;
+ }
+
+ token = njs_parser_assignment_expression(vm, parser, NULL, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_ARGUMENT;
+ node->index = index;
+ index += sizeof(njs_value_t);
+
+ node->left = parser->node;
+ parser->node->dest = node;
+ parent->right = node;
+ parent = node;
+
+ parser->code_size += sizeof(njs_vmcode_move_t);
+
+ } while (token == NJS_TOKEN_COMMA);
+
+ if (nxt_slow_path(token != NJS_TOKEN_CLOSE_PARENTHESIS)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ index = njs_index_size(index);
+ index += NJS_NATIVE_FRAME_SIZE + sizeof(njs_value_t);
+
+ parser->nesting_arguments_size += index;
+
+ parser->nesting_arguments--;
+
+ if (parser->nesting_arguments == 0) {
+
+ if (parser->method_arguments_size < parser->nesting_arguments_size) {
+ parser->method_arguments_size = parser->nesting_arguments_size;
+ }
+
+ parser->nesting_arguments_size = 0;
+ }
+
+ return token;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_utf8.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_malloc.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_regexp_pattern.h>
+#include <string.h>
+
+
+static int njs_regexp_pattern_compile(pcre **code, pcre_extra **extra,
+ u_char *source, int options);
+static njs_ret_t njs_regexp_exec_result(njs_vm_t *vm, njs_regexp_t *regexp,
+ u_char *string, int *captures, nxt_uint_t utf8);
+
+
+njs_regexp_t *
+njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern)
+{
+ njs_regexp_t *regexp;
+
+ regexp = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_regexp_t));
+
+ if (nxt_fast_path(regexp != NULL)) {
+ nxt_lvlhsh_init(®exp->object.hash);
+ nxt_lvlhsh_init(®exp->object.shared_hash);
+ regexp->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_REGEXP];
+ regexp->last_index = 0;
+ regexp->pattern = pattern;
+ }
+
+ return regexp;
+}
+
+
+njs_regexp_pattern_t *
+njs_regexp_pattern_create(njs_vm_t *vm, nxt_str_t *source,
+ njs_regexp_flags_t flags)
+{
+ int options, ret;
+ u_char *p;
+ njs_regexp_pattern_t *pattern;
+
+ /* TODO: pcre_malloc */
+
+ pattern = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_regexp_pattern_t) + source->len + 1);
+ if (nxt_slow_path(pattern == NULL)) {
+ return NULL;
+ }
+
+ p = (u_char *) pattern + sizeof(njs_regexp_pattern_t);
+ pattern->source = p;
+
+ p = memcpy(p, source->data, source->len);
+ p += source->len;
+ *p = '\0';
+
+ pattern->ncaptures = 0;
+
+ pattern->global = ((flags & NJS_REGEXP_GLOBAL) != 0);
+
+#ifdef PCRE_JAVASCRIPT_COMPAT
+ /* JavaScript compatibility has been introduced in PCRE-7.7. */
+ options = PCRE_JAVASCRIPT_COMPAT;
+#else
+ options = 0;
+#endif
+
+ if ((flags & NJS_REGEXP_IGNORE_CASE) != 0) {
+ pattern->ignore_case = 1;
+ options |= PCRE_CASELESS;
+ }
+
+ if ((flags & NJS_REGEXP_MULTILINE) != 0) {
+ pattern->multiline = 1;
+ options |= PCRE_MULTILINE;
+ }
+
+ ret = njs_regexp_pattern_compile(&pattern->code[0], &pattern->extra[0],
+ pattern->source, options);
+
+ if (nxt_slow_path(ret < 0)) {
+ return NULL;
+ }
+
+ pattern->ncaptures = ret;
+
+ ret = njs_regexp_pattern_compile(&pattern->code[1], &pattern->extra[1],
+ pattern->source, options | PCRE_UTF8);
+
+ if (nxt_slow_path(ret < 0)) {
+
+ if (ret == NXT_DECLINED) {
+ return pattern;
+ }
+
+ return NULL;
+ }
+
+ if (nxt_fast_path((unsigned) ret == pattern->ncaptures)) {
+ return pattern;
+ }
+
+ nxt_thread_log_error(NXT_LOG_ERR, "numbers of byte and UTF-8 captures "
+ "in RegExp \"%s\" vary: %d vs %d",
+ pattern->source, pattern->ncaptures, ret);
+
+ return NULL;
+}
+
+
+static int
+njs_regexp_pattern_compile(pcre **code, pcre_extra **extra, u_char *source,
+ int options)
+{
+ int ret, erroff, captures;
+ u_char *error;
+ const char *errstr;
+
+ *code = pcre_compile((char *) source, options, &errstr, &erroff, NULL);
+
+ if (nxt_slow_path(*code == NULL)) {
+
+ if ((options & PCRE_UTF8) != 0) {
+ return NXT_DECLINED;
+ }
+
+ error = source + erroff;
+
+ if (*error != '\0') {
+ nxt_thread_log_error(NXT_LOG_ERR,
+ "pcre_compile(\"%s\") failed: %s at \"%s\"",
+ source, errstr, error);
+ } else {
+ nxt_thread_log_error(NXT_LOG_ERR,
+ "pcre_compile(\"%s\") failed: %s",
+ source, errstr);
+ }
+
+ return NXT_ERROR;
+ }
+
+ *extra = pcre_study(*code, 0, &errstr);
+
+ if (nxt_slow_path(errstr != NULL)) {
+ nxt_thread_log_error(NXT_LOG_ERR, "pcre_study(\"%s\") failed: %s",
+ source, errstr);
+ return NXT_ERROR;
+ }
+
+ ret = pcre_fullinfo(*code, NULL, PCRE_INFO_CAPTURECOUNT, &captures);
+
+ if (nxt_fast_path(ret >= 0)) {
+ /* Reserve additional elements for the first "$0" capture. */
+ return captures + 1;
+ }
+
+ nxt_thread_log_error(NXT_LOG_ERR,
+ "pcre_fullinfo(\"%s\", PCRE_INFO_CAPTURECOUNT) failed: %d",
+ source, ret);
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_regexp_function(njs_vm_t *vm, njs_param_t *param)
+{
+ return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_regexp_prototype_last_index(njs_vm_t *vm, njs_value_t *value)
+{
+ uint32_t index;
+ njs_regexp_t *regexp;
+ njs_string_prop_t string;
+
+ njs_release(vm, value);
+
+ regexp = value->data.u.regexp;
+
+ (void) njs_string_prop(&string, ®exp->string);
+
+ index = njs_string_index(&string, regexp->last_index);
+ njs_number_set(&vm->retval, index);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_regexp_prototype_ignore_case(njs_vm_t *vm, njs_value_t *regexp)
+{
+ njs_regexp_pattern_t *pattern;
+
+ pattern = regexp->data.u.regexp->pattern;
+ vm->retval = pattern->ignore_case ? njs_value_true : njs_value_false;
+ njs_release(vm, regexp);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_regexp_prototype_global(njs_vm_t *vm, njs_value_t *regexp)
+{
+ njs_regexp_pattern_t *pattern;
+
+ pattern = regexp->data.u.regexp->pattern;
+ vm->retval = pattern->global ? njs_value_true : njs_value_false;
+ njs_release(vm, regexp);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_regexp_prototype_multiline(njs_vm_t *vm, njs_value_t *regexp)
+{
+ njs_regexp_pattern_t *pattern;
+
+ pattern = regexp->data.u.regexp->pattern;
+ vm->retval = pattern->multiline ? njs_value_true : njs_value_false;
+ njs_release(vm, regexp);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *regexp)
+{
+ size_t length;
+ u_char *source;
+ njs_regexp_pattern_t *pattern;
+
+ pattern = regexp->data.u.regexp->pattern;
+
+ /*
+ * The pattern source is stored not as value but as C string even
+ * without length, because retrieving it is very seldom operation.
+ */
+ source = pattern->source;
+
+ /* TODO: can regexp string be UTF-8? */
+ length = strlen((char *) source);
+
+ return njs_string_create(vm, &vm->retval, source, length, length);
+}
+
+
+static njs_ret_t
+njs_regexp_prototype_test(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_uint_t n;
+ njs_ret_t ret;
+ njs_value_t val;
+ const njs_value_t *retval;
+ njs_string_prop_t string;
+ njs_regexp_pattern_t *pattern;
+
+ if (param->nargs != 0) {
+ ret = njs_value_to_string(vm, &val, ¶m->args[0]);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ retval = &njs_value_false;
+
+ (void) njs_string_prop(&string, &val);
+
+ n = (string.length != 0 && string.length != string.size);
+ pattern = param->object->data.u.regexp->pattern;
+
+ if (pattern->code[n] != NULL) {
+ ret = pcre_exec(pattern->code[n], pattern->extra[n],
+ (char *) string.start, string.size,
+ 0, 0, NULL, 0);
+
+ if (ret >= 0) {
+ retval = &njs_value_true;
+
+ } else if (ret != PCRE_ERROR_NOMATCH) {
+ /* TODO: exception */
+ return NXT_ERROR;
+ }
+ }
+
+ vm->retval = *retval;
+
+ return NXT_OK;
+ }
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_regexp_prototype_exec(njs_vm_t *vm, njs_param_t *param)
+{
+ int *captures, ncaptures;
+ nxt_uint_t n, utf8;
+ njs_ret_t ret;
+ njs_regexp_t *regexp;
+ njs_string_prop_t string;
+ njs_regexp_pattern_t *pattern;
+
+ if (param->nargs != 0) {
+ regexp = param->object->data.u.regexp;
+
+ ret = njs_value_to_string(vm, ®exp->string, ¶m->args[0]);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ (void) njs_string_prop(&string, ®exp->string);
+
+ utf8 = 0;
+ n = 0;
+
+ if (string.length != 0) {
+ utf8 = 1;
+ n = 1;
+
+ if (string.length != string.size) {
+ utf8 = 2;
+ }
+ }
+
+ pattern = regexp->pattern;
+
+ if (pattern->code[n] != NULL) {
+ string.start += regexp->last_index;
+ string.size -= regexp->last_index;
+
+ /* Each capture is stored in 3 vector elements. */
+ ncaptures = pattern->ncaptures * 3;
+
+ captures = alloca(ncaptures * sizeof(int));
+
+ ret = pcre_exec(pattern->code[n], pattern->extra[n],
+ (char *) string.start, string.size,
+ 0, 0, captures, ncaptures);
+
+ if (ret >= 0) {
+ return njs_regexp_exec_result(vm, regexp, string.start,
+ captures, utf8);
+ }
+
+ if (nxt_slow_path(ret != PCRE_ERROR_NOMATCH)) {
+ /* TODO: exception */
+ return NXT_ERROR;
+ }
+ }
+
+ regexp->last_index = 0;
+ vm->retval = njs_value_null;
+
+ return NXT_OK;
+ }
+ }
+
+ return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_regexp_exec_result(njs_vm_t *vm, njs_regexp_t *regexp, u_char *string,
+ int *captures, nxt_uint_t utf8)
+{
+ u_char *start;
+ int32_t size, length;
+ nxt_uint_t i, n;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ static const njs_value_t njs_string_index = njs_string("index");
+ static const njs_value_t njs_string_input = njs_string("input");
+
+ array = njs_array_alloc(vm, regexp->pattern->ncaptures, 0);
+ if (nxt_slow_path(array == NULL)) {
+ return NXT_ERROR;
+ }
+
+ for (i = 0; i < regexp->pattern->ncaptures; i++) {
+ n = 2 * i;
+
+ if (captures[n] != -1) {
+ start = &string[captures[n]];
+ size = captures[n + 1] - captures[n];
+
+ switch (utf8) {
+ case 0:
+ length = 0;
+ break;
+ case 1:
+ length = size;
+ break;
+ default:
+ length = nxt_utf8_length(start, size);
+ break;
+ }
+
+ ret = njs_string_create(vm, &array->start[i], start, size, length);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ } else {
+ array->start[i] = njs_value_void;
+ }
+ }
+
+ prop = njs_object_prop_alloc(vm, &njs_string_index);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ /* TODO: Non UTF-8 position */
+
+ njs_number_set(&prop->value, regexp->last_index + captures[0]);
+
+ if (regexp->pattern->global) {
+ regexp->last_index += captures[1];
+ }
+
+ lhq.key_hash = NJS_INDEX_HASH;
+ lhq.key.len = sizeof("index") - 1;
+ lhq.key.data = (u_char *) "index";
+ lhq.replace = 0;
+ lhq.value = prop;
+ lhq.pool = vm->mem_cache_pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&array->object.hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ /* Only NXT_ERROR can be returned here. */
+ return ret;
+ }
+
+ prop = njs_object_prop_alloc(vm, &njs_string_input);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ njs_string_copy(&prop->value, ®exp->string);
+
+ lhq.key_hash = NJS_INPUT_HASH;
+ lhq.key.len = sizeof("input") - 1;
+ lhq.key.data = (u_char *) "input";
+ lhq.value = prop;
+
+ ret = nxt_lvlhsh_insert(&array->object.hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ /* Only NXT_ERROR can be returned here. */
+ return ret;
+ }
+
+ vm->retval.data.u.array = array;
+ vm->retval.type = NJS_ARRAY;
+ vm->retval.data.truth = 1;
+
+ return NXT_OK;
+}
+
+
+static const njs_object_prop_t njs_regexp_function_properties[] =
+{
+ { njs_string("RegExp"),
+ njs_string("name"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_value(NJS_NUMBER, 1, 2.0),
+ njs_string("length"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_prototype),
+ njs_string("prototype"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_regexp_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_regexp_function_properties,
+ nxt_nitems(njs_regexp_function_properties));
+}
+
+
+static const njs_object_prop_t njs_regexp_prototype_properties[] =
+{
+ { njs_getter(njs_regexp_prototype_last_index),
+ njs_string("lastIndex"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_regexp_prototype_ignore_case),
+ njs_string("ignoreCase"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_regexp_prototype_global),
+ njs_string("global"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_regexp_prototype_multiline),
+ njs_string("multiline"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_regexp_prototype_source),
+ njs_string("source"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_native_function(njs_regexp_prototype_test, 0),
+ njs_string("test"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_regexp_prototype_exec, 0),
+ njs_string("exec"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_regexp_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_regexp_prototype_properties,
+ nxt_nitems(njs_regexp_prototype_properties));
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_REGEXP_H_INCLUDED_
+#define _NJS_REGEXP_H_INCLUDED_
+
+
+typedef enum {
+ NJS_REGEXP_IGNORE_CASE = 1,
+ NJS_REGEXP_GLOBAL = 2,
+ NJS_REGEXP_MULTILINE = 4,
+} njs_regexp_flags_t;
+
+
+struct njs_regexp_s {
+ /* Must be aligned to njs_value_t. */
+ njs_object_t object;
+
+ uint32_t last_index;
+
+ njs_regexp_pattern_t *pattern;
+
+ /*
+ * This string value can be not aligned since
+ * it never used in nJSVM operations.
+ */
+ njs_value_t string;
+};
+
+
+njs_regexp_t *njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern);
+njs_regexp_pattern_t *njs_regexp_pattern_create(njs_vm_t *vm,
+ nxt_str_t *source, njs_regexp_flags_t flags);
+njs_ret_t njs_regexp_function(njs_vm_t *vm, njs_param_t *param);
+njs_ret_t njs_regexp_prototype_exec(njs_vm_t *vm, njs_param_t *param);
+nxt_int_t njs_regexp_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+nxt_int_t njs_regexp_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+#endif /* _NJS_REGEXP_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_REGEXP_PATTERN_H_INCLUDED_
+#define _NJS_REGEXP_PATTERN_H_INCLUDED_
+
+#include <pcre.h>
+
+
+struct njs_regexp_pattern_s {
+ pcre *code[2];
+ pcre_extra *extra[2];
+ u_char *source;
+
+#if (NXT_64BIT)
+ uint32_t ncaptures;
+ uint8_t global; /* 1 bit */
+ uint8_t ignore_case; /* 1 bit */
+ uint8_t multiline; /* 1 bit */
+#else
+ uint16_t ncaptures;
+ uint8_t global; /* 1 bit */
+ uint8_t ignore_case:1;
+ uint8_t multiline:1;
+#endif
+};
+
+
+#endif /* _NJS_REGEXP_PATTERN_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <string.h>
+
+
+typedef nxt_int_t (*njs_shared_hash_t) (njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+
+/* STUB */
+static nxt_int_t
+njs_stub_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return NXT_OK;
+}
+static njs_ret_t
+njs_stub_function(njs_vm_t *vm, njs_param_t *param)
+{
+ return NXT_ERROR;
+}
+/**/
+
+
+nxt_int_t
+njs_shared_objects_create(njs_vm_t *vm)
+{
+ size_t size;
+ nxt_int_t ret;
+ nxt_uint_t i;
+ njs_object_t *prototypes;
+ njs_function_t *functions;
+
+ static const njs_shared_hash_t prototype_hash[] = {
+ njs_object_prototype_hash,
+ njs_array_prototype_hash,
+ njs_stub_hash,
+ njs_number_prototype_hash,
+ njs_string_prototype_hash,
+ njs_function_prototype_hash,
+ njs_regexp_prototype_hash,
+ };
+
+ static const njs_shared_hash_t function_hash[] = {
+ njs_object_function_hash,
+ njs_array_function_hash,
+ njs_stub_hash,
+ njs_number_function_hash,
+ njs_string_function_hash,
+ njs_function_function_hash,
+ njs_regexp_function_hash,
+ njs_stub_hash,
+ };
+
+ static const njs_native_t native_functions[] = {
+ njs_object_function,
+ njs_array_function,
+ njs_stub_function,
+ njs_number_function,
+ njs_string_ctor_function,
+ njs_stub_function,
+ njs_stub_function,
+ njs_stub_function,
+ };
+
+ size = NJS_PROTOTYPE_MAX * sizeof(njs_object_t);
+
+ prototypes = nxt_mem_cache_zalign(vm->mem_cache_pool, sizeof(njs_value_t),
+ size);
+ if (nxt_slow_path(prototypes == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->shared->prototypes = prototypes;
+
+ for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) {
+ /* TODO: shared hash: prototype & constructor getters, methods */
+
+ ret = prototype_hash[i](vm, &prototypes[i].shared_hash);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ size = NJS_FUNCTION_MAX * sizeof(njs_function_t);
+
+ functions = nxt_mem_cache_zalign(vm->mem_cache_pool, sizeof(njs_value_t),
+ size);
+ if (nxt_slow_path(functions == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->shared->functions = functions;
+
+ for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) {
+ functions[i].native = 1;
+ functions[i].args_offset = 1;
+ functions[i].code.native = native_functions[i];
+
+ ret = function_hash[i](vm, &functions[i].object.shared_hash);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+ }
+
+ /* TODO: create function shared hash: prototype+contructor getter */
+
+ return NXT_OK;
+}
+
+
+/*
+ * Object(),
+ * Object.__proto__ -> Function_Prototype,
+ * Object_Prototype.__proto__ -> null,
+ * the null value is handled by njs_object_prototype_get_prototype(),
+ *
+ * Array(),
+ * Array.__proto__ -> Function_Prototype,
+ * Array_Prototype.__proto__ -> Object_Prototype,
+ *
+ * Function(),
+ * Function.__proto__ -> Function_Prototype,
+ * Function_Prototype.__proto__ -> Object_Prototype,
+ *
+ * [...]
+ *
+ * eval().
+ */
+
+nxt_int_t
+njs_shared_objects_clone(njs_vm_t *vm)
+{
+ size_t size;
+ nxt_uint_t i;
+ njs_value_t *values;
+ njs_object_t *prototypes;
+ njs_function_t *functions;
+
+ size = NJS_PROTOTYPE_MAX * sizeof(njs_object_t);
+
+ prototypes = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ size);
+ if (nxt_slow_path(prototypes == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->prototypes = prototypes;
+
+ memcpy(prototypes, vm->shared->prototypes, size);
+
+ for (i = NJS_PROTOTYPE_ARRAY; i < NJS_PROTOTYPE_MAX; i++) {
+ prototypes[i].__proto__ = &prototypes[NJS_PROTOTYPE_OBJECT];
+ }
+
+ size = NJS_FUNCTION_MAX * sizeof(njs_function_t);
+
+ functions = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ size);
+ if (nxt_slow_path(functions == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->functions = functions;
+
+ memcpy(functions, vm->shared->functions, size);
+
+ values = vm->scopes[NJS_SCOPE_GLOBAL];
+
+ for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) {
+ values[i].type = NJS_FUNCTION;
+ values[i].data.truth = 1;
+ values[i].data.u.function = &functions[i];
+ functions[i].object.__proto__ = &prototypes[NJS_FUNCTION_FUNCTION];
+ }
+
+ return NXT_OK;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_utf8.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_malloc.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_regexp_pattern.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+static nxt_noinline njs_ret_t njs_string_index_of(njs_vm_t *vm, njs_value_t *src,
+ njs_value_t *search_string, size_t index);
+
+
+njs_ret_t
+njs_string_create(njs_vm_t *vm, njs_value_t *value, u_char *start, size_t size,
+ size_t length)
+{
+ u_char *dst, *src;
+ njs_string_t *string;
+
+ value->type = NJS_STRING;
+ njs_string_truth(value, size);
+
+ if (size <= NJS_STRING_SHORT) {
+ value->short_string.size = size;
+ value->short_string.length = length;
+
+ dst = value->short_string.start;
+ src = start;
+
+ while (size != 0) {
+ /* The maximum size is just 14 bytes. */
+ nxt_pragma_loop_disable_vectorization;
+
+ *dst++ = *src++;
+ size--;
+ }
+
+ } else {
+ /*
+ * Setting UTF-8 length is not required here, it just allows
+ * to store the constant in whole byte instead of bit twiddling.
+ */
+ value->short_string.size = NJS_STRING_LONG;
+ value->short_string.length = 0;
+ value->data.external0 = 0xff;
+ value->data.string_size = size;
+
+ string = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_string_t));
+ if (nxt_slow_path(string == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value->data.u.string = string;
+
+ string->start = start;
+ string->length = length;
+ string->retain = 1;
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_noinline u_char *
+njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size,
+ uint32_t length)
+{
+ uint32_t total;
+ njs_string_t *string;
+
+ value->type = NJS_STRING;
+ njs_string_truth(value, size);
+
+ if (size <= NJS_STRING_SHORT) {
+ value->short_string.size = size;
+ value->short_string.length = length;
+
+ return value->short_string.start;
+ }
+
+ /*
+ * Setting UTF-8 length is not required here, it just allows
+ * to store the constant in whole byte instead of bit twiddling.
+ */
+ value->short_string.size = NJS_STRING_LONG;
+ value->short_string.length = 0;
+ value->data.external0 = 0;
+ value->data.string_size = size;
+
+ if (size != length && length > NJS_STRING_MAP_OFFSET) {
+ total = nxt_align_size(size, sizeof(uint32_t));
+ total += ((length - 1) / NJS_STRING_MAP_OFFSET) * sizeof(uint32_t);
+
+ } else {
+ total = size;
+ }
+
+ string = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_string_t) + total);
+
+ if (nxt_fast_path(string != NULL)) {
+ value->data.u.string = string;
+
+ string->start = (u_char *) string + sizeof(njs_string_t);
+ string->length = length;
+ string->retain = 1;
+
+ return string->start;
+ }
+
+ return NULL;
+}
+
+
+void
+njs_string_copy(njs_value_t *dst, njs_value_t *src)
+{
+ *dst = *src;
+
+ /* GC: long string retain */
+}
+
+
+/*
+ * njs_string_validate() validates an UTF-8 string, evaluates its length,
+ * sets njs_string_prop_t struct, and initializes offset map if it is required.
+ */
+
+nxt_noinline njs_ret_t
+njs_string_validate(njs_vm_t *vm, njs_string_prop_t *string, njs_value_t *value)
+{
+ u_char *start;
+ size_t new_size;
+ ssize_t size;
+ njs_ret_t length;
+
+ size = value->short_string.size;
+
+ if (size != NJS_STRING_LONG) {
+ string->start = value->short_string.start;
+ length = value->short_string.length;
+
+ if (length == 0 && length != size) {
+ length = nxt_utf8_length(value->short_string.start, size);
+
+ if (nxt_slow_path(length < 0)) {
+ /* Invalid UTF-8 string. */
+ return length;
+ }
+
+ value->short_string.length = length;
+ }
+
+ } else {
+ string->start = value->data.u.string->start;
+ size = value->data.string_size;
+ length = value->data.u.string->length;
+
+ if (length == 0 && length != size) {
+ length = nxt_utf8_length(string->start, size);
+
+ if (length != size) {
+ if (nxt_slow_path(length < 0)) {
+ /* Invalid UTF-8 string. */
+ return length;
+ }
+
+ if (length > NJS_STRING_MAP_OFFSET) {
+ /*
+ * Reallocate the long string with offset map
+ * after the string.
+ */
+ new_size = nxt_align_size(size, sizeof(uint32_t));
+ new_size += ((length - 1) / NJS_STRING_MAP_OFFSET)
+ * sizeof(uint32_t);
+
+ start = nxt_mem_cache_alloc(vm->mem_cache_pool, new_size);
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ memcpy(start, string->start, size);
+ string->start = start;
+ value->data.u.string->start = start;
+
+ njs_string_offset_map_init(start, size);
+ }
+ }
+
+ value->data.u.string->length = length;
+ }
+ }
+
+ string->size = size;
+ string->length = length;
+
+ return length;
+}
+
+
+nxt_noinline size_t
+njs_string_prop(njs_string_prop_t *string, njs_value_t *value)
+{
+ size_t size;
+ uintptr_t length;
+
+ size = value->short_string.size;
+
+ if (size != NJS_STRING_LONG) {
+ string->start = value->short_string.start;
+ length = value->short_string.length;
+
+ } else {
+ string->start = value->data.u.string->start;
+ size = value->data.string_size;
+ length = value->data.u.string->length;
+ }
+
+ string->size = size;
+ string->length = length;
+
+ return (length == 0) ? size : length;
+}
+
+
+njs_ret_t
+njs_string_ctor_function(njs_vm_t *vm, njs_param_t *param)
+{
+ njs_object_t *object;
+ const njs_value_t *value;
+
+ if (param->nargs == 0) {
+ value = &njs_string_empty;
+
+ } else {
+ /* TODO: to_string. */
+ value = ¶m->args[0];
+ }
+
+ if (vm->frame->ctor) {
+ /* value->type is the same as prototype offset. */
+ object = njs_object_value_alloc(vm, value, value->type);
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.object = object;
+ vm->retval.type = NJS_OBJECT_STRING;
+ vm->retval.data.truth = 1;
+
+ } else {
+ vm->retval = *value;
+ }
+
+ return NXT_OK;
+}
+
+
+static const njs_object_prop_t njs_string_function_properties[] =
+{
+ { njs_string("String"),
+ njs_string("name"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_value(NJS_NUMBER, 1, 1.0),
+ njs_string("length"),
+ NJS_PROPERTY, 0, 0, 0, },
+
+ { njs_getter(njs_object_prototype_create_prototype),
+ njs_string("prototype"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_string_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_string_function_properties,
+ nxt_nitems(njs_string_function_properties));
+}
+
+
+static njs_ret_t
+njs_string_prototype_get_prototype(njs_vm_t *vm, njs_value_t *value)
+{
+ vm->retval.type = NJS_OBJECT;
+ vm->retval.data.truth = 1;
+ vm->retval.data.u.object = &vm->prototypes[NJS_PROTOTYPE_STRING];
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_length(njs_vm_t *vm, njs_value_t *value)
+{
+ size_t size;
+ uintptr_t length;
+
+ length = 0;
+
+ /* TODO: String object. */
+
+ if (njs_is_string(value)) {
+ size = value->short_string.size;
+ length = value->short_string.length;
+
+ if (size == NJS_STRING_LONG) {
+ size = value->data.string_size;
+ length = value->data.u.string->length;
+ }
+
+ length = (length == 0) ? size : length;
+ }
+
+ njs_number_set(&vm->retval, length);
+
+ njs_release(vm, value);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_bytes(njs_vm_t *vm, njs_value_t *value)
+{
+ u_char *p;
+ uintptr_t size;
+ const u_char *s, *end;
+ njs_string_prop_t string;
+
+ size = njs_string_prop(&string, value);
+
+ p = njs_string_alloc(vm, &vm->retval, size, 0);
+
+ if (nxt_fast_path(p != NULL)) {
+
+ if (string.length == 0) {
+ memcpy(p, string.start, size);
+
+ } else {
+ s = string.start;
+ end = s + string.size;
+
+ while (s < end) {
+ *p++ = (u_char) nxt_utf8_decode(&s, end);
+ }
+ }
+
+ njs_release(vm, value);
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_string_prototype_utf8(njs_vm_t *vm, njs_value_t *value)
+{
+ u_char *p;
+ ssize_t length;
+ njs_string_prop_t string;
+
+ (void) njs_string_prop(&string, value);
+
+ length = nxt_utf8_length(string.start, string.size);
+
+ if (length < 0) {
+ vm->retval = njs_value_null;
+ njs_release(vm, value);
+ return NXT_OK;
+ }
+
+ if ((size_t) length == string.size) {
+ return njs_string_create(vm, &vm->retval, string.start,
+ length, length);
+ }
+
+ /* length != string.size */
+
+ p = njs_string_alloc(vm, &vm->retval, string.size, length);
+
+ if (nxt_fast_path(p != NULL)) {
+ memcpy(p, string.start, string.size);
+
+ if (length >= NJS_STRING_MAP_OFFSET) {
+ njs_string_offset_map_init(p, string.size);
+ }
+
+ njs_release(vm, value);
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+nxt_noinline void
+njs_string_offset_map_init(const u_char *start, size_t size)
+{
+ size_t offset;
+ uint32_t *map;
+ nxt_uint_t n;
+ const u_char *p, *end;
+
+ end = start + size;
+ map = (uint32_t *) nxt_align_ptr(end, sizeof(uint32_t));
+ p = start;
+ n = 0;
+ offset = NJS_STRING_MAP_OFFSET;
+
+ do {
+ if (offset == 0) {
+ map[n++] = p - start;
+ offset = NJS_STRING_MAP_OFFSET;
+ }
+
+ /* The UTF-8 string should be valid since its length is known. */
+ p = nxt_utf8_next(p, end);
+
+ offset--;
+
+ } while (p < end);
+}
+
+
+nxt_bool_t
+njs_string_eq(const njs_value_t *v1, const njs_value_t *v2)
+{
+ size_t size;
+ const u_char *start1, *start2;
+
+ size = v1->short_string.size;
+
+ if (size != v2->short_string.size) {
+ return 0;
+ }
+
+ if (size != NJS_STRING_LONG) {
+ start1 = v1->short_string.start;
+ start2 = v2->short_string.start;
+
+ } else {
+ size = v1->data.string_size;
+
+ if (size != v2->data.string_size) {
+ return 0;
+ }
+
+ start1 = v1->data.u.string->start;
+ start2 = v2->data.u.string->start;
+ }
+
+ return (memcmp(start1, start2, size) == 0);
+}
+
+
+nxt_int_t
+njs_string_cmp(const njs_value_t *v1, const njs_value_t *v2)
+{
+ nxt_int_t ret;
+ size_t size, size1, size2;
+ const u_char *start1, *start2;
+
+ size1 = v1->short_string.size;
+
+ if (size1 != NJS_STRING_LONG) {
+ start1 = v1->short_string.start;
+
+ } else {
+ size1 = v1->data.string_size;
+ start1 = v1->data.u.string->start;
+ }
+
+ size2 = v2->short_string.size;
+
+ if (size2 != NJS_STRING_LONG) {
+ start2 = v2->short_string.start;
+
+ } else {
+ size2 = v2->data.string_size;
+ start2 = v2->data.u.string->start;
+ }
+
+ size = nxt_min(size1, size2);
+
+ ret = memcmp(start1, start2, size);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ return (size1 - size2);
+}
+
+
+njs_ret_t
+njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param)
+{
+ u_char *p, *start;
+ size_t size, length, mask;
+ uintptr_t nargs;
+ nxt_uint_t i;
+ njs_ret_t ret;
+ njs_value_t *object, *args, *values;
+ njs_string_prop_t string;
+
+ object = param->object;
+ nargs = param->nargs;
+
+ if (nargs == 0) {
+ njs_string_copy(&vm->retval, object);
+ return NXT_OK;
+ }
+
+ values = alloca((nargs + 1) * sizeof(njs_value_t));
+
+ ret = njs_value_to_string(vm, &values[0], object);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ (void) njs_string_prop(&string, &values[0]);
+
+ size = string.size;
+ length = string.length;
+ mask = (length != 0) ? -1 : 0;
+
+ args = param->args;
+
+ for (i = 0; i < nargs; i++) {
+ ret = njs_value_to_string(vm, &values[i + 1], &args[i]);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ (void) njs_string_prop(&string, &values[i + 1]);
+
+ size += string.size;
+ length += string.length;
+
+ if (string.length == 0 && string.size != 0) {
+ mask = 0;
+ }
+ }
+
+ length &= mask;
+
+ start = njs_string_alloc(vm, &vm->retval, size, length);
+
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = start;
+
+ for (i = 0; i <= nargs; i++) {
+ (void) njs_string_prop(&string, &values[i]);
+
+ p = memcpy(p, string.start, string.size);
+ p += string.size;
+ }
+
+ if (length >= NJS_STRING_MAP_OFFSET && size != length) {
+ njs_string_offset_map_init(start, size);
+ }
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_slice(njs_vm_t *vm, njs_param_t *param)
+{
+ ssize_t start, end;
+ uintptr_t nargs;
+ njs_ret_t length, string_length;
+ njs_value_t *args;
+ njs_string_prop_t string;
+
+ string_length = njs_string_prop(&string, param->object);
+
+ length = string_length;
+ start = 0;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ args = param->args;
+
+ start = njs_value_to_number(&args[0]);
+
+ if (start < 0) {
+ start += length;
+
+ if (start < 0) {
+ start = 0;
+ }
+ }
+
+ if (nargs > 1) {
+ end = njs_value_to_number(&args[1]);
+
+ if (end < 0) {
+ end += length;
+ }
+
+ length = end - start;
+
+ if (length < 0) {
+ start = 0;
+ length = 0;
+ }
+ }
+ }
+
+ return njs_string_slice(vm, &vm->retval, &string, string_length,
+ start, length);
+}
+
+
+static njs_ret_t
+njs_string_prototype_substring(njs_vm_t *vm, njs_param_t *param)
+{
+ ssize_t start, end;
+ uintptr_t nargs;
+ njs_ret_t length, string_length;
+ njs_value_t *args;
+ njs_string_prop_t string;
+
+ string_length = njs_string_prop(&string, param->object);
+
+ length = string_length;
+ start = 0;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ args = param->args;
+
+ start = njs_value_to_number(&args[0]);
+
+ if (start < 0) {
+ start = 0;
+ }
+
+ if (nargs > 1) {
+ end = njs_value_to_number(&args[1]);
+
+ if (end < 0) {
+ end = 0;
+ }
+
+ length = end - start;
+
+ if (length < 0) {
+ length = -length;
+ start = end;
+ }
+ }
+ }
+
+ return njs_string_slice(vm, &vm->retval, &string, string_length,
+ start, length);
+}
+
+
+static njs_ret_t
+njs_string_prototype_substr(njs_vm_t *vm, njs_param_t *param)
+{
+ ssize_t start;
+ uintptr_t nargs;
+ njs_ret_t length, string_length;
+ njs_value_t *args;
+ njs_string_prop_t string;
+
+ string_length = njs_string_prop(&string, param->object);
+
+ length = string_length;
+ start = 0;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ args = param->args;
+
+ start = njs_value_to_number(&args[0]);
+
+ if (start < 0) {
+
+ start += length;
+ if (start < 0) {
+ start = 0;
+ }
+ }
+
+ if (nargs > 1) {
+ length = njs_value_to_number(&args[1]);
+ }
+ }
+
+ return njs_string_slice(vm, &vm->retval, &string, string_length,
+ start, length);
+}
+
+
+static njs_ret_t
+njs_string_prototype_char_at(njs_vm_t *vm, njs_param_t *param)
+{
+ ssize_t start;
+ njs_ret_t length, string_length;
+ njs_string_prop_t string;
+
+ string_length = njs_string_prop(&string, param->object);
+
+ start = 0;
+ length = 1;
+
+ if (param->nargs != 0) {
+ start = njs_value_to_number(¶m->args[0]);
+
+ if (start < 0) {
+ length = 0;
+ }
+ }
+
+ return njs_string_slice(vm, &vm->retval, &string, string_length,
+ start, length);
+}
+
+
+nxt_noinline njs_ret_t
+njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
+ const njs_string_prop_t *string, size_t string_length, size_t index,
+ size_t length)
+{
+ u_char *slice;
+ size_t size, n;
+ ssize_t excess;
+ const u_char *p, *start, *end;
+
+ if (length > 0 && index < string_length) {
+
+ start = string->start;
+ end = start + string->size;
+
+ if (string->size == string_length) {
+ /* Byte or ASCII string. */
+ start += index;
+
+ excess = (start + length) - end;
+ if (excess > 0) {
+ length -= excess;
+ }
+
+ size = length;
+
+ if (string->length == 0) {
+ length = 0;
+ }
+
+ } else {
+ /* UTF-8 string. */
+ start = njs_string_offset(start, end, index);
+
+ /* Evaluate size of the slice in bytes and ajdust length. */
+ p = start;
+ n = length;
+
+ do {
+ p = nxt_utf8_next(p, end);
+ n--;
+ } while (n != 0 && p < end);
+
+ size = p - start;
+ length -= n;
+ }
+
+ if (nxt_fast_path(size != 0)) {
+ slice = njs_string_alloc(vm, &vm->retval, size, length);
+
+ if (nxt_slow_path(slice == NULL)) {
+ return NXT_ERROR;
+ }
+
+ memcpy(slice, start, size);
+
+ if (length >= NJS_STRING_MAP_OFFSET && size != length) {
+ njs_string_offset_map_init(slice, size);
+ }
+
+ return NXT_OK;
+ }
+ }
+
+ vm->retval = njs_string_empty;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_char_code_at(njs_vm_t *vm, njs_param_t *param)
+{
+ double num;
+ ssize_t index;
+ uint32_t code;
+ njs_ret_t length;
+ const u_char *start, *end;
+ njs_string_prop_t string;
+
+ length = njs_string_prop(&string, param->object);
+
+ index = 0;
+
+ if (param->nargs != 0) {
+ index = njs_value_to_number(¶m->args[0]);
+
+ if (nxt_slow_path(index < 0 || index >= length)) {
+ num = NJS_NAN;
+ goto done;
+ }
+ }
+
+ if ((uint32_t) length == string.size) {
+ /* Byte or ASCII string. */
+ code = string.start[index];
+
+ } else {
+ /* UTF-8 string. */
+ end = string.start + string.size;
+ start = njs_string_offset(string.start, end, index);
+ code = nxt_utf8_decode(&start, end);
+ }
+
+ num = code;
+
+done:
+
+ njs_number_set(&vm->retval, num);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_index_of(njs_vm_t *vm, njs_param_t *param)
+{
+ ssize_t start;
+ uintptr_t nargs;
+ njs_ret_t index;
+ njs_value_t *args;
+
+ index = -1;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ start = 0;
+ args = param->args;
+
+ if (nargs > 1) {
+ start = njs_value_to_number(&args[1]);
+
+ if (start < 0) {
+ start = 0;
+ }
+ }
+
+ index = njs_string_index_of(vm, param->object, &args[0], start);
+ }
+
+ njs_number_set(&vm->retval, index);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_last_index_of(njs_vm_t *vm, njs_param_t *param)
+{
+ uintptr_t nargs;
+ njs_ret_t ret, index, last;
+ njs_value_t *args;
+
+ index = -1;
+ nargs = param->nargs;
+
+ if (nargs != 0) {
+ last = NJS_STRING_MAX_LENGTH;
+ args = param->args;
+
+ if (nargs > 1) {
+ last = njs_value_to_number(&args[1]);
+
+ if (last < 0) {
+ last = 0;
+ }
+ }
+
+ ret = 0;
+
+ for ( ;; ) {
+ ret = njs_string_index_of(vm, param->object, &args[0], ret);
+
+ if (ret < 0 || ret >= last) {
+ break;
+ }
+
+ index = ret++;
+ }
+ }
+
+ njs_number_set(&vm->retval, index);
+
+ return NXT_OK;
+}
+
+
+static nxt_noinline njs_ret_t
+njs_string_index_of(njs_vm_t *vm, njs_value_t *src, njs_value_t *search_string,
+ size_t index)
+{
+ njs_ret_t length;
+ const u_char *p, *end;
+ njs_string_prop_t string, search;
+
+ (void) njs_string_prop(&search, search_string);
+
+ length = njs_string_prop(&string, src);
+
+ if (index < (size_t) length) {
+
+ p = string.start;
+ end = p + string.size;
+
+ if (string.size == (size_t) length) {
+ /* Byte or ASCII string. */
+ p += index;
+
+ } else {
+ /* UTF-8 string. */
+ p = njs_string_offset(p, end, index);
+ }
+
+ while (p < end) {
+ if (memcmp(p, search.start, search.size) == 0) {
+ return index;
+ }
+
+ index++;
+ p = nxt_utf8_next(p, end);
+ }
+
+ } else if (search.size == 0) {
+ return length;
+ }
+
+ return -1;
+}
+
+
+/*
+ * njs_string_offset() assumes that index is correct
+ * and the optional offset map has been initialized.
+ */
+
+nxt_noinline const u_char *
+njs_string_offset(const u_char *start, const u_char *end, size_t index)
+{
+ uint32_t *map;
+ nxt_uint_t skip;
+
+ if (index >= NJS_STRING_MAP_OFFSET) {
+ map = (uint32_t *) nxt_align_ptr(end, sizeof(uint32_t));
+
+ start += map[index / NJS_STRING_MAP_OFFSET - 1];
+ }
+
+ for (skip = index % NJS_STRING_MAP_OFFSET; skip != 0; skip--) {
+ start = nxt_utf8_next(start, end);
+ }
+
+ return start;
+}
+
+
+/*
+ * njs_string_index() assumes that offset is correct
+ * and the optional offset map has been initialized.
+ */
+
+nxt_noinline uint32_t
+njs_string_index(njs_string_prop_t *string, uint32_t offset)
+{
+ uint32_t *map, last, index;
+ const u_char *p, *start, *end;
+
+ if (string->size == string->length) {
+ return offset;
+ }
+
+ last = 0;
+ index = 0;
+
+ if (string->length >= NJS_STRING_MAP_OFFSET) {
+
+ end = string->start + string->size;
+ map = (uint32_t *) nxt_align_ptr(end, sizeof(uint32_t));
+
+ while (index + NJS_STRING_MAP_OFFSET < string->length
+ && *map <= offset)
+ {
+ last = *map++;
+ index += NJS_STRING_MAP_OFFSET;
+ }
+ }
+
+ p = string->start + last;
+ start = string->start + offset;
+ end = string->start + string->size;
+
+ while (p < start) {
+ index++;
+ p = nxt_utf8_next(p, end);
+ }
+
+ return index;
+}
+
+
+static njs_ret_t
+njs_string_prototype_search(njs_vm_t *vm, njs_param_t *param)
+{
+ nxt_int_t index;
+ nxt_uint_t n;
+ njs_ret_t ret;
+ njs_string_prop_t string;
+ njs_regexp_pattern_t *pattern;
+ int captures[3];
+
+ /* TODO: convert object to String. */
+
+ index = 0;
+
+ if (param->nargs != 0) {
+ /*
+ * TODO: convert args[0] to RegExp:
+ * RegExp > RegExp
+ * String > RegExp
+ * undefined > //
+ * otherwise > String > RegExp
+ */
+ pattern = param->args[0].data.u.regexp->pattern;
+
+ index = -1;
+
+ (void) njs_string_prop(&string, param->object);
+
+ n = (string.length != 0 && string.length != string.size);
+
+ if (pattern->code[n] != NULL) {
+ ret = pcre_exec(pattern->code[n], pattern->extra[n],
+ (char *) string.start, string.size,
+ 0, 0, captures, 3);
+
+ if (ret >= 0) {
+ index = njs_string_index(&string, captures[0]);
+
+ } else if (ret != PCRE_ERROR_NOMATCH) {
+ /* TODO: exception */
+ return NXT_ERROR;
+ }
+ }
+ }
+
+ njs_number_set(&vm->retval, index);
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_prototype_match(njs_vm_t *vm, njs_param_t *param)
+{
+ u_char *start;
+ int32_t size, length;
+ nxt_uint_t n, utf8;
+ njs_ret_t ret;
+ njs_value_t *args;
+ njs_array_t *array;
+ njs_regexp_t *regexp;
+ njs_string_prop_t string;
+ njs_regexp_pattern_t *pattern;
+ int captures[3];
+
+ /* TODO: empty regexp */
+
+ args = param->args;
+ regexp = args[0].data.u.regexp;
+
+ if (!regexp->pattern->global) {
+ /*
+ * string.match(regexp) is the same as regexp.exec(string)
+ * if the regexp has no global flag.
+ */
+ param->args = param->object;
+ param->object = args;
+ param->nargs = 1;
+
+ return njs_regexp_prototype_exec(vm, param);
+ }
+
+ vm->retval = njs_value_null;
+
+ (void) njs_string_prop(&string, param->object);
+
+ utf8 = 0;
+ n = 0;
+
+ if (string.length != 0) {
+ utf8 = 1;
+ n = 1;
+
+ if (string.length != string.size) {
+ utf8 = 2;
+ }
+ }
+
+ pattern = regexp->pattern;
+
+ if (pattern->code[n] != NULL) {
+ array = NULL;
+
+ if (n != 0) {
+ utf8 = 2;
+ } else if (string.length != 0) {
+ utf8 = 1;
+ } else {
+ utf8 = 1;
+ }
+
+ do {
+ ret = pcre_exec(pattern->code[n], pattern->extra[n],
+ (char *) string.start, string.size,
+ 0, 0, captures, 3);
+
+ if (ret >= 0) {
+ if (array != NULL) {
+ if (array->length == array->size) {
+ ret = njs_array_realloc(vm, array, 0, array->size + 1);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ } else {
+ array = njs_array_alloc(vm, 0, NJS_ARRAY_SPARE);
+ if (nxt_slow_path(array == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.array = array;
+ vm->retval.type = NJS_ARRAY;
+ vm->retval.data.truth = 1;
+ }
+
+ start = &string.start[captures[0]];
+
+ string.start += captures[1];
+ string.size -= captures[1];
+
+ size = captures[1] - captures[0];
+
+ switch (utf8) {
+ case 0:
+ length = 0;
+ break;
+ case 1:
+ length = size;
+ break;
+ default:
+ length = nxt_utf8_length(start, size);
+ break;
+ }
+
+ ret = njs_string_create(vm, &array->start[array->length],
+ start, size, length);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ array->length++;
+
+ } else if (ret == PCRE_ERROR_NOMATCH) {
+ break;
+
+ } else {
+ /* TODO: internal error exception */
+ return NXT_ERROR;
+ }
+
+ } while (string.size > 0);
+ }
+
+ regexp->last_index = 0;
+
+ return NXT_OK;
+}
+
+
+njs_ret_t
+njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src)
+{
+ njs_ret_t ret;
+ njs_param_t param;
+ njs_object_prop_t *prop;
+ const njs_value_t *value;
+ nxt_lvlhsh_query_t lhq;
+
+ switch (src->type) {
+
+ case NJS_NULL:
+ value = &njs_string_null;
+ break;
+
+ case NJS_VOID:
+ value = &njs_string_void;
+ break;
+
+ case NJS_BOOLEAN:
+ value = njs_is_true(src) ? &njs_string_true : &njs_string_false;
+ break;
+
+ case NJS_NUMBER:
+ return njs_number_to_string(vm, dst, src);
+
+ case NJS_STRING:
+ /* GC: njs_retain(src); */
+ value = src;
+ break;
+
+ case NJS_OBJECT:
+ case NJS_ARRAY:
+ case NJS_FUNCTION:
+ case NJS_REGEXP:
+ lhq.key_hash = NJS_TO_STRING_HASH;
+ lhq.key.len = sizeof("toString") - 1;
+ lhq.key.data = (u_char *) "toString";
+
+ prop = njs_object_property(vm, &src->data.u.array->object, &lhq);
+
+ if (nxt_fast_path(prop != NULL)) {
+ param.object = (njs_value_t *) src;
+ param.args = NULL;
+ param.nargs = 0;
+ param.retval = (njs_index_t) dst;
+
+ ret = njs_function_apply(vm, &prop->value, ¶m);
+ if (nxt_fast_path(ret == 0)) {
+ *dst = vm->retval;
+ return ret;
+ }
+ }
+
+ return NXT_ERROR;
+
+ /*
+ * TODO:
+ * function, regexp: find "toString()" in prototype chain:
+ * function: full function text.
+ * regex: full regexp text like "/regexp/gim".
+ */
+
+ case NJS_NATIVE:
+ case NJS_EXTERNAL:
+ value = &njs_string_native;
+ break;
+
+ default: /* NJS_INVALID */
+ return NXT_ERROR;
+ }
+
+ *dst = *value;
+
+ return NXT_OK;
+}
+
+
+njs_ret_t
+njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst, const njs_value_t *src)
+{
+ u_char *start;
+ size_t size;
+ njs_ret_t ret;
+ njs_value_t value;
+
+ if (nxt_fast_path(src != NULL)) {
+ ret = njs_value_to_string(vm, &value, src);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ size = value.short_string.size;
+
+ if (size != NJS_STRING_LONG) {
+ start = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
+ if (nxt_slow_path(start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ memcpy(start, value.short_string.start, size);
+
+ } else {
+ size = value.data.string_size;
+ start = value.data.u.string->start;
+ }
+
+ dst->len = size;
+ dst->data = start;
+ }
+
+ return ret;
+ }
+
+ dst->len = 0;
+ dst->data = NULL;
+
+ return NXT_OK;
+}
+
+
+double
+njs_string_to_number(njs_value_t *value)
+{
+ double num;
+ size_t size;
+ nxt_bool_t minus;
+ const u_char *p, *end;
+
+ size = value->short_string.size;
+
+ if (size != NJS_STRING_LONG) {
+ p = value->short_string.start;
+
+ } else {
+ size = value->data.string_size;
+ p = value->data.u.string->start;
+ }
+
+ end = p + size;
+
+ while (p < end) {
+ if (*p != ' ' && *p != '\t') {
+ break;
+ }
+
+ p++;
+ }
+
+ if (p == end) {
+ return 0.0;
+ }
+
+ minus = 0;
+
+ if (*p == '+') {
+ p++;
+
+ } else if (*p == '-') {
+ p++;
+ minus = 1;
+ }
+
+ if (*p >= '0' && *p <= '9') {
+ num = njs_number_parse(&p, end);
+
+ } else {
+ return NJS_NAN;
+ }
+
+ while (p < end) {
+ if (*p != ' ' && *p != '\t') {
+ return NJS_NAN;
+ }
+
+ p++;
+ }
+
+ return minus ? -num : num;
+}
+
+
+static const njs_object_prop_t njs_string_prototype_properties[] =
+{
+ { njs_getter(njs_string_prototype_get_prototype),
+ njs_string("__proto__"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_string_prototype_length),
+ njs_string("length"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_string_prototype_bytes),
+ njs_string("bytes"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_getter(njs_string_prototype_utf8),
+ njs_string("utf8"),
+ NJS_NATIVE_GETTER, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_concat, 0),
+ njs_string("concat"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_slice, 0),
+ njs_string("slice"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_substring, 0),
+ njs_string("substring"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_substr, 0),
+ njs_string("substr"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_char_at, 0),
+ njs_string("charAt"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_char_code_at, 0),
+ njs_string("charCodeAt"),
+ NJS_METHOD, 0, 0, 0, },
+
+ /* ECMAScript 6, codePointAt(). */
+
+ { njs_native_function(njs_string_prototype_char_code_at, 0),
+ njs_string("codePointAt"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_index_of, 0),
+ njs_string("indexOf"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_last_index_of, 0),
+ njs_string("lastIndexOf"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_search, 0),
+ njs_string("search"),
+ NJS_METHOD, 0, 0, 0, },
+
+ { njs_native_function(njs_string_prototype_match, 0),
+ njs_string("match"),
+ NJS_METHOD, 0, 0, 0, },
+};
+
+
+nxt_int_t
+njs_string_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash)
+{
+ return njs_object_hash_create(vm, hash, njs_string_prototype_properties,
+ nxt_nitems(njs_string_prototype_properties));
+}
+
+
+static nxt_int_t
+njs_values_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_value_t *value;
+
+ value = data;
+
+ if (lhq->key.len == sizeof(njs_value_t)
+ && memcmp(lhq->key.data, value, sizeof(njs_value_t)) == 0)
+ {
+ return NXT_OK;
+ }
+
+ if (value->type == NJS_STRING
+ && value->data.string_size == lhq->key.len
+ && memcmp(value->data.u.string->start, lhq->key.data, lhq->key.len)
+ == 0)
+ {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static const nxt_lvlhsh_proto_t njs_values_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_values_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+njs_index_t
+njs_value_index(njs_vm_t *vm, njs_parser_t *parser, const njs_value_t *src)
+{
+ u_char *start;
+ uint32_t value_size, size, length;
+ nxt_int_t ret;
+ njs_value_t *value;
+ njs_string_t *string;
+ nxt_lvlhsh_query_t lhq;
+
+ if (src->type != NJS_STRING || src->short_string.size != NJS_STRING_LONG) {
+ size = sizeof(njs_value_t);
+ start = (u_char *) src;
+
+ } else {
+ size = src->data.string_size;
+ start = src->data.u.string->start;
+ }
+
+ lhq.key_hash = nxt_djb_hash(start, size);
+ lhq.key.len = size;
+ lhq.key.data = start;
+ lhq.proto = &njs_values_hash_proto;
+
+ if (nxt_lvlhsh_find(&vm->values_hash, &lhq) == NXT_OK) {
+ value = lhq.value;
+
+ } else if (nxt_lvlhsh_find(&parser->values_hash, &lhq) == NXT_OK) {
+ value = lhq.value;
+
+ } else {
+ value_size = 0;
+
+ if (start != (u_char *) src) {
+ /* Long string value is allocated together with string. */
+ value_size = sizeof(njs_value_t) + sizeof(njs_string_t);
+
+ length = src->data.u.string->length;
+
+ if (size != length && length > NJS_STRING_MAP_OFFSET) {
+ size = nxt_align_size(size, sizeof(uint32_t));
+ size += ((length - 1) / NJS_STRING_MAP_OFFSET)
+ * sizeof(uint32_t);
+ }
+ }
+
+ value = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
+ value_size + size);
+ if (nxt_slow_path(value == NULL)) {
+ return NJS_INDEX_NONE;
+ }
+
+ *value = *src;
+
+ if (start != (u_char *) src) {
+ string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t));
+ value->data.u.string = string;
+
+ string->start = (u_char *) string + sizeof(njs_string_t);
+ string->length = src->data.u.string->length;
+ string->retain = 0xffff;
+
+ memcpy(string->start, start, size);
+ }
+
+ lhq.replace = 0;
+ lhq.value = value;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&parser->values_hash, &lhq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_INDEX_NONE;
+ }
+ }
+
+ if (start != (u_char *) src) {
+ /*
+ * The source node value must be updated with the shared value
+ * allocated from the permanent memory pool because the node
+ * value can be used as a variable initial value.
+ */
+ *(njs_value_t *) src = *value;
+ }
+
+ return (njs_index_t) value;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_STRING_H_INCLUDED_
+#define _NJS_STRING_H_INCLUDED_
+
+
+/*
+ * nJSVM supports two string variants:
+ *
+ * 1) short strings which size is lesser than 14 bytes, these strings are
+ * stored inside njs_value_t (see njs_vm.h for details);
+ *
+ * 2) and long strings using additional njs_string_t structure.
+ * This structure has the start field to support external strings.
+ * The long strings can have optional UTF-8 offset map.
+ *
+ * The number of the string variants is limited to 2 variants to minimize
+ * overhead of processing string fields.
+ */
+
+/* The maximum signed int32_t. */
+#define NJS_STRING_MAX_LENGTH 0x7fffffff
+
+/*
+ * Should be power of two to use shift and binary and operations instead of
+ * division and remainder operations but no less than 16 because the maximum
+ * length of short string inlined in njs_value_t is less than 16 bytes.
+ */
+#define NJS_STRING_MAP_OFFSET 32
+
+/*
+ * The JavaScript standard states that strings are stored in UTF-16.
+ * nJSVM allows to store any byte sequences in strings. A size of the
+ * string in bytes is stored in the size field. If a byte sequence is
+ * valid UTF-8 string then its length is stored in the UTF-8 length field.
+ * Otherwise, the length field is zero. If a string is UTF-8 string then
+ * string functions work with UTF-8 characters positions and lengths.
+ * Othersise they work with byte positions and lengths. Using UTF-8
+ * encoding does not allow to get quickly a character at specified position.
+ * To speed up this search a map of offsets is stored after the UTF-8 string.
+ * The map is aligned to uint32_t and contains byte positions of each
+ * NJS_STRING_MAP_OFFSET UTF-8 character except zero position. The map
+ * can be allocated and updated on demand. If a string come outside
+ * JavaScript as byte sequnece just to be concatenated or to be used in
+ * regular expressions the offset map is not required.
+ *
+ * The map is not allocated:
+ * 1) if the length is zero hence it is a byte string;
+ * 2) if the size and length are equal so the string contains only ASCII
+ * characters map is not required;
+ * 3) if the length is less than NJS_STRING_MAP_OFFSET.
+ *
+ * The current implementation does not support Unicode surrogate pairs.
+ * If offset in map points to surrogate pair, it the previous offset
+ * should be used and so on until start of the string.
+ */
+
+struct njs_string_s {
+ u_char *start;
+ uint32_t length; /* Length in UTF-8 characters. */
+ uint32_t retain; /* Link counter. */
+};
+
+
+typedef struct {
+ size_t size;
+ size_t length;
+ u_char *start;
+} njs_string_prop_t;
+
+
+u_char *njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size,
+ uint32_t length)
+ NXT_MALLOC_LIKE;
+void njs_string_copy(njs_value_t *dst, njs_value_t *src);
+njs_ret_t njs_string_validate(njs_vm_t *vm, njs_string_prop_t *string,
+ njs_value_t *value);
+nxt_noinline size_t njs_string_prop(njs_string_prop_t *string,
+ njs_value_t *value);
+njs_ret_t njs_string_ctor_function(njs_vm_t *vm, njs_param_t *param);
+nxt_int_t njs_string_function_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+void njs_string_offset_map_init(const u_char *start, size_t size);
+nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2);
+nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2);
+njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
+ const njs_string_prop_t *string, size_t string_length, size_t index,
+ size_t length);
+const u_char *njs_string_offset(const u_char *start, const u_char *end,
+ size_t index);
+nxt_noinline uint32_t njs_string_index(njs_string_prop_t *string,
+ uint32_t offset);
+njs_ret_t njs_string_prototype_concat(njs_vm_t *vm, njs_param_t *param);
+njs_ret_t njs_value_to_string(njs_vm_t *vm, njs_value_t *dst,
+ const njs_value_t *src);
+njs_ret_t njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst,
+ const njs_value_t *src);
+double njs_string_to_number(njs_value_t *value);
+nxt_int_t njs_string_prototype_hash(njs_vm_t *vm, nxt_lvlhsh_t *hash);
+
+njs_index_t njs_value_index(njs_vm_t *vm, njs_parser_t *parser,
+ const njs_value_t *src);
+
+
+#endif /* _NJS_STRING_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_utf8.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_regexp.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+static njs_variable_t *njs_variable_alloc(njs_vm_t *vm,
+ njs_parser_t *parser, nxt_str_t *name);
+
+
+static nxt_int_t
+njs_variables_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_variable_t *var;
+
+ var = data;
+
+ if (lhq->key.len == var->name_len
+ && memcmp(var->name_start, lhq->key.data, lhq->key.len) == 0)
+ {
+ return NXT_OK;
+ }
+
+
+ return NXT_DECLINED;
+}
+
+
+static const nxt_lvlhsh_proto_t njs_variables_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_variables_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+njs_variable_t *
+njs_parser_variable(njs_vm_t *vm, njs_parser_t *parser, nxt_uint_t *level)
+{
+ nxt_int_t ret;
+ nxt_uint_t n;
+ njs_parser_t *scope;
+ njs_variable_t *var;
+ nxt_lvlhsh_query_t lhq;
+
+ level = 0;
+
+ lhq.key_hash = parser->lexer->key_hash;
+ lhq.key = parser->lexer->text;
+ lhq.proto = &njs_variables_hash_proto;
+
+ scope = parser;
+
+ do {
+ var = scope->arguments->start;
+ n = scope->arguments->items;
+
+ while (n != 0) {
+ if (lhq.key.len == var->name_len
+ && memcmp(var->name_start, lhq.key.data, lhq.key.len) == 0)
+ {
+ return var;
+ }
+
+ var++;
+ n--;
+ }
+
+ if (nxt_lvlhsh_find(&scope->variables_hash, &lhq) == NXT_OK) {
+ return lhq.value;
+ }
+
+ scope = scope->parent;
+ level++;
+
+ } while (scope != NULL);
+
+ level = 0;
+
+ if (nxt_lvlhsh_find(&vm->variables_hash, &lhq) == NXT_OK) {
+ return lhq.value;
+ }
+
+ var = njs_variable_alloc(vm, parser, &parser->lexer->text);
+ if (nxt_slow_path(var == NULL)) {
+ return NULL;
+ }
+
+ lhq.replace = 0;
+ lhq.value = var;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&parser->variables_hash, &lhq);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return var;
+ }
+
+ return NULL;
+}
+
+
+static njs_variable_t *
+njs_variable_alloc(njs_vm_t *vm, njs_parser_t *parser, nxt_str_t *name)
+{
+ njs_value_t *value;
+ njs_variable_t *var;
+
+ var = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_variable_t));
+
+ if (nxt_fast_path(var != NULL)) {
+ var->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->len);
+
+ if (nxt_fast_path(var->name_start != NULL)) {
+
+ memcpy(var->name_start, name->data, name->len);
+ var->name_len = name->len;
+
+ value = nxt_vector_add(parser->scope_values, &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_fast_path(value != NULL)) {
+ *value = njs_value_void;
+ var->index = njs_parser_index(parser, parser->scope);
+ return var;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+njs_value_t *
+njs_variable_value(njs_parser_t *parser, njs_index_t index)
+{
+ u_char *scope;
+
+ scope = parser->scope_values->start;
+
+ return (njs_value_t *) (scope + (njs_offset(index) - parser->scope_offset));
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_VARIABLE_H_INCLUDED_
+#define _NJS_VARIABLE_H_INCLUDED_
+
+
+typedef enum {
+ NJS_VARIABLE_CREATED = 0,
+ NJS_VARIABLE_PENDING,
+ NJS_VARIABLE_USED,
+ NJS_VARIABLE_SET,
+ NJS_VARIABLE_DECLARED,
+} njs_variable_state_t;
+
+
+typedef struct {
+ u_char *name_start;
+ uint16_t name_len;
+ njs_variable_state_t state:8; /* 3 bits */
+
+ njs_index_t index;
+} njs_variable_t;
+
+
+njs_variable_t *njs_parser_variable(njs_vm_t *vm, njs_parser_t *parser,
+ nxt_uint_t *level);
+njs_value_t *njs_variable_value(njs_parser_t *parser, njs_index_t index);
+
+
+#endif /* _NJS_VARIABLE_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_array.h>
+#include <njs_function.h>
+#include <njs_regexp.h>
+#include <njs_extern.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+/* The values must not coincide with NXT_OK, NXT_ERROR, and NXT_DECLINED. */
+#define NJS_PRIMITIVE_VALUE 1
+#define NJS_ARRAY_VALUE 2
+#define NJS_EXTERNAL_VALUE 3
+
+#define NJS_PROPERTY_QUERY_GET 0
+#define NJS_PROPERTY_QUERY_IN 0
+#define NJS_PROPERTY_QUERY_DELETE 1
+#define NJS_PROPERTY_QUERY_SET 2
+
+
+typedef struct {
+ nxt_lvlhsh_query_t lhq;
+ njs_value_t value;
+ njs_object_t *prototype;
+ uint8_t query;
+ uint8_t shared;
+} njs_property_query_t;
+
+
+typedef struct {
+ int32_t index;
+ nxt_lvlhsh_each_t lhe;
+} njs_property_each_t;
+
+
+/*
+ * These functions are forbidden to inline to minimize JavaScript VM
+ * interpreter memory footprint. The size is less than 8K on AMD64
+ * and should fit in CPU L1 instruction cache.
+ */
+
+static nxt_noinline njs_ret_t njs_property_query(njs_vm_t *vm,
+ njs_property_query_t *pq, njs_value_t *object, njs_value_t *property);
+static njs_ret_t njs_array_property_query(njs_vm_t *vm,
+ njs_property_query_t *pq, njs_value_t *object, int32_t index);
+static njs_ret_t njs_object_property_query(njs_vm_t *vm,
+ njs_property_query_t *pq, njs_value_t *value, njs_object_t *object);
+static njs_ret_t njs_function_private_copy(njs_vm_t *vm,
+ njs_property_query_t *pq);
+static nxt_noinline uint32_t njs_integer_value(double num);
+static nxt_noinline njs_ret_t njs_values_equal(njs_value_t *val1,
+ njs_value_t *val2);
+static nxt_noinline njs_ret_t njs_values_compare(njs_value_t *val1,
+ njs_value_t *val2);
+static nxt_noinline nxt_bool_t njs_values_strict_equal(njs_value_t *val1,
+ njs_value_t *val2);
+void njs_debug(njs_index_t index, njs_value_t *value);
+
+
+const njs_value_t njs_value_null = njs_value(NJS_NULL, 0, 0.0);
+const njs_value_t njs_value_void = njs_value(NJS_VOID, 0, NJS_NAN);
+const njs_value_t njs_value_false = njs_value(NJS_BOOLEAN, 0, 0.0);
+const njs_value_t njs_value_true = njs_value(NJS_BOOLEAN, 1, 1.0);
+const njs_value_t njs_value_zero = njs_value(NJS_NUMBER, 0, 0.0);
+const njs_value_t njs_value_nan = njs_value(NJS_NUMBER, 0, NJS_NAN);
+
+const njs_value_t njs_string_empty = njs_string("");
+const njs_value_t njs_string_comma = njs_string(",");
+const njs_value_t njs_string_void = njs_string("undefined");
+const njs_value_t njs_string_null = njs_string("null");
+const njs_value_t njs_string_boolean = njs_string("boolean");
+const njs_value_t njs_string_false = njs_string("false");
+const njs_value_t njs_string_true = njs_string("true");
+const njs_value_t njs_string_number = njs_string("number");
+const njs_value_t njs_string_minus_infinity =
+ njs_string("-Infinity");
+const njs_value_t njs_string_plus_infinity =
+ njs_string("Infinity");
+const njs_value_t njs_string_nan = njs_string("NaN");
+const njs_value_t njs_string_string = njs_string("string");
+const njs_value_t njs_string_object = njs_string("object");
+const njs_value_t njs_string_function = njs_string("function");
+const njs_value_t njs_string_native = njs_string("[native code]");
+const njs_value_t njs_string_prototype = njs_string("prototype");
+const njs_value_t njs_string_constructor = njs_string("constructor");
+
+const njs_value_t njs_string_object_null = njs_string("[object Null]");
+const njs_value_t njs_string_object_undefined =
+ njs_long_string("[object Undefined]");
+const njs_value_t njs_string_object_boolean =
+ njs_long_string("[object Boolean]");
+const njs_value_t njs_string_object_number =
+ njs_long_string("[object Number]");
+const njs_value_t njs_string_object_string =
+ njs_long_string("[object String]");
+const njs_value_t njs_string_object_object =
+ njs_long_string("[object Object]");
+const njs_value_t njs_string_object_array = njs_string("[object Array]");
+const njs_value_t njs_string_object_function =
+ njs_long_string("[object Function]");
+const njs_value_t njs_string_object_regexp =
+ njs_long_string("[object RegExp]");
+
+const njs_value_t njs_exception_syntax_error = njs_string("SyntaxError");
+const njs_value_t njs_exception_reference_error = njs_string("ReferenceError");
+const njs_value_t njs_exception_type_error = njs_string("TypeError");
+const njs_value_t njs_exception_range_error = njs_string("RangeError");
+const njs_value_t njs_exception_memory_error = njs_string("MemoryError");
+
+
+/*
+ * The nJSVM is optimized for an ABIs where the first several arguments
+ * are passed in registers (AMD64, ARM32/64): two pointers to the operand
+ * values is passed as arguments although they are not always used.
+ */
+
+nxt_int_t
+njs_vmcode_interpreter(njs_vm_t *vm)
+{
+ u_char *catch;
+ njs_ret_t ret;
+ njs_value_t *retval, *value1, *value2;
+ njs_frame_t *frame;
+ njs_native_frame_t *previous;
+ njs_vmcode_generic_t *vmcode;
+
+ for ( ;; ) {
+
+ again:
+ for ( ;; ) {
+
+ vmcode = (njs_vmcode_generic_t *) vm->current;
+
+ /*
+ * The first operand index is passed as the value2 to
+ * njs_vmcode_jump(),
+ * njs_vmcode_if_true_jump(),
+ * njs_vmcode_if_false_jump(),
+ * njs_vmcode_validate(),
+ * njs_vmcode_call(),
+ * njs_vmcode_return(),
+ * njs_vmcode_try_start(),
+ * njs_vmcode_try_next(),
+ * njs_vmcode_try_end(),
+ * njs_vmcode_catch().
+ * njs_vmcode_throw().
+ * njs_vmcode_stop().
+ */
+ value2 = (njs_value_t *) vmcode->operand1;
+ value1 = NULL;
+
+ switch (vmcode->code.operands) {
+
+ case NJS_VMCODE_3OPERANDS:
+ value2 = njs_vmcode_operand(vm, vmcode->operand3);
+
+ case NJS_VMCODE_2OPERANDS:
+ value1 = njs_vmcode_operand(vm, vmcode->operand2);
+ }
+
+ ret = vmcode->code.operation(vm, value1, value2);
+
+ /*
+ * On success an operation returns size of the bytecode,
+ * a jump offset or zero after the call or return operations.
+ * Jumps can return a negative offset. Compilers can generate
+ * (ret < 0 && ret >= NJS_PASS)
+ * as a single unsigned comparision.
+ */
+
+ if (nxt_slow_path(ret < 0 && ret >= NJS_PASS)) {
+ break;
+ }
+
+ vm->current += ret;
+
+ if (vmcode->code.retval) {
+ retval = njs_vmcode_operand(vm, vmcode->operand1);
+ //njs_release(vm, retval);
+ *retval = vm->retval;
+ }
+ }
+
+ switch (ret) {
+
+ case NJS_TRAP_NUMBER:
+ ret = njs_vmcode_trap(vm,
+ vm->number_trap + sizeof(njs_vmcode_to_number_t),
+ value1, value1, 0);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ goto again;
+ }
+
+ ret = NXT_ERROR;
+ break;
+
+ case NJS_TRAP_LVALUE_NUMBER:
+ ret = njs_vmcode_trap(vm,
+ vm->number_trap + sizeof(njs_vmcode_to_number_t),
+ value1, value2, 1);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ goto again;
+ }
+
+ ret = NXT_ERROR;
+ break;
+
+ case NJS_TRAP_NUMBERS:
+ ret = njs_vmcode_trap(vm, vm->number_trap, value1, value2, 0);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ goto again;
+ }
+
+ ret = NXT_ERROR;
+ break;
+
+ case NJS_TRAP_STRINGS:
+ ret = njs_vmcode_trap(vm, vm->string_trap, value1, value2, 0);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ goto again;
+ }
+
+ ret = NXT_ERROR;
+ break;
+ }
+
+ if (ret == NXT_ERROR) {
+
+ for ( ;; ) {
+ frame = (njs_frame_t *) vm->frame;
+ catch = frame->native.u.exception.catch;
+
+ if (catch != NULL) {
+ vm->current = catch;
+ goto again;
+ }
+
+ previous = frame->native.previous;
+ if (previous == NULL) {
+ return ret;
+ }
+
+ vm->frame = previous;
+
+ /* GC: NJS_SCOPE_ARGUMENTS and NJS_SCOPE_LOCAL. */
+
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
+ vm->scopes[NJS_SCOPE_LOCAL] = frame->prev_local;
+ vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
+
+ if (frame->native.start) {
+ nxt_mem_cache_free(vm->mem_cache_pool, frame);
+ }
+ }
+ }
+
+ /* NXT_AGAIN, NXT_DONE */
+
+ return ret;
+ }
+}
+
+
+nxt_noinline void
+njs_value_retain(njs_value_t *value)
+{
+ njs_string_t *string;
+
+ if (value->type == NJS_STRING) {
+
+ if (value->data.external0 != 0xff) {
+ string = value->data.u.string;
+
+ nxt_thread_log_debug("retain:%uxD \"%*s\"", string->retain,
+ value->data.string_size, string->start);
+
+ if (string->retain != 0xffff) {
+ string->retain++;
+ }
+ }
+ }
+}
+
+
+nxt_noinline void
+njs_value_release(njs_vm_t *vm, njs_value_t *value)
+{
+ njs_string_t *string;
+
+ if (value->type == NJS_STRING) {
+
+ if (value->data.external0 != 0xff) {
+ string = value->data.u.string;
+
+ nxt_thread_log_debug("release:%uxD \"%*s\"", string->retain,
+ value->data.string_size, string->start);
+
+ if (string->retain != 0xffff) {
+ string->retain--;
+
+#if 0
+ if (string->retain == 0) {
+ if ((u_char *) string + sizeof(njs_string_t)
+ != string->start)
+ {
+ nxt_memcache_pool_free(vm->mem_cache_pool,
+ string->start);
+ }
+
+ nxt_memcache_pool_free(vm->mem_cache_pool, string);
+ }
+#endif
+ }
+ }
+ }
+}
+
+
+njs_ret_t
+njs_vmcode_object_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_object_t *object;
+
+ object = njs_object_alloc(vm);
+
+ if (nxt_fast_path(object != NULL)) {
+ vm->retval.data.u.object = object;
+ vm->retval.type = NJS_OBJECT;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_object_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_array_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ uint32_t size;
+ njs_array_t *array;
+ njs_value_t *value;
+ njs_vmcode_array_t *code;
+
+ code = (njs_vmcode_array_t *) vm->current;
+
+ array = njs_array_alloc(vm, code->length, NJS_ARRAY_SPARE);
+
+ if (nxt_fast_path(array != NULL)) {
+ size = array->size;
+ value = array->start;
+
+ do {
+ njs_set_invalid(value);
+ value++;
+ size--;
+ } while (size != 0);
+
+ vm->retval.data.u.array = array;
+ vm->retval.type = NJS_ARRAY;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_array_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_function_create(njs_vm_t *vm, njs_value_t *invld1,
+ njs_value_t *invld2)
+{
+ njs_function_t *func;
+ njs_vmcode_function_create_t *code;
+
+ func = nxt_mem_cache_zalign(vm->mem_cache_pool, sizeof(njs_value_t),
+ sizeof(njs_function_t));
+
+ if (nxt_fast_path(func != NULL)) {
+ func->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ func->args_offset = 1;
+
+ code = (njs_vmcode_function_create_t *) vm->current;
+ func->code.script = code->function;
+ vm->retval.data.u.function = func;
+ vm->retval.type = NJS_FUNCTION;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_function_create_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_regexp_create(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ njs_regexp_t *regexp;
+ njs_vmcode_regexp_t *code;
+
+ code = (njs_vmcode_regexp_t *) vm->current;
+
+ regexp = njs_regexp_alloc(vm, code->pattern);
+
+ if (nxt_fast_path(regexp != NULL)) {
+ vm->retval.data.u.regexp = regexp;
+ vm->retval.type = NJS_REGEXP;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_regexp_t);
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ size_t length;
+ double num;
+ int32_t index;
+ uintptr_t data;
+ njs_ret_t ret;
+ njs_value_t *val;
+ njs_extern_t *ext;
+ njs_object_prop_t *prop;
+ njs_string_prop_t string;
+ const njs_value_t *retval;
+ njs_property_query_t pq;
+
+ pq.query = NJS_PROPERTY_QUERY_GET;
+
+ ret = njs_property_query(vm, &pq, object, property);
+
+ retval = &njs_value_void;
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ prop = pq.lhq.value;
+
+ switch (prop->type) {
+
+ case NJS_METHOD:
+ if (pq.shared) {
+ ret = njs_function_private_copy(vm, &pq);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ prop = pq.lhq.value;
+ }
+
+ case NJS_PROPERTY:
+ retval = &prop->value;
+ break;
+
+ case NJS_NATIVE_GETTER:
+ ret = prop->value.data.u.getter(vm, object);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return sizeof(njs_vmcode_prop_get_t);
+ }
+
+ return ret;
+
+ default:
+ nxt_thread_log_alert("invalid property get type:%d", prop->type);
+
+ return NXT_ERROR;
+ }
+
+ } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+
+ val = pq.lhq.value;
+
+ if (njs_is_valid(val)) {
+ retval = val;
+ }
+
+ } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+
+ ext = object->data.u.external;
+
+ ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
+
+ if (ret == NXT_OK) {
+ ext = pq.lhq.value;
+
+ if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
+ retval = &ext->value;
+ goto done;
+ }
+
+ data = ext->data;
+
+ } else {
+ data = (uintptr_t) &pq.lhq.key;
+ }
+
+ vm->retval = njs_value_void;
+
+ ret = ext->get(vm, &vm->retval, vm->external[ext->object], data);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* The vm->retval is already retained by ext->get(). */
+
+ return sizeof(njs_vmcode_prop_get_t);
+
+ } else if (nxt_fast_path(ret == NJS_PRIMITIVE_VALUE)) {
+
+ if (njs_is_string(object)) {
+
+ /* string[n]. */
+
+ num = njs_value_to_number(property);
+ index = (int32_t) num;
+
+ if (index >= 0 && index == num) {
+ length = njs_string_prop(&string, object);
+
+ /* A single codepoint string fits in vm->retval cannot fail. */
+ (void) njs_string_slice(vm, &vm->retval, &string, length,
+ index, 1);
+
+ if (nxt_fast_path(vm->retval.data.truth != 0)) {
+ /* Non-empty string. */
+ return sizeof(njs_vmcode_prop_get_t);
+ }
+ }
+ }
+
+ } else if (ret == NXT_ERROR) {
+ return ret;
+ }
+
+done:
+
+ vm->retval = *retval;
+
+ /* GC: njs_retain(retval) */
+
+ return sizeof(njs_vmcode_prop_get_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ nxt_str_t s;
+ uintptr_t data;
+ njs_ret_t ret;
+ njs_value_t *p, *value;
+ njs_extern_t *ext;
+ njs_object_prop_t *prop;
+ njs_property_query_t pq;
+ njs_vmcode_prop_set_t *code;
+
+ code = (njs_vmcode_prop_set_t *) vm->current;
+ value = njs_vmcode_operand(vm, code->value);
+
+ pq.query = NJS_PROPERTY_QUERY_SET;
+
+ ret = njs_property_query(vm, &pq, object, property);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+
+ prop = pq.lhq.value;
+
+ } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+
+ p = pq.lhq.value;
+ *p = *value;
+
+ return sizeof(njs_vmcode_prop_set_t);
+
+ } else if (nxt_fast_path(ret == NXT_DECLINED)) {
+
+ prop = njs_object_prop_alloc(vm, &pq.value);
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ pq.lhq.replace = 0;
+ pq.lhq.value = prop;
+ pq.lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ /* Only NXT_ERROR can be returned here. */
+ return ret;
+ }
+
+ } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+
+ ext = object->data.u.external;
+
+ ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
+
+ if (ret == NXT_OK) {
+ ext = pq.lhq.value;
+ data = ext->data;
+
+ } else {
+ data = (uintptr_t) &pq.lhq.key;
+ }
+
+ ret = njs_value_to_ext_string(vm, &s, value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ /* TODO retain value if it is string. */
+
+ ret = ext->set(vm, vm->external[ext->object], data, &s);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ return sizeof(njs_vmcode_prop_set_t);
+
+ } else if (ret == NJS_PRIMITIVE_VALUE) {
+ return sizeof(njs_vmcode_prop_set_t);
+
+ } else {
+ return ret;
+ }
+
+ prop->value = *value;
+
+ return sizeof(njs_vmcode_prop_set_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property, njs_value_t *object)
+{
+ uintptr_t data;
+ njs_ret_t ret;
+ njs_value_t *value;
+ njs_extern_t *ext;
+ const njs_value_t *retval;
+ njs_property_query_t pq;
+
+ retval = &njs_value_false;
+
+ pq.query = NJS_PROPERTY_QUERY_IN;
+
+ ret = njs_property_query(vm, &pq, object, property);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ retval = &njs_value_true;
+ goto done;
+
+ } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+ value = pq.lhq.value;
+ retval = njs_is_valid(value) ? &njs_value_true : &njs_value_false;
+ goto done;
+
+ } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+
+ ext = object->data.u.external;
+
+ ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
+
+ if (ret == NXT_OK) {
+ ext = pq.lhq.value;
+
+ if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
+ retval = &njs_value_true;
+ goto done;
+ }
+
+ data = ext->data;
+
+ } else {
+ data = (uintptr_t) &pq.lhq.key;
+ }
+
+ ret = ext->find(vm, vm->external[ext->object], data, 0);
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ if (ret == NXT_OK) {
+ retval = &njs_value_true;
+ }
+
+ goto done;
+
+ } else if (nxt_fast_path(ret == NXT_DECLINED)) {
+ retval = &njs_value_false;
+ goto done;
+
+ } else if (nxt_fast_path(ret != NXT_ERROR)) {
+ vm->exception = &njs_exception_type_error;
+ }
+
+ return NXT_ERROR;
+
+done:
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property)
+{
+ uintptr_t data;
+ njs_ret_t ret;
+ njs_value_t *value;
+ njs_extern_t *ext;
+ const njs_value_t *retval;
+ njs_object_prop_t *prop;
+ njs_property_query_t pq;
+
+ retval = &njs_value_false;
+
+ pq.query = NJS_PROPERTY_QUERY_DELETE;
+
+ ret = njs_property_query(vm, &pq, object, property);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ prop = pq.lhq.value;
+
+ if (prop->configurable) {
+ pq.lhq.pool = vm->mem_cache_pool;
+
+ (void) nxt_lvlhsh_delete(&object->data.u.object->hash, &pq.lhq);
+
+ njs_release(vm, property);
+
+ retval = &njs_value_true;
+ }
+
+ } else if (nxt_fast_path(ret == NJS_ARRAY_VALUE)) {
+ value = pq.lhq.value;
+ njs_set_invalid(value);
+
+ retval = &njs_value_true;
+
+ } else if (nxt_fast_path(ret == NJS_EXTERNAL_VALUE)) {
+
+ ext = object->data.u.external;
+
+ ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
+
+ if (ret == NXT_OK) {
+ ext = pq.lhq.value;
+
+ if ((ext->type & NJS_EXTERN_OBJECT) != 0) {
+ goto done;
+ }
+
+ data = ext->data;
+
+ } else {
+ data = (uintptr_t) &pq.lhq.key;
+ }
+
+ ret = ext->find(vm, vm->external[ext->object], data, 1);
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ if (ret == NXT_OK) {
+ retval = &njs_value_true;
+ }
+
+ } else if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+done:
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+static nxt_noinline njs_ret_t
+njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
+ njs_value_t *property)
+{
+ double num;
+ int32_t index;
+ uint32_t (*hash)(const void *, size_t);
+ njs_ret_t ret;
+ njs_extern_t *ext;
+ njs_object_t *obj;
+
+ hash = nxt_djb_hash;
+
+ switch (object->type) {
+
+ case NJS_NULL:
+ case NJS_VOID:
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+
+ case NJS_STRING:
+ if (pq->query == NJS_PROPERTY_QUERY_DELETE) {
+ return NXT_DECLINED;
+ }
+
+ obj = &vm->prototypes[NJS_PROTOTYPE_STRING];
+ break;
+
+ case NJS_ARRAY:
+ if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
+
+ if (njs_is_number(property)) {
+ num = property->data.u.number;
+
+ } else {
+ num = njs_value_to_number(property);
+ }
+
+ index = (int) num;
+
+ if (nxt_fast_path(index >= 0 && (double) index == num)) {
+ return njs_array_property_query(vm, pq, object, index);
+ }
+ }
+
+ /* Fall through. */
+
+ case NJS_OBJECT:
+ case NJS_FUNCTION:
+ case NJS_REGEXP:
+ obj = object->data.u.object;
+ break;
+
+ case NJS_NATIVE:
+ obj = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ break;
+
+ case NJS_EXTERNAL:
+ ext = object->data.u.external;
+
+ if (ext->type == NJS_EXTERN_CASELESS_OBJECT) {
+ hash = nxt_djb_hash_lowcase;
+ }
+
+ obj = NULL;
+ break;
+
+ default:
+ return NJS_PRIMITIVE_VALUE;
+ }
+
+ ret = njs_value_to_string(vm, &pq->value, property);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+
+ pq->lhq.key.len = pq->value.short_string.size;
+
+ if (pq->lhq.key.len != NJS_STRING_LONG) {
+ pq->lhq.key.data = pq->value.short_string.start;
+
+ } else {
+ pq->lhq.key.len = pq->value.data.string_size;
+ pq->lhq.key.data = pq->value.data.u.string->start;
+ }
+
+ pq->lhq.key_hash = hash(pq->lhq.key.data, pq->lhq.key.len);
+
+ if (obj == NULL) {
+ pq->lhq.proto = &njs_extern_hash_proto;
+
+ return NJS_EXTERNAL_VALUE;
+ }
+
+ return njs_object_property_query(vm, pq, object, obj);
+ }
+
+ return ret;
+}
+
+
+static njs_ret_t
+njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+ njs_value_t *object, int32_t index)
+{
+ njs_ret_t ret;
+ njs_array_t *array;
+
+ array = object->data.u.array;
+
+ if ((uint32_t) index >= array->length) {
+
+ if (pq->query != NJS_PROPERTY_QUERY_SET) {
+ return NXT_DECLINED;
+ }
+
+ if ((uint32_t) index >= array->size) {
+ ret = njs_array_realloc(vm, array, 0, index);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+
+ array->length = index + 1;
+ }
+
+ pq->lhq.value = &array->start[index];
+
+ return NJS_ARRAY_VALUE;
+}
+
+
+static njs_ret_t
+njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+ njs_value_t *value, njs_object_t *object)
+{
+ njs_ret_t ret;
+ njs_object_prop_t *prop;
+
+ pq->lhq.proto = &njs_object_hash_proto;
+
+ do {
+ pq->prototype = object;
+
+ ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
+
+ if (ret == NXT_OK) {
+ prop = pq->lhq.value;
+
+ if (prop->type != NJS_WHITEOUT) {
+ pq->shared = 0;
+ return ret;
+ }
+
+ goto next;
+ }
+
+ if (pq->query > NJS_PROPERTY_QUERY_IN) {
+ /* NXT_DECLINED */
+ return ret;
+ }
+
+ ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
+
+ if (ret == NXT_OK) {
+ pq->shared = 1;
+
+ if (pq->query == NJS_PROPERTY_QUERY_IN) {
+ prop = pq->lhq.value;
+
+ if (prop->type == NJS_WHITEOUT) {
+ return NXT_DECLINED;
+ }
+ }
+
+ return ret;
+ }
+
+ if (pq->query > NJS_PROPERTY_QUERY_IN) {
+ /* NXT_DECLINED */
+ return ret;
+ }
+
+ next:
+
+ object = object->__proto__;
+
+ } while (object != NULL);
+
+ if (nxt_fast_path(njs_is_primitive(value))) {
+ return NJS_PRIMITIVE_VALUE;
+ }
+
+ return ret;
+}
+
+
+static njs_ret_t
+njs_function_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
+{
+ njs_function_t *func;
+ njs_object_prop_t *prop, *shared;
+
+ prop = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_prop_t));
+ if (nxt_slow_path(prop == NULL)) {
+ return NXT_ERROR;
+ }
+
+ shared = pq->lhq.value;
+ *prop = *shared;
+
+ func = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t));
+ if (nxt_slow_path(func == NULL)) {
+ return NXT_ERROR;
+ }
+
+ *func = *prop->value.data.u.function;
+ func->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION];
+ prop->value.data.u.function = func;
+
+ pq->lhq.replace = 0;
+ pq->lhq.value = prop;
+ pq->lhq.pool = vm->mem_cache_pool;
+
+ return nxt_lvlhsh_insert(&pq->prototype->hash, &pq->lhq);
+}
+
+
+njs_ret_t
+njs_vmcode_property_each_start(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *invld)
+{
+ njs_ret_t ret;
+ njs_extern_t *ext;
+ njs_property_each_t *pe;
+ njs_vmcode_prop_start_t *code;
+
+ if (njs_is_object(object)) {
+ pe = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_property_each_t));
+ if (nxt_slow_path(pe == NULL)) {
+ return NXT_ERROR;
+ }
+
+ vm->retval.data.u.data = pe;
+
+ memset(&pe->lhe, 0, sizeof(nxt_lvlhsh_each_t));
+ pe->lhe.proto = &njs_object_hash_proto;
+ pe->index = -1;
+
+ if (njs_is_array(object) && object->data.u.array->size != 0) {
+ pe->index = 0;
+ }
+
+ } else if (njs_is_external(object)) {
+ ext = object->data.u.external;
+
+ if (ext->each_start != NULL) {
+ ret = ext->each_start(vm, vm->external[ext->object], &vm->retval);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+ }
+ }
+
+ code = (njs_vmcode_prop_start_t *) vm->current;
+
+ return code->offset;
+}
+
+
+njs_ret_t
+njs_vmcode_property_each(njs_vm_t *vm, njs_value_t *object, njs_value_t *each)
+{
+ nxt_uint_t n;
+ njs_ret_t ret;
+ njs_array_t *array;
+ njs_extern_t *ext;
+ njs_object_prop_t *prop;
+ njs_property_each_t *pe;
+ njs_vmcode_prop_each_t *code;
+
+ code = (njs_vmcode_prop_each_t *) vm->current;
+
+ if (njs_is_object(object)) {
+ pe = each->data.u.data;
+
+ if (pe->index >= 0) {
+ array = object->data.u.array;
+
+ while ((uint32_t) pe->index < array->size) {
+ n = pe->index++;
+
+ if (njs_is_valid(&array->start[n])) {
+ njs_number_set(&vm->retval, n);
+
+ return code->offset;
+ }
+ }
+
+ pe->index = -1;
+ }
+
+ prop = nxt_lvlhsh_each(&object->data.u.object->hash, &pe->lhe);
+
+ if (prop != NULL) {
+ vm->retval = prop->name;
+
+ return code->offset;
+ }
+
+ nxt_mem_cache_free(vm->mem_cache_pool, pe);
+
+ vm->retval = njs_value_void;
+
+ } else if (njs_is_external(object)) {
+ ext = object->data.u.external;
+
+ if (ext->each != NULL) {
+ ret = ext->each(vm, &vm->retval, vm->external[ext->object], each);
+
+ if (ret == NXT_OK) {
+ return code->offset;
+ }
+
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
+
+ /* ret == NXT_DONE. */
+ }
+ }
+
+ return sizeof(njs_vmcode_prop_each_t);
+}
+
+
+njs_ret_t
+njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *constructor)
+{
+ nxt_int_t ret;
+ njs_value_t *value;
+ njs_object_t *prototype, *proto;
+ njs_object_prop_t *prop;
+ const njs_value_t *retval;
+ nxt_lvlhsh_query_t lhq;
+
+ /* TODO: test constructor is function or native: TypeError. */
+ /* TODO: test object is object: false. */
+
+ retval = &njs_value_false;
+
+ lhq.key_hash = NJS_PROTOTYPE_HASH;
+ lhq.key.len = sizeof("prototype") - 1;
+ lhq.key.data = (u_char *) "prototype";
+
+ prop = njs_object_property(vm, constructor->data.u.object, &lhq);
+
+ if (prop != NULL) {
+ value = &prop->value;
+
+ if (prop->type == NJS_NATIVE_GETTER) {
+ /* STUB: getter should be called by some njs_object_property() */
+ ret = prop->value.data.u.getter(vm, constructor);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ value = &vm->retval;
+ }
+
+ /* TODO: test prop->value is object. */
+
+ prototype = value->data.u.object;
+ proto = object->data.u.object;
+
+ do {
+ proto = proto->__proto__;
+
+ if (proto == prototype) {
+ retval = &njs_value_true;
+ break;
+ }
+
+ } while (proto != NULL);
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_instance_of_t);
+}
+
+
+njs_ret_t
+njs_vmcode_increment(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ num = value->data.u.number + 1.0;
+
+ njs_release(vm, lvalue);
+
+ njs_number_set(lvalue, num);
+ vm->retval = *lvalue;
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_LVALUE_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ num = value->data.u.number - 1.0;
+
+ njs_release(vm, lvalue);
+
+ njs_number_set(lvalue, num);
+ vm->retval = *lvalue;
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_LVALUE_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ num = value->data.u.number;
+
+ njs_release(vm, lvalue);
+
+ njs_number_set(lvalue, num + 1.0);
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_LVALUE_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *value, njs_value_t *lvalue)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ num = value->data.u.number;
+
+ njs_release(vm, lvalue);
+
+ njs_number_set(lvalue, num - 1.0);
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_LVALUE_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ /* ECMAScript 5.1: null, array and regexp are objects. */
+
+ static const njs_value_t *types[] = {
+ &njs_string_object,
+ &njs_string_void,
+ &njs_string_boolean,
+ &njs_string_number,
+ &njs_string_string,
+ &njs_string_void,
+ &njs_string_void,
+ &njs_string_void,
+
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_object,
+ &njs_string_function,
+ &njs_string_object,
+ };
+
+ vm->retval = *types[value->type];
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_void(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ vm->retval = njs_value_void;
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_release(vm, value);
+
+ njs_set_invalid(value);
+
+ vm->retval = njs_value_true;
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_unary_plus(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ njs_number_set(&vm->retval, value->data.u.number);
+ return sizeof(njs_vmcode_2addr_t);
+ }
+
+ return NJS_TRAP_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_unary_negation(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ njs_number_set(&vm->retval, - value->data.u.number);
+ return sizeof(njs_vmcode_2addr_t);
+ }
+
+ return NJS_TRAP_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_addition(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ double num;
+ njs_ret_t ret;
+ njs_param_t param;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num = val1->data.u.number + val2->data.u.number;
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ if (nxt_fast_path(njs_is_string(val1) && njs_is_string(val2))) {
+ param.object = val1;
+ param.args = val2;
+ param.retval = 0;
+ param.nargs = 1;
+
+ ret = njs_string_prototype_concat(vm, ¶m);
+
+ if (nxt_fast_path(ret >= 0)) {
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+ }
+
+ return NJS_TRAP_STRINGS;
+}
+
+
+njs_ret_t
+njs_vmcode_substraction(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num = val1->data.u.number - val2->data.u.number;
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_multiplication(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num = val1->data.u.number * val2->data.u.number;
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num = val1->data.u.number / val2->data.u.number;
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ double num;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num = fmod(val1->data.u.number, val2->data.u.number);
+ njs_number_set(&vm->retval, num);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_left_shift(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ int32_t num1;
+ uint32_t num2;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num1 = njs_integer_value(val1->data.u.number);
+ num2 = njs_integer_value(val2->data.u.number);
+ njs_number_set(&vm->retval, num1 << (num2 & 0x1f));
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_right_shift(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ int32_t num1;
+ uint32_t num2;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num1 = njs_integer_value(val1->data.u.number);
+ num2 = njs_integer_value(val2->data.u.number);
+ njs_number_set(&vm->retval, num1 >> (num2 & 0x1f));
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2)
+{
+ int32_t num2;
+ uint32_t num1;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num1 = njs_integer_value(val1->data.u.number);
+ num2 = njs_integer_value(val2->data.u.number);
+ njs_number_set(&vm->retval, num1 >> (num2 & 0x1f));
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *inlvd)
+{
+ const njs_value_t *retval;
+
+ if (njs_is_true(value)) {
+ retval = &njs_value_false;
+
+ } else {
+ retval = &njs_value_true;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_2addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_logical_and(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_value_t *retval;
+
+ if (njs_is_true(val1)) {
+ retval = val2;
+
+ } else {
+ retval = val1;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_logical_or(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_value_t *retval;
+
+ if (njs_is_true(val1)) {
+ retval = val1;
+
+ } else {
+ retval = val2;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_bitwise_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ int32_t num;
+
+ if (nxt_fast_path(njs_is_numeric(value))) {
+ num = njs_integer_value(value->data.u.number);
+ njs_number_set(&vm->retval, ~num);
+
+ return sizeof(njs_vmcode_2addr_t);
+ }
+
+ return NJS_TRAP_NUMBER;
+}
+
+
+njs_ret_t
+njs_vmcode_bitwise_and(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ int32_t num1, num2;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num1 = njs_integer_value(val1->data.u.number);
+ num2 = njs_integer_value(val2->data.u.number);
+ njs_number_set(&vm->retval, num1 & num2);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_bitwise_xor(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ int32_t num1, num2;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num1 = njs_integer_value(val1->data.u.number);
+ num2 = njs_integer_value(val2->data.u.number);
+ njs_number_set(&vm->retval, num1 ^ num2);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
+njs_vmcode_bitwise_or(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ int32_t num1, num2;
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ num1 = njs_integer_value(val1->data.u.number);
+ num2 = njs_integer_value(val2->data.u.number);
+ njs_number_set(&vm->retval, num1 | num2);
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+static nxt_noinline uint32_t
+njs_integer_value(double num)
+{
+ int64_t i64;
+
+ /*
+ * ECMAScript 5.1: integer must be modulo 2^32.
+ * 2^53 is the largest integer number which can be stored in the IEEE-754
+ * format and numbers less than 2^53 can be just converted to int64_t
+ * eliding more expensive fmod() operation. Then the int64 integer is
+ * truncated to uint32_t. The NaN can be converted to 0x8000000000000000
+ * and becomes 0 after truncation. fmod() of the infinity returns NaN.
+ */
+
+ if (num < 0 || num > 9007199254740992.0) {
+ i64 = fmod(num, 4294967296.0);
+
+ } else {
+ i64 = num;
+ }
+
+ return (uint32_t) i64;
+}
+
+
+njs_ret_t
+njs_vmcode_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_ret_t ret;
+ const njs_value_t *retval;
+
+ ret = njs_values_equal(val1, val2);
+
+ if (nxt_fast_path(ret >= 0)) {
+
+ retval = (ret != 0) ? &njs_value_true : &njs_value_false;
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+}
+
+
+njs_ret_t
+njs_vmcode_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_ret_t ret;
+ const njs_value_t *retval;
+
+ ret = njs_values_equal(val1, val2);
+
+ if (nxt_fast_path(ret >= 0)) {
+
+ retval = (ret == 0) ? &njs_value_true : &njs_value_false;
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+}
+
+
+static nxt_noinline njs_ret_t
+njs_values_equal(njs_value_t *val1, njs_value_t *val2)
+{
+ /* Void and null are equal and not comparable with anything else. */
+ if (njs_is_null_or_void(val1)) {
+ return (njs_is_null_or_void(val2));
+ }
+
+ if (njs_is_numeric(val1) && njs_is_numeric(val2)) {
+ /* NaNs and Infinities are handled correctly by comparision. */
+ return (val1->data.u.number == val2->data.u.number);
+ }
+
+ if (val1->type == val2->type) {
+
+ if (njs_is_string(val1)) {
+ return njs_string_eq(val1, val2);
+ }
+
+ return (val1->data.u.object == val2->data.u.object);
+ }
+
+ return NJS_TRAP_NUMBERS;
+}
+
+
+nxt_noinline njs_ret_t
+njs_vmcode_less(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_ret_t ret;
+ const njs_value_t *retval;
+
+ ret = njs_values_compare(val1, val2);
+
+ if (nxt_fast_path(ret >= -1)) {
+
+ retval = (ret > 0) ? &njs_value_true : &njs_value_false;
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+}
+
+
+njs_ret_t
+njs_vmcode_greater(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ return njs_vmcode_less(vm, val2, val1);
+}
+
+
+njs_ret_t
+njs_vmcode_less_or_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ return njs_vmcode_greater_or_equal(vm, val2, val1);
+}
+
+
+nxt_noinline njs_ret_t
+njs_vmcode_greater_or_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ njs_ret_t ret;
+ const njs_value_t *retval;
+
+ ret = njs_values_compare(val1, val2);
+
+ if (nxt_fast_path(ret >= -1)) {
+
+ retval = (ret == 0) ? &njs_value_true : &njs_value_false;
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+ }
+
+ return ret;
+}
+
+
+/*
+ * njs_values_compare() returns
+ * 1 if val1 is less than val2,
+ * 0 if val1 is greater than or equal to val2,
+ * -1 if the values are not comparable,
+ * or negative trap number if convertion to primitive is required.
+ */
+
+static nxt_noinline njs_ret_t
+njs_values_compare(njs_value_t *val1, njs_value_t *val2)
+{
+ if (nxt_fast_path(njs_is_numeric(val1) || njs_is_numeric(val2))) {
+
+ if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+
+ /* NaN and void values are not comparable with anything. */
+ if (njs_is_nan(val1->data.u.number)
+ || njs_is_nan(val2->data.u.number))
+ {
+ return -1;
+ }
+
+ /* Infinities are handled correctly by comparision. */
+ return (val1->data.u.number < val2->data.u.number);
+ }
+
+ return NJS_TRAP_NUMBERS;
+ }
+
+ if (nxt_fast_path(njs_is_string(val1) && njs_is_string(val2))) {
+ return (njs_string_cmp(val1, val2) < 0) ? 1 : 0;
+ }
+
+ return NJS_TRAP_STRINGS;
+}
+
+
+njs_ret_t
+njs_vmcode_strict_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ const njs_value_t *retval;
+
+ if (njs_values_strict_equal(val1, val2)) {
+ retval = &njs_value_true;
+
+ } else {
+ retval = &njs_value_false;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+njs_ret_t
+njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+ const njs_value_t *retval;
+
+ if (njs_values_strict_equal(val1, val2)) {
+ retval = &njs_value_false;
+
+ } else {
+ retval = &njs_value_true;
+ }
+
+ vm->retval = *retval;
+
+ return sizeof(njs_vmcode_3addr_t);
+}
+
+
+static nxt_noinline nxt_bool_t
+njs_values_strict_equal(njs_value_t *val1, njs_value_t *val2)
+{
+ if (val1->type != val2->type) {
+ return 0;
+ }
+
+ if (njs_is_numeric(val1)) {
+ /* NaNs and Infinities are handled correctly by comparision. */
+ return (val1->data.u.number == val2->data.u.number);
+ }
+
+ if (njs_is_string(val1)) {
+ return njs_string_eq(val1, val2);
+ }
+
+ return (val1->data.u.object == val2->data.u.object);
+}
+
+
+njs_ret_t
+njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ vm->retval = *value;
+
+ njs_retain(value);
+
+ return sizeof(njs_vmcode_move_t);
+}
+
+
+njs_ret_t
+njs_vmcode_validate(njs_vm_t *vm, njs_value_t *invld, njs_value_t *index)
+{
+ njs_value_t *value;
+
+ value = njs_vmcode_operand(vm, index);
+
+ if (nxt_fast_path(njs_is_valid(value))) {
+ return sizeof(njs_vmcode_validate_t);
+ }
+
+ vm->exception = &njs_exception_reference_error;
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
+{
+ return (njs_ret_t) offset;
+}
+
+
+njs_ret_t
+njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
+{
+ if (njs_is_true(cond)) {
+ return (njs_ret_t) offset;
+ }
+
+ return sizeof(njs_vmcode_cond_jump_t);
+}
+
+
+njs_ret_t
+njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
+{
+ if (njs_is_true(cond)) {
+ return sizeof(njs_vmcode_cond_jump_t);
+ }
+
+ return (njs_ret_t) offset;
+}
+
+
+njs_ret_t
+njs_vmcode_function(njs_vm_t *vm, njs_value_t *name, njs_value_t *invld)
+{
+ njs_ret_t ret;
+ njs_value_t value, *this;
+ njs_param_t param;
+ njs_object_t *object;
+ njs_function_t *function;
+ njs_vmcode_function_t *func;
+
+ if (nxt_fast_path(njs_is_function(name))) {
+
+ func = (njs_vmcode_function_t *) vm->current;
+
+ function = name->data.u.function;
+
+ if (function->native) {
+ this = njs_vmcode_native_frame(vm, &vm->retval, func->code.nargs,
+ func->code.ctor);
+ if (nxt_fast_path(this != NULL)) {
+ *this = njs_value_void;
+ vm->retval = *name;
+
+ return sizeof(njs_vmcode_function_t);
+ }
+
+ return NXT_ERROR;
+ }
+
+ if (func->code.ctor) {
+ object = njs_object_alloc(vm);
+
+ if (nxt_slow_path(object == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value.data.u.object = object;
+ value.type = NJS_OBJECT;
+ value.data.truth = 1;
+ param.object = &value;
+
+ } else {
+ param.object = (njs_value_t *) &njs_value_void;
+ }
+
+ param.args = NULL;
+ param.nargs = func->code.nargs;
+
+ ret = njs_vmcode_function_frame(vm, name, ¶m, func->code.ctor);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return sizeof(njs_vmcode_function_t);
+ }
+
+ } else {
+ vm->exception = &njs_exception_type_error;
+ }
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_method(njs_vm_t *vm, njs_value_t *object, njs_value_t *name)
+{
+ uintptr_t nargs;
+ njs_ret_t ret;
+ njs_value_t *this;
+ njs_param_t param;
+ njs_extern_t *ext;
+ njs_object_prop_t *prop;
+ njs_vmcode_method_t *method;
+ njs_property_query_t pq;
+
+ method = (njs_vmcode_method_t *) vm->current;
+ nargs = method->code.nargs;
+
+ pq.query = NJS_PROPERTY_QUERY_GET;
+
+ switch (njs_property_query(vm, &pq, object, name)) {
+
+ case NXT_OK:
+ prop = pq.lhq.value;
+
+ if (njs_is_function(&prop->value)
+ && !prop->value.data.u.function->native)
+ {
+ param.object = object;
+ param.args = NULL;
+ param.nargs = nargs;
+
+ ret = njs_vmcode_function_frame(vm, &prop->value, ¶m,
+ method->code.ctor);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return sizeof(njs_vmcode_method_t);
+ }
+
+ return ret;
+ }
+
+ vm->retval = prop->value;
+
+ njs_retain(object);
+
+ this = njs_vmcode_native_frame(vm, &vm->retval, nargs,
+ method->code.ctor);
+ if (nxt_slow_path(this == NULL)) {
+ return NXT_ERROR;
+ }
+
+ *this = *object;
+
+ return sizeof(njs_vmcode_method_t);
+
+ case NJS_EXTERNAL_VALUE:
+ ext = object->data.u.external;
+
+ ret = nxt_lvlhsh_find(&ext->hash, &pq.lhq);
+
+ if (ret == NXT_OK) {
+ ext = pq.lhq.value;
+
+ if (ext->type == NJS_EXTERN_METHOD) {
+ vm->retval.type = NJS_NATIVE;
+ vm->retval.data.truth = 1;
+ vm->retval.data.string_size = 0;
+ vm->retval.data.u.method = ext->method;
+
+ this = njs_vmcode_native_frame(vm, &vm->retval, nargs,
+ method->code.ctor);
+
+ if (nxt_slow_path(this == NULL)) {
+ return NXT_ERROR;
+ }
+
+ this->data.u.data = vm->external[ext->object];
+
+ return sizeof(njs_vmcode_method_t);
+ }
+ }
+
+ /* Fall through. */
+
+ default:
+ vm->exception = &njs_exception_type_error;
+ return NXT_ERROR;
+ }
+}
+
+
+njs_ret_t
+njs_vmcode_call(njs_vm_t *vm, njs_value_t *func, njs_value_t *retval)
+{
+ njs_ret_t ret;
+ njs_value_t *args;
+ njs_param_t param;
+ njs_native_t native;
+ njs_function_t *function;
+ njs_vmcode_call_t *call;
+
+ call = (njs_vmcode_call_t *) vm->current;
+ vm->current += sizeof(njs_vmcode_call_t);
+
+ if (njs_is_function(func)) {
+ function = func->data.u.function;
+
+ if (!function->native) {
+ (void) njs_function_call(vm, function, (njs_index_t) retval);
+ return 0;
+ }
+
+ native = function->code.native;
+
+ } else {
+ native = func->data.u.method;
+ }
+
+ param.retval = (njs_index_t) retval;
+ param.nargs = call->code.nargs - 1;
+ args = vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS];
+ param.args = args;
+ param.object = args - 1;
+
+ ret = native(vm, ¶m);
+ /*
+ * A native method can return:
+ * NXT_OK on method success;
+ * NJS_PASS by Function.apply() and Function.call();
+ * NXT_AGAIN to postpone nJSVM processing;
+ * NXT_ERROR.
+ *
+ * The callee arguments must be preserved for NJS_PASS and NXT_AGAIN cases.
+ */
+ if (ret == NXT_OK) {
+ /*
+ * If a retval is in a callee arguments scope it
+ * must be in the previous callee arguments scope.
+ */
+ vm->frame = vm->frame->previous;
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments;
+
+ retval = njs_vmcode_operand(vm, retval);
+ /*
+ * GC: value external/internal++ depending
+ * on vm->retval and retval type
+ */
+ *retval = vm->retval;
+
+ } else if (ret == NJS_PASS) {
+ ret = 0;
+
+ } else if (ret == NXT_AGAIN) {
+ vm->frame->reentrant = 1;
+ }
+
+ return ret;
+}
+
+
+njs_ret_t
+njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+ njs_frame_t *frame;
+ njs_value_t *args;
+ njs_native_frame_t *previous;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ frame = (njs_frame_t *) vm->frame;
+
+ if (frame->native.ctor) {
+ if (njs_is_object(value)) {
+ njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
+
+ } else {
+ value = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ }
+ }
+
+ previous = frame->native.previous;
+ vm->frame = previous;
+
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
+ vm->scopes[NJS_SCOPE_LOCAL] = frame->prev_local;
+ args = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
+
+ /*
+ * If a retval is in a callee arguments scope it
+ * must be in the previous callee arguments scope.
+ */
+ retval = njs_vmcode_operand(vm, frame->retval);
+
+ /* GC: value external/internal++ depending on value and retval type */
+ *retval = *value;
+
+ vm->current = frame->return_address;
+
+ /* GC: arguments and local. */
+
+ njs_release(vm, &args[0]);
+
+ if (frame->native.start) {
+ nxt_mem_cache_free(vm->mem_cache_pool, frame);
+ }
+
+ return 0;
+}
+
+
+njs_ret_t
+njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ vm->retval = *value;
+
+ return NXT_DONE;
+}
+
+
+/*
+ * njs_vmcode_try_start() is set on the start of a "try" block to create
+ * a "try" block, to set a catch address to the start of a "catch" or
+ * "finally" blocks and to initialize a value to track uncaught exception.
+ */
+
+njs_ret_t
+njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset)
+{
+ njs_exception_t *e;
+
+ if (vm->frame->u.exception.catch != NULL) {
+ e = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_exception_t));
+ if (nxt_slow_path(e == NULL)) {
+ return NXT_ERROR;
+ }
+
+ *e = vm->frame->u.exception;
+ vm->frame->u.exception.next = e;
+ }
+
+ vm->frame->u.exception.catch = vm->current + (njs_ret_t) offset;
+
+ njs_set_invalid(value);
+
+ return sizeof(njs_vmcode_try_start_t);
+}
+
+
+/*
+ * njs_vmcode_try_end() is set on the end of a "try" block to remove the block.
+ * It is also set on the end of a "catch" block followed by a "finally" block.
+ */
+
+nxt_noinline njs_ret_t
+njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
+{
+ njs_exception_t *e;
+
+ e = vm->frame->u.exception.next;
+
+ if (e == NULL) {
+ vm->frame->u.exception.catch = NULL;
+
+ } else {
+ vm->frame->u.exception = *e;
+ nxt_mem_cache_free(vm->mem_cache_pool, e);
+ }
+
+ return (njs_ret_t) offset;
+}
+
+
+njs_ret_t
+njs_vmcode_throw(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ vm->retval = *value;
+
+ return NXT_ERROR;
+}
+
+
+/*
+ * njs_vmcode_catch() is set on the start of a "catch" block to store
+ * exception and to remove a "try" block if there is no "finally" block
+ * or to update a catch address to the start of a "finally" block.
+ * njs_vmcode_catch() is set on the start of a "finally" block to store
+ * uncaught exception and to remove a "try" block.
+ */
+
+njs_ret_t
+njs_vmcode_catch(njs_vm_t *vm, njs_value_t *exception, njs_value_t *offset)
+{
+ *exception = vm->retval;
+
+ if ((njs_ret_t) offset == sizeof(njs_vmcode_catch_t)) {
+ return njs_vmcode_try_end(vm, exception, offset);
+ }
+
+ vm->frame->u.exception.catch = vm->current + (njs_ret_t) offset;
+
+ return sizeof(njs_vmcode_catch_t);
+}
+
+
+/*
+ * njs_vmcode_finally() is set on the end of a "finally" block to throw
+ * uncaught exception.
+ */
+
+njs_ret_t
+njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
+{
+ njs_value_t *value;
+
+ value = njs_vmcode_operand(vm, retval);
+
+ if (!njs_is_valid(value)) {
+ return sizeof(njs_vmcode_finally_t);
+ }
+
+ vm->retval = *value;
+
+ return NXT_ERROR;
+}
+
+
+njs_ret_t
+njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
+{
+ double num;
+ njs_ret_t ret;
+ njs_value_t *value;
+
+ value = njs_native_data(vm->frame);
+ value = &value[(uintptr_t) narg + 1];
+
+ ret = njs_value_to_primitive(vm, value, 0);
+
+ if (nxt_fast_path(ret > 0)) {
+
+ if (!njs_is_numeric(value)) {
+ num = NJS_NAN;
+
+ if (njs_is_string(value)) {
+ num = njs_string_to_number(value);
+ }
+
+ njs_number_set(value, num);
+ }
+
+ ret = sizeof(njs_vmcode_to_number_t);
+ }
+
+ return ret;
+}
+
+
+njs_ret_t
+njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
+{
+ njs_ret_t ret;
+ njs_value_t *value;
+ const njs_value_t *string;
+
+ value = njs_native_data(vm->frame);
+ value = &value[(uintptr_t) narg + 1];
+
+ ret = njs_value_to_primitive(vm, value, 1);
+
+ if (nxt_fast_path(ret > 0)) {
+
+ if (!njs_is_string(value)) {
+
+ switch (value->type) {
+
+ case NJS_NULL:
+ string = &njs_string_null;
+ break;
+
+ case NJS_VOID:
+ string = &njs_string_void;
+ break;
+
+ case NJS_BOOLEAN:
+ string = njs_is_true(value) ? &njs_string_true:
+ &njs_string_false;
+ break;
+
+ case NJS_NUMBER:
+ ret = njs_number_to_string(vm, value, value);
+ if (nxt_fast_path(ret == NXT_OK)) {
+ goto done;
+ }
+
+ default:
+ return NXT_ERROR;
+ }
+
+ *value = *string;
+ }
+
+ done:
+
+ ret = sizeof(njs_vmcode_to_string_t);
+ }
+
+ return ret;
+}
+
+
+/*
+ * A hint value is 0 for numbers and 1 for strings. The value chooses method
+ * calls order specified by ECMAScript 5.1: "valueOf", "toString" for numbers
+ * and "toString", "valueOf" for strings.
+ */
+
+nxt_noinline njs_ret_t
+njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
+{
+ njs_ret_t ret;
+ njs_param_t param;
+ njs_value_t *retval;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ static const uint32_t hashes[] = {
+ NJS_VALUE_OF_HASH,
+ NJS_TO_STRING_HASH,
+ };
+
+ static const nxt_str_t names[] = {
+ nxt_string("valueOf"),
+ nxt_string("toString"),
+ };
+
+ if (!njs_is_primitive(value)) {
+ retval = njs_native_data(vm->frame);
+
+ if (!njs_is_valid(retval)) {
+
+ for ( ;; ) {
+ vm->exception = &njs_exception_type_error;
+ ret = NXT_ERROR;
+
+ if (njs_is_object(value) && vm->frame->reentrant < 2) {
+ hint ^= vm->frame->reentrant++;
+
+ lhq.key_hash = hashes[hint];
+ lhq.key = names[hint];
+
+ prop = njs_object_property(vm, value->data.u.object, &lhq);
+
+ if (nxt_fast_path(prop != NULL)) {
+ param.object = value;
+ param.retval = (njs_index_t) retval;
+ param.args = NULL;
+ param.nargs = 0;
+
+ ret = njs_function_apply(vm, &prop->value, ¶m);
+
+ /*
+ * njs_function_apply() can return
+ * NXT_OK, NJS_PASS, NXT_ERROR, NXT_AGAIN.
+ */
+ if (nxt_fast_path(ret == NXT_OK)) {
+
+ if (njs_is_primitive(&vm->retval)) {
+ retval = &vm->retval;
+ break;
+ }
+
+ continue;
+ }
+
+ if (ret == NJS_PASS) {
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ *value = *retval;
+
+ njs_set_invalid(retval);
+ }
+
+ vm->frame->reentrant = 0;
+
+ return 1;
+}
+
+
+njs_ret_t
+njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
+{
+ u_char *restart;
+ njs_ret_t ret;
+ njs_value_t *retval, *value1, *value2;
+ njs_native_frame_t *frame;
+ njs_vmcode_generic_t *vmcode;
+
+ frame = vm->frame;
+ vm->frame = frame->previous;
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = frame->previous->arguments;
+
+ restart = frame->u.restart;
+ vm->current = restart;
+ vmcode = (njs_vmcode_generic_t *) restart;
+
+ value1 = njs_native_data(frame);
+ value1 = &value1[1];
+
+ if (frame->lvalue) {
+ value2 = value1[1].data.u.value;
+
+ } else {
+ value2 = &value1[1];
+ }
+
+ ret = vmcode->code.operation(vm, value1, value2);
+
+ retval = njs_vmcode_operand(vm, vmcode->operand1);
+
+ //njs_release(vm, retval);
+
+ *retval = vm->retval;
+
+ if (frame->start) {
+ nxt_mem_cache_free(vm->mem_cache_pool, frame);
+ }
+
+ return ret;
+}
+
+
+nxt_noinline void
+njs_number_set(njs_value_t *value, double num)
+{
+ value->data.u.number = num;
+ value->type = NJS_NUMBER;
+ value->data.truth = njs_is_number_true(num);
+}
+
+
+njs_ret_t
+njs_void_set(njs_value_t *value)
+{
+ *value = njs_value_void;
+ return NXT_OK;
+}
+
+
+void *
+njs_value_data(njs_value_t *value)
+{
+ return value->data.u.data;
+}
+
+
+nxt_uint_t
+njs_vm_is_reentrant(njs_vm_t *vm)
+{
+ return vm->frame->reentrant;
+}
+
+
+nxt_int_t
+njs_value_string(njs_vm_t *vm, nxt_str_t *retval, njs_value_t *value,
+ njs_value_t **tmp)
+{
+ size_t size;
+ nxt_int_t ret;
+ njs_value_t *val;
+
+ val = value;
+ *tmp = NULL;
+
+ if (!njs_is_string(val)) {
+ val = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t));
+ if (nxt_slow_path(val == NULL)) {
+ return NXT_ERROR;
+ }
+
+ ret = njs_value_to_string(vm, val, value);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ /* TODO: njs_free(vm, val); */
+ return ret;
+ }
+
+ *tmp = val;
+ }
+
+ size = val->short_string.size;
+
+ if (size != NJS_STRING_LONG) {
+ retval->len = size;
+ retval->data = val->short_string.start;
+
+ } else {
+ njs_retain(val);
+ retval->len = val->data.string_size;
+ retval->data = val->data.u.string->start;
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+njs_value_string_copy(njs_vm_t *vm, nxt_str_t *retval, njs_value_t *value,
+ uintptr_t *next)
+{
+ uintptr_t n;
+ njs_array_t *array;
+
+ switch (value->type) {
+
+ case NJS_STRING:
+ if (*next != 0) {
+ return NXT_DECLINED;
+ }
+
+ *next = 1;
+ break;
+
+ case NJS_ARRAY:
+ array = value->data.u.array;
+
+ do {
+ n = (*next)++;
+
+ if (n == array->length) {
+ return NXT_DECLINED;
+ }
+
+ value = &array->start[n];
+
+ } while (!njs_is_valid(value));
+
+ break;
+
+ default:
+ return NXT_ERROR;
+ }
+
+ return njs_value_to_ext_string(vm, retval, value);
+}
+
+
+void
+njs_debug(njs_index_t index, njs_value_t *value)
+{
+#if (NXT_DEBUG)
+ u_char *p;
+ uint32_t len;
+
+ switch (value->type) {
+
+ case NJS_NULL:
+ nxt_thread_log_debug("%p [null]", index);
+ return;
+
+ case NJS_VOID:
+ nxt_thread_log_debug("%p [void]", index);
+ return;
+
+ case NJS_BOOLEAN:
+ nxt_thread_log_debug("%p [%s]", index,
+ (value->data.u.number == 0.0) ? "false" : "true");
+ return;
+
+ case NJS_NUMBER:
+ nxt_thread_log_debug("%p [%f]", index, value->data.u.number);
+ return;
+
+ case NJS_STRING:
+ len = value->short_string.size;
+ if (len != NJS_STRING_LONG) {
+ p = value->short_string.start;
+
+ } else {
+ len = value->data.string_size;
+ p = value->data.u.string->start;
+ }
+
+ nxt_thread_log_debug("%p [\"%*s\"]", index, len, p);
+ return;
+
+ case NJS_ARRAY:
+ nxt_thread_log_debug("%p [array]", index);
+ return;
+
+ default:
+ nxt_thread_log_debug("%p [invalid]", index);
+ return;
+ }
+#endif
+}
+
+
+void *
+njs_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc)
+{
+ return nxt_mem_cache_align(data, size, size);
+}
+
+
+void
+njs_lvlhsh_free(void *data, void *p, size_t size)
+{
+ nxt_mem_cache_free(data, p);
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_VM_H_INCLUDED_
+#define _NJS_VM_H_INCLUDED_
+
+
+#define NJS_TRAP_STRINGS -10
+#define NJS_TRAP_NUMBERS -11
+#define NJS_TRAP_LVALUE_NUMBER -12
+#define NJS_TRAP_NUMBER -13
+
+#define NJS_PASS -20
+
+
+/* The order of the enum is used in njs_vmcode_typeof() */
+
+typedef enum {
+ NJS_NULL = 0x00,
+ NJS_VOID = 0x01,
+
+ /* The order of the above type is used in njs_is_null_or_void(). */
+
+ NJS_BOOLEAN = 0x02,
+ /*
+ * The order of the above type is used in njs_is_null_or_void_or_boolean().
+ */
+ NJS_NUMBER = 0x03,
+ /*
+ * The order of the above type is used in njs_is_numeric().
+ * Booleans, null and void values can be used in mathematical operations:
+ * a numeric value of the true value is one,
+ * a numeric value of the null and false values is zero,
+ * a numeric value of the void value is NaN.
+ */
+ NJS_STRING = 0x04,
+
+ /* The order of the above type is used in njs_is_primitive(). */
+
+ /* The type is native code. */
+ NJS_NATIVE = 0x05,
+
+ /* The type is external code. */
+ NJS_EXTERNAL = 0x06,
+
+ /*
+ * A special value type for uninitialized array members.
+ * It is also used to detect variable non-declared explicitly
+ * or implicitly and to throw ReferenceError exception.
+ */
+ NJS_INVALID = 0x07,
+
+ /*
+ * The object types have the third bit set. It is used in njs_is_object().
+ */
+ NJS_OBJECT = 0x08,
+ NJS_ARRAY = 0x09,
+ NJS_OBJECT_BOOLEAN = 0x0a,
+ NJS_OBJECT_NUMBER = 0x0b,
+ NJS_OBJECT_STRING = 0x0c,
+ NJS_FUNCTION = 0x0d,
+ NJS_REGEXP = 0x0e,
+} njs_value_type_t;
+
+
+typedef struct njs_parser_s njs_parser_t;
+
+typedef njs_ret_t (*njs_getter_t) (njs_vm_t *vm, njs_value_t *obj);
+typedef njs_ret_t (*njs_native_t) (njs_vm_t *vm, njs_param_t *param);
+
+
+typedef struct njs_string_s njs_string_t;
+typedef struct njs_object_s njs_object_t;
+typedef struct njs_array_s njs_array_t;
+typedef struct njs_object_value_s njs_object_value_t;
+typedef struct njs_function_s njs_function_t;
+typedef struct njs_regexp_s njs_regexp_t;
+typedef struct njs_regexp_pattern_s njs_regexp_pattern_t;
+typedef struct njs_extern_s njs_extern_t;
+
+
+union njs_value_s {
+ /*
+ * The njs_value_t size is 16 bytes and must be aligned to 16 bytes
+ * to provide 4 bits to encode scope in njs_index_t. This space is
+ * used to store short strings. The maximum size of a short string
+ * is 14 (NJS_STRING_SHORT). If the short_string.size field is 15
+ * (NJS_STRING_LONG) then the size is in the data.string_size field
+ * and the data.u.string field points to a long string.
+ *
+ * The number of the string types is limited to 2 types to minimize
+ * overhead of processing string fields. It also is possible to add
+ * strings with size from 14 to 254 which size and length are stored in
+ * the string_size and string_length byte wide fields. This will lessen
+ * the maximum size of short string to 13.
+ */
+ struct {
+ njs_value_type_t type:8; /* 4 bits */
+ /*
+ * The truth field is set during value assignment and then can be
+ * quickly tested by logical and conditional operations regardless
+ * of value type. The truth field coincides with short_string.size
+ * and short_string.length so when string size and length are zero
+ * the string's value is false.
+ */
+ uint8_t truth;
+
+ /* 0xff if u.data.string is external string. */
+ uint8_t external0;
+ uint8_t _spare;
+ /*
+ * A long string size. It is better to store here a size instead of
+ * an UTF-8 length because the size is known at creation time but the
+ * length may be set later and it should be updated in one shared place.
+ */
+ uint32_t string_size;
+
+ union {
+ double number;
+ njs_string_t *string;
+ njs_object_t *object;
+ njs_array_t *array;
+ njs_object_value_t *object_value;
+ njs_function_t *function;
+ njs_regexp_t *regexp;
+ njs_getter_t getter;
+ njs_native_t method;
+ njs_extern_t *external;
+ njs_value_t *value;
+ void *data;
+ } u;
+ } data;
+
+ struct {
+ njs_value_type_t type:8; /* 4 bits */
+
+#define NJS_STRING_SHORT 14
+#define NJS_STRING_LONG 15
+
+ uint8_t size:4;
+ uint8_t length:4;
+
+ u_char start[NJS_STRING_SHORT];
+ } short_string;
+
+ njs_value_type_t type:8; /* 4 bits */
+};
+
+
+#define \
+njs_value(_type, _truth, _number) { \
+ .data = { \
+ .type = _type, \
+ .truth = _truth, \
+ .u.number = _number, \
+ } \
+}
+
+
+#define \
+njs_string(s) { \
+ .short_string = { \
+ .type = NJS_STRING, \
+ .size = sizeof(s) - 1, \
+ .length = sizeof(s) - 1, \
+ .start = s, \
+ } \
+}
+
+
+/* NJS_STRING_LONG is set for both big and little endian platforms. */
+
+#define \
+njs_long_string(s) { \
+ .data = { \
+ .type = NJS_STRING, \
+ .truth = (NJS_STRING_LONG << 4) | NJS_STRING_LONG, \
+ .string_size = sizeof(s) - 1, \
+ .u.string = & (njs_string_t) { \
+ .start = (u_char *) s, \
+ .length = sizeof(s) - 1, \
+ } \
+ } \
+}
+
+
+#define \
+njs_native_function(_function, _local_size) { \
+ .data = { \
+ .type = NJS_FUNCTION, \
+ .truth = 1, \
+ .string_size = _local_size, \
+ .u.function = & (njs_function_t) { \
+ .native = 1, \
+ .args_offset = 1, \
+ .code.native = _function, \
+ } \
+ } \
+}
+
+
+#define \
+njs_getter(_getter) \
+ { .data = { .type = NJS_NATIVE, \
+ .truth = 1, \
+ .u = { .getter = _getter } \
+ } }
+
+
+#define \
+njs_method(_method, _size) \
+ { .data = { .type = NJS_NATIVE, \
+ .truth = 1, \
+ .string_size = _size, \
+ .u = { .method = _method } \
+ } }
+
+
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_array.h>
+#include <njs_function.h>
+#include <njs_extern.h>
+
+
+typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
+ njs_value_t *value2);
+
+
+#define \
+njs_is_null(value) \
+ ((value)->type == NJS_NULL)
+
+
+#define \
+njs_is_void(value) \
+ ((value)->type == NJS_VOID)
+
+
+#define \
+njs_is_null_or_void(value) \
+ ((value)->type <= NJS_VOID)
+
+
+#define \
+njs_is_boolean(value) \
+ ((value)->type == NJS_BOOLEAN)
+
+
+#define \
+njs_is_null_or_void_or_boolean(value) \
+ ((value)->type <= NJS_BOOLEAN)
+
+
+#define \
+njs_is_true(value) \
+ ((value)->data.truth != 0)
+
+
+#define \
+njs_is_number(value) \
+ ((value)->type == NJS_NUMBER)
+
+
+/* Testing for NaN first generates a better code at least on i386/amd64. */
+
+#define \
+njs_is_number_true(num) \
+ (!njs_is_nan(num) && num != 0)
+
+
+#define \
+njs_is_numeric(value) \
+ ((value)->type <= NJS_NUMBER)
+
+
+#define \
+njs_is_string(value) \
+ ((value)->type == NJS_STRING)
+
+
+/*
+ * The truth field coincides with short_string.size and short_string.length
+ * so when string size and length are zero the string's value is false and
+ * otherwise is true.
+ */
+#define \
+njs_string_truth(value, size)
+
+
+#define \
+njs_is_primitive(value) \
+ ((value)->type <= NJS_STRING)
+
+
+#define \
+njs_is_object(value) \
+ (((value)->type & NJS_OBJECT) != 0)
+
+
+#define \
+njs_is_array(value) \
+ ((value)->type == NJS_ARRAY)
+
+
+#define \
+njs_is_function(value) \
+ ((value)->type == NJS_FUNCTION)
+
+
+#define \
+njs_is_native(value) \
+ ((value)->type == NJS_NATIVE)
+
+
+#define \
+njs_is_external(value) \
+ ((value)->type == NJS_EXTERNAL)
+
+
+#define \
+njs_is_valid(value) \
+ ((value)->type != NJS_INVALID)
+
+
+#define \
+njs_set_invalid(value) \
+ (value)->type = NJS_INVALID
+
+
+#define \
+njs_retain(value) \
+ do { \
+ if ((value)->data.truth == NJS_STRING_LONG) { \
+ njs_value_retain(value); \
+ } \
+ } while (0)
+
+
+#define \
+njs_release(vm, value) \
+ do { \
+ if ((value)->data.truth == NJS_STRING_LONG) { \
+ njs_value_release((vm), (value)); \
+ } \
+ } while (0)
+
+
+#define NJS_VMCODE_3OPERANDS 0
+#define NJS_VMCODE_2OPERANDS 1
+#define NJS_VMCODE_1OPERAND 2
+#define NJS_VMCODE_NO_OPERAND 3
+
+#define NJS_VMCODE_NO_RETVAL 0
+#define NJS_VMCODE_RETVAL 1
+
+
+typedef struct {
+ njs_vmcode_operation_t operation;
+ uint8_t operands; /* 2 bits */
+ uint8_t retval; /* 1 bit */
+ uint8_t ctor; /* 1 bit */
+#if (NXT_64BIT)
+ uint32_t nargs;
+#else
+ uint16_t nargs;
+#endif
+} njs_vmcode_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t operand1;
+ njs_index_t operand2;
+ njs_index_t operand3;
+} njs_vmcode_generic_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t index;
+} njs_vmcode_1addr_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+ njs_index_t src;
+} njs_vmcode_2addr_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+ njs_index_t src1;
+ njs_index_t src2;
+} njs_vmcode_3addr_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_stop_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t index;
+} njs_vmcode_validate_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t dst;
+ njs_index_t src;
+} njs_vmcode_move_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_object_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ uintptr_t length;
+} njs_vmcode_array_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_function_script_t *function;
+} njs_vmcode_function_create_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_regexp_pattern_t *pattern;
+} njs_vmcode_regexp_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+} njs_vmcode_jump_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t cond;
+} njs_vmcode_cond_jump_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t value;
+ njs_index_t object;
+ njs_index_t property;
+} njs_vmcode_prop_get_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t value;
+ njs_index_t object;
+ njs_index_t property;
+} njs_vmcode_prop_set_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t each;
+ njs_index_t object;
+ njs_ret_t offset;
+} njs_vmcode_prop_start_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t object;
+ njs_index_t each;
+ njs_ret_t offset;
+} njs_vmcode_prop_each_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t value;
+ njs_index_t constructor;
+ njs_index_t object;
+} njs_vmcode_instance_of_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t function;
+ njs_index_t name;
+} njs_vmcode_function_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t function;
+ njs_index_t object;
+ njs_index_t method;
+} njs_vmcode_method_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+ njs_index_t function;
+} njs_vmcode_call_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t value;
+} njs_vmcode_try_start_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+ njs_index_t exception;
+} njs_vmcode_catch_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_throw_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
+} njs_vmcode_try_end_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_finally_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ uintptr_t narg;
+} njs_vmcode_to_string_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ uintptr_t narg;
+} njs_vmcode_to_number_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ uintptr_t unused;
+} njs_vmcode_restart_t;
+
+
+typedef enum {
+ NJS_SCOPE_ABSOLUTE = 0,
+ NJS_SCOPE_LOCAL,
+ NJS_SCOPE_GLOBAL,
+ NJS_SCOPE_CALLEE_ARGUMENTS,
+ NJS_SCOPE_ARGUMENTS,
+ NJS_SCOPE_CLOSURE,
+ NJS_SCOPE_PARENT_LOCAL,
+ NJS_SCOPE_PARENT_ARGUMENTS,
+ NJS_SCOPE_PARENT_CLOSURE,
+} njs_scope_t;
+
+#define NJS_SCOPES (NJS_SCOPE_PARENT_CLOSURE + 1)
+
+#define NJS_SCOPE_SHIFT 4
+#define NJS_SCOPE_MASK ((uintptr_t) ((1 << NJS_SCOPE_SHIFT) - 1))
+
+#define NJS_INDEX_CACHE NJS_SCOPE_LOCAL
+
+#define NJS_INDEX_NONE ((njs_index_t) 0)
+#define NJS_INDEX_ERROR ((njs_index_t) -1)
+#define NJS_INDEX_THIS ((njs_index_t) (0 | NJS_SCOPE_ARGUMENTS))
+
+
+enum njs_prototypes_e {
+ NJS_PROTOTYPE_OBJECT = 0,
+ NJS_PROTOTYPE_ARRAY,
+ NJS_PROTOTYPE_BOOLEAN,
+ NJS_PROTOTYPE_NUMBER,
+ NJS_PROTOTYPE_STRING,
+ NJS_PROTOTYPE_FUNCTION,
+ NJS_PROTOTYPE_REGEXP,
+#define NJS_PROTOTYPE_MAX (NJS_PROTOTYPE_REGEXP + 1)
+};
+
+
+enum njs_functions_e {
+ NJS_FUNCTION_OBJECT = NJS_PROTOTYPE_OBJECT,
+ NJS_FUNCTION_ARRAY = NJS_PROTOTYPE_ARRAY,
+ NJS_FUNCTION_BOOLEAN = NJS_PROTOTYPE_BOOLEAN,
+ NJS_FUNCTION_NUMBER = NJS_PROTOTYPE_NUMBER,
+ NJS_FUNCTION_STRING = NJS_PROTOTYPE_STRING,
+ NJS_FUNCTION_FUNCTION = NJS_PROTOTYPE_FUNCTION,
+ NJS_FUNCTION_REGEXP = NJS_PROTOTYPE_REGEXP,
+
+ NJS_FUNCTION_EVAL,
+#define NJS_FUNCTION_MAX (NJS_FUNCTION_EVAL + 1)
+};
+
+
+#define \
+njs_scope_index(value) \
+ ((njs_index_t) (value << NJS_SCOPE_SHIFT))
+
+#define \
+njs_global_scope_index(value) \
+ ((njs_index_t) ((value << NJS_SCOPE_SHIFT) | NJS_SCOPE_GLOBAL))
+
+
+#define NJS_INDEX_OBJECT njs_global_scope_index(NJS_FUNCTION_OBJECT)
+#define NJS_INDEX_ARRAY njs_global_scope_index(NJS_FUNCTION_ARRAY)
+#define NJS_INDEX_BOOLEAN njs_global_scope_index(NJS_FUNCTION_BOOLEAN)
+#define NJS_INDEX_NUMBER njs_global_scope_index(NJS_FUNCTION_NUMBER)
+#define NJS_INDEX_STRING njs_global_scope_index(NJS_FUNCTION_STRING)
+#define NJS_INDEX_FUNCTION njs_global_scope_index(NJS_FUNCTION_FUNCTION)
+#define NJS_INDEX_REGEXP njs_global_scope_index(NJS_FUNCTION_REGEXP)
+#define NJS_INDEX_EVAL njs_global_scope_index(NJS_FUNCTION_EVAL)
+
+#define NJS_INDEX_GLOBAL_OFFSET njs_scope_index(NJS_FUNCTION_MAX)
+
+
+#define \
+njs_offset(index) \
+ ((uintptr_t) (index) & ~NJS_SCOPE_MASK)
+
+
+#define \
+njs_vmcode_operand(vm, index) \
+ ((njs_value_t *) \
+ ((u_char *) vm->scopes[(uintptr_t) (index) & NJS_SCOPE_MASK] \
+ + njs_offset(index)))
+
+
+#define \
+njs_index_size(index) \
+ njs_offset(index)
+
+
+struct njs_vm_s {
+ /* njs_vm_t must be aligned to njs_value_t due to scratch value. */
+ njs_value_t retval;
+
+ /*
+ * The scratch value is used for lvalue operations on nonexistent
+ * properties of non-object values: "a = 1; a.b++".
+ */
+ njs_value_t scratch;
+
+ u_char *current;
+
+ njs_value_t *scopes[NJS_SCOPES];
+
+ void **external;
+
+ njs_native_frame_t *frame;
+
+ const njs_value_t *exception;
+
+ nxt_lvlhsh_t externals_hash;
+ nxt_lvlhsh_t variables_hash;
+ nxt_lvlhsh_t functions_hash;
+ nxt_lvlhsh_t values_hash;
+
+ njs_object_t *prototypes;
+ njs_function_t *functions;
+
+ u_char *number_trap;
+ u_char *string_trap;
+
+ nxt_mem_cache_pool_t *mem_cache_pool;
+
+ njs_value_t *global_scope;
+ size_t scope_size;
+
+ njs_vm_shared_t *shared;
+ njs_parser_t *parser;
+};
+
+
+struct njs_vm_shared_s {
+ nxt_lvlhsh_t keywords_hash;
+ nxt_lvlhsh_t values_hash;
+
+ njs_object_t *prototypes;
+ njs_function_t *functions;
+
+ u_char *number_trap;
+ u_char *string_trap;
+};
+
+
+nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm);
+
+void njs_disassembler(u_char *start, u_char *end, nxt_str_t *text);
+
+void njs_value_retain(njs_value_t *value);
+void njs_value_release(njs_vm_t *vm, njs_value_t *value);
+
+njs_ret_t njs_vmcode_object_create(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *inlvd2);
+njs_ret_t njs_vmcode_array_create(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *inlvd2);
+njs_ret_t njs_vmcode_function_create(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
+njs_ret_t njs_vmcode_regexp_create(njs_vm_t *vm, njs_value_t *inlvd1,
+ njs_value_t *invld2);
+
+njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_set(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *property,
+ njs_value_t *object);
+njs_ret_t njs_vmcode_property_delete(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *property);
+njs_ret_t njs_vmcode_property_each_start(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_property_each(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *each);
+njs_ret_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *constructor);
+
+njs_ret_t njs_vmcode_increment(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *lvalue);
+njs_ret_t njs_vmcode_decrement(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *lvalue);
+njs_ret_t njs_vmcode_post_increment(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *lvalue);
+njs_ret_t njs_vmcode_post_decrement(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *lvalue);
+njs_ret_t njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_void(njs_vm_t *vm, njs_value_t *invld1,
+ njs_value_t *invld2);
+njs_ret_t njs_vmcode_delete(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_unary_plus(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_unary_negation(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_addition(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_substraction(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_multiplication(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *inlvd);
+njs_ret_t njs_vmcode_logical_and(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_logical_or(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_bitwise_not(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *inlvd);
+njs_ret_t njs_vmcode_bitwise_and(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_bitwise_xor(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_bitwise_or(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_left_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_right_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_unsigned_right_shift(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2);
+njs_ret_t njs_vmcode_not_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_less(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2);
+njs_ret_t njs_vmcode_greater(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_less_or_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_greater_or_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_strict_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+njs_ret_t njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1,
+ njs_value_t *val2);
+
+njs_ret_t njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld);
+njs_ret_t njs_vmcode_validate(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *index);
+
+njs_ret_t njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond,
+ njs_value_t *offset);
+
+njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *name,
+ njs_value_t *invld);
+njs_ret_t njs_vmcode_method(njs_vm_t *vm, njs_value_t *object,
+ njs_value_t *method);
+njs_ret_t njs_vmcode_call(njs_vm_t *vm, njs_value_t *func, njs_value_t *retval);
+njs_ret_t njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+njs_ret_t njs_vmcode_stop(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+
+njs_ret_t njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *offset);
+njs_ret_t njs_vmcode_throw(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+njs_ret_t njs_vmcode_catch(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *exception);
+njs_ret_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *retval);
+
+njs_ret_t njs_vmcode_to_number(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *narg);
+njs_ret_t njs_vmcode_to_string(njs_vm_t *vm, njs_value_t *invld,
+ njs_value_t *narg);
+njs_ret_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *value,
+ nxt_uint_t hint);
+njs_ret_t njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1,
+ njs_value_t *invld2);
+
+nxt_noinline void njs_number_set(njs_value_t *value, double num);
+
+nxt_int_t njs_shared_objects_create(njs_vm_t *vm);
+nxt_int_t njs_shared_objects_clone(njs_vm_t *vm);
+
+
+/* STUB */
+u_char *njs_number_trap_create(njs_vm_t *vm);
+u_char *njs_string_trap_create(njs_vm_t *vm);
+
+
+void *njs_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc);
+void njs_lvlhsh_free(void *data, void *p, size_t size);
+
+
+extern const njs_value_t njs_value_void;
+extern const njs_value_t njs_value_null;
+extern const njs_value_t njs_value_false;
+extern const njs_value_t njs_value_true;
+extern const njs_value_t njs_value_zero;
+extern const njs_value_t njs_value_nan;
+
+extern const njs_value_t njs_string_empty;
+extern const njs_value_t njs_string_comma;
+extern const njs_value_t njs_string_void;
+extern const njs_value_t njs_string_null;
+extern const njs_value_t njs_string_false;
+extern const njs_value_t njs_string_true;
+extern const njs_value_t njs_string_native;
+extern const njs_value_t njs_string_minus_infinity;
+extern const njs_value_t njs_string_plus_infinity;
+extern const njs_value_t njs_string_nan;
+extern const njs_value_t njs_string_prototype;
+extern const njs_value_t njs_string_constructor;
+
+extern const njs_value_t njs_string_object_null;
+extern const njs_value_t njs_string_object_undefined;
+extern const njs_value_t njs_string_object_boolean;
+extern const njs_value_t njs_string_object_number;
+extern const njs_value_t njs_string_object_string;
+extern const njs_value_t njs_string_object_object;
+extern const njs_value_t njs_string_object_array;
+extern const njs_value_t njs_string_object_function;
+extern const njs_value_t njs_string_object_regexp;
+
+extern const njs_value_t njs_exception_syntax_error;
+extern const njs_value_t njs_exception_reference_error;
+extern const njs_value_t njs_exception_type_error;
+extern const njs_value_t njs_exception_range_error;
+extern const njs_value_t njs_exception_memory_error;
+
+extern const nxt_mem_proto_t njs_array_mem_proto;
+extern const nxt_lvlhsh_proto_t njs_object_hash_proto;
+
+
+#endif /* _NJS_VM_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_malloc.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_array.h>
+#include <njs_function.h>
+#include <njs_regexp.h>
+#include <njs_extern.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
+#include <string.h>
+
+
+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) {
+ memset(p, 0, 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);
+}
+
+
+static const nxt_mem_proto_t njs_vm_mem_cache_pool_proto = {
+ njs_alloc,
+ njs_zalloc,
+ njs_align,
+ NULL,
+ njs_free,
+ NULL,
+ NULL,
+};
+
+
+static void *
+njs_array_mem_alloc(void *mem, size_t size)
+{
+ return nxt_mem_cache_alloc(mem, size);
+}
+
+
+static void
+njs_array_mem_free(void *mem, void *p)
+{
+ nxt_mem_cache_free(mem, p);
+}
+
+
+const nxt_mem_proto_t njs_array_mem_proto = {
+ njs_array_mem_alloc,
+ NULL,
+ NULL,
+ NULL,
+ njs_array_mem_free,
+ NULL,
+ NULL,
+};
+
+
+njs_vm_t *
+njs_vm_create(nxt_mem_cache_pool_t *mcp, njs_vm_shared_t **shared,
+ nxt_lvlhsh_t *externals)
+{
+ njs_vm_t *vm;
+
+ if (mcp == NULL) {
+ mcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL,
+ NULL, 2 * nxt_pagesize(), 128, 512, 16);
+ if (nxt_slow_path(mcp == NULL)) {
+ return NULL;
+ }
+ }
+
+ vm = nxt_mem_cache_zalign(mcp, sizeof(njs_value_t), sizeof(njs_vm_t));
+
+ if (nxt_fast_path(vm != NULL)) {
+ vm->mem_cache_pool = mcp;
+
+ if (shared != NULL) {
+
+ if (*shared == NULL) {
+ *shared = nxt_mem_cache_zalloc(mcp, sizeof(njs_vm_shared_t));
+ if (nxt_slow_path(*shared == NULL)) {
+ return NULL;
+ }
+ }
+
+ vm->shared = *shared;
+ }
+
+ if (externals != NULL) {
+ vm->externals_hash = *externals;
+ }
+ }
+
+ return vm;
+}
+
+
+void
+njs_vm_destroy(njs_vm_t *vm)
+{
+ nxt_mem_cache_pool_destroy(vm->mem_cache_pool);
+}
+
+
+nxt_int_t
+njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
+{
+ nxt_int_t ret;
+ nxt_lvlhsh_t keywords_hash;
+ njs_lexer_t *lexer;
+ njs_parser_t *parser;
+ njs_parser_node_t *node;
+
+ parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t));
+ if (nxt_slow_path(parser == NULL)) {
+ return NJS_ERROR;
+ }
+
+ vm->parser = parser;
+
+ lexer = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_lexer_t));
+ if (nxt_slow_path(lexer == NULL)) {
+ return NJS_ERROR;
+ }
+
+ parser->lexer = lexer;
+
+ if (vm->shared != NULL) {
+ keywords_hash = vm->shared->keywords_hash;
+ parser->values_hash = vm->shared->values_hash;
+
+ vm->number_trap = vm->shared->number_trap;
+ vm->string_trap = vm->shared->string_trap;
+
+ /* STUB */
+ if (vm->shared->prototypes == NULL) {
+ ret = njs_shared_objects_create(vm);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
+ } else {
+ nxt_lvlhsh_init(&keywords_hash);
+ }
+
+ if (nxt_lvlhsh_is_empty(&keywords_hash)) {
+
+ ret = njs_lexer_keywords_init(vm->mem_cache_pool, &keywords_hash);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_ERROR;
+ }
+
+ if (vm->shared != NULL) {
+ vm->shared->keywords_hash = keywords_hash;
+ }
+ }
+
+ if (vm->number_trap == NULL) {
+
+ vm->number_trap = njs_number_trap_create(vm);
+ if (nxt_slow_path(vm->number_trap == NULL)) {
+ return NJS_ERROR;
+ }
+
+ vm->string_trap = njs_string_trap_create(vm);
+ if (nxt_slow_path(vm->string_trap == NULL)) {
+ return NJS_ERROR;
+ }
+
+ if (vm->shared != NULL) {
+ vm->shared->number_trap = vm->number_trap;
+ vm->shared->string_trap = vm->string_trap;
+ }
+ }
+
+ parser->lexer->keywords_hash = keywords_hash;
+
+ parser->lexer->start = *start;
+ parser->lexer->end = end;
+
+ parser->code_size = sizeof(njs_vmcode_stop_t);
+ parser->scope = NJS_SCOPE_GLOBAL;
+ parser->scope_offset = NJS_INDEX_GLOBAL_OFFSET;
+ parser->index[NJS_SCOPE_GLOBAL - NJS_INDEX_CACHE] = NJS_INDEX_GLOBAL_OFFSET;
+
+ parser->scope_values = nxt_vector_create(4, sizeof(njs_value_t),
+ &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(parser->scope_values == NULL)) {
+ return NJS_ERROR;
+ }
+
+ /* Empty vector to minimize tests in njs_parser_variable(). */
+ parser->arguments = nxt_vector_create(0, sizeof(njs_variable_t),
+ &njs_array_mem_proto,
+ vm->mem_cache_pool);
+ if (nxt_slow_path(parser->arguments == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node = njs_parser(vm, parser);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_ERROR;
+ }
+
+ *start = parser->lexer->start;
+
+ ret = njs_generate_scope(vm, parser, node, njs_vmcode_stop);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_ERROR;
+ }
+
+ vm->current = parser->code_start;
+
+ vm->global_scope = parser->local_scope;
+ vm->scope_size = parser->scope_size;
+
+ vm->variables_hash = parser->variables_hash;
+ vm->values_hash = parser->values_hash;
+
+ if (vm->shared != NULL) {
+ vm->shared->values_hash = parser->values_hash;
+ }
+
+ vm->parser = NULL;
+
+ return NJS_OK;
+}
+
+
+njs_vm_t *
+njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external)
+{
+ u_char *values;
+ size_t size, scope_size;
+ njs_vm_t *nvm;
+ nxt_int_t ret;
+ njs_frame_t *frame;
+ nxt_mem_cache_pool_t *nmcp;
+
+ nxt_thread_log_debug("CLONE:");
+
+ nmcp = mcp;
+
+ if (nmcp == NULL) {
+ nmcp = nxt_mem_cache_pool_create(&njs_vm_mem_cache_pool_proto, NULL,
+ NULL, 2 * nxt_pagesize(), 128, 512, 16);
+ if (nxt_slow_path(nmcp == NULL)) {
+ return NULL;
+ }
+ }
+
+ nvm = nxt_mem_cache_zalloc(nmcp, sizeof(njs_vm_t));
+
+ if (nxt_fast_path(nvm != NULL)) {
+ nvm->mem_cache_pool = nmcp;
+
+ nvm->shared = vm->shared;
+
+ nvm->variables_hash = vm->variables_hash;
+ nvm->values_hash = vm->values_hash;
+
+ nvm->number_trap = vm->number_trap;
+ nvm->string_trap = vm->string_trap;
+
+ nvm->retval = njs_value_void;
+ nvm->current = vm->current;
+ nvm->external = external;
+
+ nvm->global_scope = vm->global_scope;
+ scope_size = vm->scope_size;
+ nvm->scope_size = scope_size;
+ 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_mem_cache_align(nmcp, sizeof(njs_value_t), size);
+ if (nxt_slow_path(frame == NULL)) {
+ goto fail;
+ }
+
+ nvm->frame = &frame->native;
+
+ frame->native.previous = NULL;
+ frame->native.arguments = NULL;
+ frame->native.start = 1;
+
+ frame->native.u.exception.next = NULL;
+ frame->native.u.exception.catch = NULL;
+
+ frame->prev_arguments = NULL;
+ frame->local = NULL;
+ frame->closure = NULL;
+
+ frame->native.size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size);
+
+ values = (u_char *) frame + NJS_GLOBAL_FRAME_SIZE;
+
+ frame->native.last = values + scope_size;
+
+ nvm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values;
+ memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope,
+ vm->scope_size);
+
+ ret = njs_shared_objects_clone(nvm);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
+ return nvm;
+ }
+
+fail:
+
+ if (mcp == NULL && nmcp != NULL) {
+ nxt_mem_cache_pool_destroy(nmcp);
+ }
+
+ return NULL;
+}
+
+
+nxt_int_t
+njs_vm_run(njs_vm_t *vm)
+{
+ nxt_str_t s;
+ nxt_int_t ret;
+
+ nxt_thread_log_debug("RUN:");
+
+ ret = njs_vmcode_interpreter(vm);
+
+ if (nxt_slow_path(ret == NXT_AGAIN)) {
+ nxt_thread_log_debug("VM: AGAIN");
+ return ret;
+ }
+
+ if (nxt_slow_path(ret != NXT_DONE)) {
+ nxt_thread_log_debug("VM: ERROR");
+ return ret;
+ }
+
+ if (vm->retval.type == NJS_NUMBER) {
+ nxt_thread_log_debug("VM: %f", vm->retval.data.u.number);
+
+ } else if (vm->retval.type == NJS_BOOLEAN) {
+ nxt_thread_log_debug("VM: boolean: %d", vm->retval.data.truth);
+
+ } else if (vm->retval.type == NJS_STRING) {
+
+ if (njs_value_to_ext_string(vm, &s, &vm->retval) == NJS_OK) {
+ nxt_thread_log_debug("VM: '%V'", &s);
+ }
+
+ } else if (vm->retval.type == NJS_FUNCTION) {
+ nxt_thread_log_debug("VM: function");
+
+ } else if (vm->retval.type == NJS_NULL) {
+ nxt_thread_log_debug("VM: null");
+
+ } else if (vm->retval.type == NJS_VOID) {
+ nxt_thread_log_debug("VM: void");
+
+ } else {
+ nxt_thread_log_debug("VM: unknown: %d", vm->retval.type);
+ }
+
+ return NJS_OK;
+}
+
+
+void
+njs_vm_return(njs_vm_t *vm, njs_value_t *retval)
+{
+ vm->retval = *retval;
+}
+
+
+nxt_int_t
+njs_vm_retval(njs_vm_t *vm, nxt_str_t *retval)
+{
+ return njs_value_to_ext_string(vm, retval, &vm->retval);
+}
+
+
+nxt_int_t
+njs_vm_exception(njs_vm_t *vm, nxt_str_t *retval)
+{
+ return njs_value_to_ext_string(vm, retval, vm->exception);
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJSCRIPT_H_INCLUDED_
+#define _NJSCRIPT_H_INCLUDED_
+
+
+typedef intptr_t njs_ret_t;
+typedef uintptr_t njs_index_t;
+typedef struct njs_vm_s njs_vm_t;
+typedef union njs_value_s njs_value_t;
+typedef struct njs_vm_shared_s njs_vm_shared_t;
+
+typedef struct {
+ njs_value_t *object;
+ njs_value_t *args;
+ uintptr_t nargs;
+ njs_index_t retval;
+} njs_param_t;
+
+
+/* sizeof(njs_value_t) is 16 bytes. */
+#define \
+njs_argument(args, n) \
+ (njs_value_t *) ((u_char *) args + n * 16)
+
+
+typedef njs_ret_t (*njs_extern_get_t)(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data);
+typedef njs_ret_t (*njs_extern_set_t)(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_str_t *value);
+typedef njs_ret_t (*njs_extern_find_t)(njs_vm_t *vm, void *obj, uintptr_t data,
+ nxt_bool_t delete);
+typedef njs_ret_t (*njs_extern_each_start_t)(njs_vm_t *vm, void *obj,
+ void *each);
+typedef njs_ret_t (*njs_extern_each_t)(njs_vm_t *vm, njs_value_t *value,
+ void *obj, void *each);
+typedef njs_ret_t (*njs_extern_method_t)(njs_vm_t *vm, njs_param_t *param);
+
+
+typedef struct njs_external_s njs_external_t;
+
+struct njs_external_s {
+ nxt_str_t name;
+
+#define NJS_EXTERN_PROPERTY 0x00
+#define NJS_EXTERN_METHOD 0x01
+#define NJS_EXTERN_OBJECT 0x80
+#define NJS_EXTERN_CASELESS_OBJECT 0x81
+
+ uintptr_t type;
+
+ njs_external_t *properties;
+ nxt_uint_t nproperties;
+
+ njs_extern_get_t get;
+ njs_extern_set_t set;
+ njs_extern_find_t find;
+
+ njs_extern_each_start_t each_start;
+ njs_extern_each_t each;
+
+ njs_extern_method_t method;
+
+ uintptr_t data;
+};
+
+
+#define NJS_OK NXT_OK
+#define NJS_ERROR NXT_ERROR
+#define NJS_AGAIN NXT_AGAIN
+#define NJS_DECLINED NXT_DECLINED
+#define NJS_DONE NXT_DONE
+
+
+NXT_EXPORT nxt_int_t njs_add_external(nxt_lvlhsh_t *hash,
+ nxt_mem_cache_pool_t *mcp, uintptr_t object, njs_external_t *external,
+ nxt_uint_t n);
+
+NXT_EXPORT njs_vm_t *njs_vm_create(nxt_mem_cache_pool_t *mcp,
+ njs_vm_shared_t **shared, nxt_lvlhsh_t *externals);
+NXT_EXPORT void njs_vm_destroy(njs_vm_t *vm);
+
+NXT_EXPORT nxt_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end);
+NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp,
+ void **external);
+NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t *vm);
+
+NXT_EXPORT void njs_vm_return(njs_vm_t *vm, njs_value_t *retval);
+NXT_EXPORT nxt_int_t njs_vm_retval(njs_vm_t *vm, nxt_str_t *retval);
+NXT_EXPORT nxt_int_t njs_vm_exception(njs_vm_t *vm, nxt_str_t *retval);
+
+NXT_EXPORT njs_ret_t njs_string_create(njs_vm_t *vm, njs_value_t *value,
+ u_char *start, size_t size, size_t length);
+NXT_EXPORT njs_ret_t njs_void_set(njs_value_t *value);
+
+NXT_EXPORT void *njs_value_data(njs_value_t *value);
+NXT_EXPORT nxt_uint_t njs_vm_is_reentrant(njs_vm_t *vm);
+NXT_EXPORT nxt_int_t njs_value_string(njs_vm_t *vm, nxt_str_t *retval,
+ njs_value_t *value, njs_value_t **tmp);
+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 njs_value_t *njs_array_add(njs_vm_t *vm, njs_value_t *array,
+ u_char *start, size_t size);
+
+
+#endif /* _NJSCRIPT_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.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>
+
+
+#define NXT_FIBOBENCH 0
+
+
+typedef struct {
+ nxt_str_t script;
+ nxt_str_t ret;
+} nxt_jscript_test_t;
+
+
+extern char **environ;
+
+
+static nxt_jscript_test_t js_test[] =
+{
+#if (NXT_FIBOBENCH == 1)
+
+ { nxt_string("function fibo(n) { \
+ if (n > 1) \
+ return fibo(n - 1) + fibo(n - 2) \
+ return 'α' \
+ } \
+ fibo(32).length"),
+ nxt_string("3524578") },
+
+#elif (NXT_FIBOBENCH > 1)
+
+ { nxt_string("function fibo(n) { \
+ if (n > 1) \
+ return fibo(n - 1) + fibo(n - 2) \
+ return 1 \
+ } \
+ fibo(32)"),
+ nxt_string("3524578") },
+
+#else /* !(NXT_FIBOBENCH) */
+
+ { nxt_string("0 == '000'"),
+ nxt_string("true") },
+
+ { nxt_string("999999999999999999999"),
+ nxt_string("1e+21") },
+
+#if 0
+ { nxt_string("9223372036854775808"),
+ nxt_string("9223372036854775808") },
+
+ { nxt_string("18446744073709551616"),
+ nxt_string("18446744073709552000") },
+
+ { nxt_string("1.7976931348623157E+308"),
+ nxt_string("1.7976931348623157e+308") },
+#endif
+
+#if 0
+ { nxt_string("var a = 'a\\'b'"),
+ nxt_string("a'b") },
+#endif
+
+ { nxt_string("+1"),
+ nxt_string("1") },
+
+ { nxt_string("+1\n"),
+ nxt_string("1") },
+
+#if 0
+ { nxt_string(""),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("\n"),
+ nxt_string("SyntaxError") },
+#endif
+
+ { nxt_string("\n +1"),
+ nxt_string("1") },
+
+ { nxt_string("1 + undefined"),
+ nxt_string("NaN") },
+
+ { nxt_string("1 + ''"),
+ nxt_string("1") },
+
+ { nxt_string("undefined + undefined"),
+ nxt_string("NaN") },
+
+ { nxt_string("1.2 + 5.7"),
+ nxt_string("6.9") },
+
+ { nxt_string("1 + 1 + '2' + 1 + 1"),
+ nxt_string("2211") },
+
+ { nxt_string("1.2 - '5.7'"),
+ nxt_string("-4.5") },
+
+ { nxt_string("1.2 + -'5.7'"),
+ nxt_string("-4.5") },
+
+ { nxt_string("1 + +'3'"),
+ nxt_string("4") },
+
+ { nxt_string("1 - undefined"),
+ nxt_string("NaN") },
+
+ { nxt_string("1 - ''"),
+ nxt_string("1") },
+
+ { nxt_string("undefined - undefined"),
+ nxt_string("NaN") },
+
+ { nxt_string("12 | 6"),
+ nxt_string("14") },
+
+ { nxt_string("12 | 'abc'"),
+ nxt_string("12") },
+
+ { nxt_string("-1 | 0"),
+ nxt_string("-1") },
+
+ { nxt_string("-2147483648 | 0"),
+ nxt_string("-2147483648") },
+
+ { nxt_string("1024.9 | 0"),
+ nxt_string("1024") },
+
+ { nxt_string("-1024.9 | 0"),
+ nxt_string("-1024") },
+
+ { nxt_string("9007199254740991 | 0"),
+ nxt_string("-1") },
+
+ { nxt_string("9007199254740992 | 0"),
+ nxt_string("0") },
+
+ { nxt_string("9007199254740993 | 0"),
+ nxt_string("0") },
+
+#if 0
+ { nxt_string("9223372036854775808 | 0"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("9223372036854777856 | 0"),
+ nxt_string("2048") },
+
+ { nxt_string("-9223372036854777856 | 0"),
+ nxt_string("-2048") },
+
+ { nxt_string("NaN | 0"),
+ nxt_string("0") },
+
+ { nxt_string("-NaN | 0"),
+ nxt_string("0") },
+
+ { nxt_string("Infinity | 0"),
+ nxt_string("0") },
+
+ { nxt_string("-Infinity | 0"),
+ nxt_string("0") },
+
+ { nxt_string("+0 | 0"),
+ nxt_string("0") },
+
+ { nxt_string("-0 | 0"),
+ nxt_string("0") },
+
+ { nxt_string("32.5 << 2.4"),
+ nxt_string("128") },
+
+ { nxt_string("32.5 << 'abc'"),
+ nxt_string("32") },
+
+ { nxt_string("'abc' << 2"),
+ nxt_string("0") },
+
+ { nxt_string("-1 << 0"),
+ nxt_string("-1") },
+
+ { nxt_string("-1 << -1"),
+ nxt_string("-2147483648") },
+
+ { nxt_string("-2147483648 << 0"),
+ nxt_string("-2147483648") },
+
+#if 0
+ { nxt_string("9223372036854775808 << 0"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("9223372036854777856 << 0"),
+ nxt_string("2048") },
+
+ { nxt_string("-9223372036854777856 << 0"),
+ nxt_string("-2048") },
+
+ { nxt_string("NaN << 0"),
+ nxt_string("0") },
+
+ { nxt_string("32.5 >> 2.4"),
+ nxt_string("8") },
+
+ { nxt_string("-1 >> 30"),
+ nxt_string("-1") },
+
+ { nxt_string("'abc' >> 2"),
+ nxt_string("0") },
+
+ { nxt_string("-1 >> 0"),
+ nxt_string("-1") },
+
+ { nxt_string("-1 >> -1"),
+ nxt_string("-1") },
+
+ { nxt_string("-2147483648 >> 0"),
+ nxt_string("-2147483648") },
+
+ { nxt_string("-2147483648 >> -1"),
+ nxt_string("-1") },
+
+#if 0
+ { nxt_string("9223372036854775808 >> 0"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("9223372036854777856 >> 0"),
+ nxt_string("2048") },
+
+ { nxt_string("-9223372036854777856 >> 0"),
+ nxt_string("-2048") },
+
+ { nxt_string("NaN >> 0"),
+ nxt_string("0") },
+
+ { nxt_string("-1 >>> 30"),
+ nxt_string("3") },
+
+ { nxt_string("NaN >>> 1"),
+ nxt_string("0") },
+
+#if 0
+ { nxt_string("9223372036854775808 >>> 1"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("-1 >>> 0"),
+ nxt_string("4294967295") },
+
+ { nxt_string("-1 >>> -1"),
+ nxt_string("1") },
+
+ { nxt_string("-2147483648 >>> 0"),
+ nxt_string("2147483648") },
+
+ { nxt_string("-2147483648 >>> -1"),
+ nxt_string("1") },
+
+#if 0
+ { nxt_string("9223372036854775808 >>> 0"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("9223372036854777856 >>> 0"),
+ nxt_string("2048") },
+
+ { nxt_string("-9223372036854777856 >>> 0"),
+ nxt_string("4294965248") },
+
+ { nxt_string("NaN >>> 0"),
+ nxt_string("0") },
+
+ { nxt_string("!2"),
+ nxt_string("false") },
+
+ { nxt_string("1 || 2"),
+ nxt_string("1") },
+
+ { nxt_string("1 && 2"),
+ nxt_string("2") },
+
+ { nxt_string("a = true; a = -~!a"),
+ nxt_string("1") },
+
+ { nxt_string("12 & 6"),
+ nxt_string("4") },
+
+ { nxt_string("-1 & 65536"),
+ nxt_string("65536") },
+
+ { nxt_string("-2147483648 & 65536"),
+ nxt_string("0") },
+
+#if 0
+ { nxt_string("9223372036854775808 & 65536"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("NaN & 65536"),
+ nxt_string("0") },
+
+ { nxt_string("12 ^ 6"),
+ nxt_string("10") },
+
+ { nxt_string("-1 ^ 65536"),
+ nxt_string("-65537") },
+
+ { nxt_string("-2147483648 ^ 65536"),
+ nxt_string("-2147418112") },
+
+#if 0
+ { nxt_string("9223372036854775808 ^ 65536"),
+ nxt_string("65536") },
+#endif
+
+ { nxt_string("NaN ^ 65536"),
+ nxt_string("65536") },
+
+ { nxt_string("x = '1'; +x + 2"),
+ nxt_string("3") },
+
+ { nxt_string("'3' -+-+-+ '1' + '1' / '3' * '6' + '2'"),
+ nxt_string("42") },
+
+ { nxt_string("'true' == true"),
+ nxt_string("false") },
+
+ { nxt_string("null == false"),
+ nxt_string("false") },
+
+ { nxt_string("!null"),
+ nxt_string("true") },
+
+ { nxt_string("0 === -0"),
+ nxt_string("true") },
+
+ { nxt_string("1/-0"),
+ nxt_string("-Infinity") },
+
+ { nxt_string("1/0 === 1/-0"),
+ nxt_string("false") },
+
+ { nxt_string("1 == true"),
+ nxt_string("true") },
+
+ { nxt_string("NaN === NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN !== NaN"),
+ nxt_string("true") },
+
+ { nxt_string("NaN == NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN != NaN"),
+ nxt_string("true") },
+
+ { nxt_string("NaN == false"),
+ nxt_string("false") },
+
+ { nxt_string("Infinity == Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("-Infinity == -Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("-Infinity < Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("Infinity - Infinity"),
+ nxt_string("NaN") },
+
+ { nxt_string("Infinity - -Infinity"),
+ nxt_string("Infinity") },
+
+ { nxt_string("undefined == 0"),
+ nxt_string("false") },
+
+ { nxt_string("undefined == null"),
+ nxt_string("true") },
+
+ { nxt_string("'1' == 1"),
+ nxt_string("true") },
+
+ { nxt_string("'1a' == '1'"),
+ nxt_string("false") },
+
+ { nxt_string("'abc' == 'abc'"),
+ nxt_string("true") },
+
+ { nxt_string("'abc' < 'abcde'"),
+ nxt_string("true") },
+
+ { nxt_string("0 == ''"),
+ nxt_string("true") },
+
+ { nxt_string("0 == ' '"),
+ nxt_string("true") },
+
+ { nxt_string("0 == ' '"),
+ nxt_string("true") },
+
+ { nxt_string("0 == '0'"),
+ nxt_string("true") },
+
+ { nxt_string("0 == ' 0 '"),
+ nxt_string("true") },
+
+ { nxt_string("0 == '000'"),
+ nxt_string("true") },
+
+ { nxt_string("'0' == ''"),
+ nxt_string("false") },
+
+ { nxt_string("1 < 2"),
+ nxt_string("true") },
+
+ { nxt_string("NaN < NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN > NaN"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < 1"),
+ nxt_string("false") },
+
+ { nxt_string("[] == false"),
+ nxt_string("true") },
+
+ { nxt_string("[0] == false"),
+ nxt_string("true") },
+
+ { nxt_string("[0,0] == false"),
+ nxt_string("false") },
+
+ { nxt_string("({}) == false"),
+ nxt_string("false") },
+
+ { nxt_string("var a = { valueOf: function() { return 5 } }; a == 5"),
+ nxt_string("true") },
+
+ { nxt_string("var a = { valueOf: function() { return '5' } }; a == 5"),
+ nxt_string("true") },
+
+ { nxt_string("var a = { valueOf: function() { return '5' } }; a == '5'"),
+ nxt_string("true") },
+
+ /* Comparisions. */
+
+ { nxt_string("null === null"),
+ nxt_string("true") },
+
+ { nxt_string("null !== null"),
+ nxt_string("false") },
+
+ { nxt_string("null == null"),
+ nxt_string("true") },
+
+ { nxt_string("null != null"),
+ nxt_string("false") },
+
+ { nxt_string("null < null"),
+ nxt_string("false") },
+
+ { nxt_string("null > null"),
+ nxt_string("false") },
+
+ { nxt_string("null <= null"),
+ nxt_string("true") },
+
+ { nxt_string("null >= null"),
+ nxt_string("true") },
+
+ /**/
+
+ { nxt_string("null === undefined"),
+ nxt_string("false") },
+
+ { nxt_string("null !== undefined"),
+ nxt_string("true") },
+
+ { nxt_string("null == undefined"),
+ nxt_string("true") },
+
+ { nxt_string("null != undefined"),
+ nxt_string("false") },
+
+ { nxt_string("null < undefined"),
+ nxt_string("false") },
+
+ { nxt_string("null > undefined"),
+ nxt_string("false") },
+
+ { nxt_string("null <= undefined"),
+ nxt_string("false") },
+
+ { nxt_string("null >= undefined"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("null === false"),
+ nxt_string("false") },
+
+ { nxt_string("null !== false"),
+ nxt_string("true") },
+
+ { nxt_string("null == false"),
+ nxt_string("false") },
+
+ { nxt_string("null != false"),
+ nxt_string("true") },
+
+ { nxt_string("null < false"),
+ nxt_string("false") },
+
+ { nxt_string("null > false"),
+ nxt_string("false") },
+
+ { nxt_string("null <= false"),
+ nxt_string("true") },
+
+ { nxt_string("null >= false"),
+ nxt_string("true") },
+
+ /**/
+
+ { nxt_string("null === true"),
+ nxt_string("false") },
+
+ { nxt_string("null !== true"),
+ nxt_string("true") },
+
+ { nxt_string("null == true"),
+ nxt_string("false") },
+
+ { nxt_string("null != true"),
+ nxt_string("true") },
+
+ { nxt_string("null < true"),
+ nxt_string("true") },
+
+ { nxt_string("null > true"),
+ nxt_string("false") },
+
+ { nxt_string("null <= true"),
+ nxt_string("true") },
+
+ { nxt_string("null >= true"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("Infinity === Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("Infinity !== Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("Infinity == Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("Infinity != Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("Infinity < Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("Infinity > Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("Infinity <= Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("Infinity >= Infinity"),
+ nxt_string("true") },
+
+ /**/
+
+ { nxt_string("-Infinity === Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("-Infinity !== Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("-Infinity == Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("-Infinity != Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("-Infinity < Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("-Infinity > Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("-Infinity <= Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("-Infinity >= Infinity"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("NaN === NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN !== NaN"),
+ nxt_string("true") },
+
+ { nxt_string("NaN == NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN != NaN"),
+ nxt_string("true") },
+
+ { nxt_string("NaN < NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN > NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN >= NaN"),
+ nxt_string("false") },
+
+ { nxt_string("NaN <= NaN"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("null < 0"),
+ nxt_string("false") },
+
+ { nxt_string("null < 1"),
+ nxt_string("true") },
+
+ { nxt_string("null < NaN"),
+ nxt_string("false") },
+
+ { nxt_string("null < -Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("null < Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("null < 'null'"),
+ nxt_string("false") },
+
+ { nxt_string("null < '1'"),
+ nxt_string("true") },
+
+ { nxt_string("null < [1]"),
+ nxt_string("true") },
+
+ { nxt_string("null < ({})"),
+ nxt_string("false") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } }; null < a"),
+ nxt_string("true") },
+
+ { nxt_string("var a = { valueOf: function() { return 'null' } }; null < a"),
+ nxt_string("false") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } }; null < a"),
+ nxt_string("true") },
+
+ /**/
+
+ { nxt_string("undefined < null"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < undefined"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < false"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < true"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < 0"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < 1"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < NaN"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < -Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < 'undefined'"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < '1'"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < [1]"),
+ nxt_string("false") },
+
+ { nxt_string("undefined < ({})"),
+ nxt_string("false") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } undefined < a"),
+ nxt_string("false") },
+
+ { nxt_string("var a = { valueOf: function() { return 'undefined' } } \
+ undefined < a"),
+ nxt_string("false") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } \
+ undefined < a"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("false < 1"),
+ nxt_string("true") },
+
+ { nxt_string("true < 1"),
+ nxt_string("false") },
+
+ { nxt_string("-1 < 1"),
+ nxt_string("true") },
+
+ { nxt_string("-1 < '1'"),
+ nxt_string("true") },
+
+ { nxt_string("NaN < NaN"),
+ nxt_string("false") },
+
+ { nxt_string("-Infinity < Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("Infinity < -Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("1 < 'abc'"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("[] === []"),
+ nxt_string("false") },
+
+ { nxt_string("[] !== []"),
+ nxt_string("true") },
+
+ { nxt_string("[] == []"),
+ nxt_string("false") },
+
+ { nxt_string("[] != []"),
+ nxt_string("true") },
+
+ { nxt_string("[] < []"),
+ nxt_string("false") },
+
+ { nxt_string("[] > []"),
+ nxt_string("false") },
+
+ { nxt_string("[] >= []"),
+ nxt_string("true") },
+
+ { nxt_string("[] <= []"),
+ nxt_string("true") },
+
+ /**/
+
+ { nxt_string("({}) === ({})"),
+ nxt_string("false") },
+
+ { nxt_string("({}) !== ({})"),
+ nxt_string("true") },
+
+ { nxt_string("({}) == ({})"),
+ nxt_string("false") },
+
+ { nxt_string("({}) != ({})"),
+ nxt_string("true") },
+
+ { nxt_string("({}) > ({})"),
+ nxt_string("false") },
+
+ { nxt_string("({}) <= ({})"),
+ nxt_string("true") },
+
+ { nxt_string("({}) >= ({})"),
+ nxt_string("true") },
+
+ /**/
+
+ { nxt_string("[0] == ({})"),
+ nxt_string("false") },
+
+ { nxt_string("[0] != ({})"),
+ nxt_string("true") },
+
+ { nxt_string("[0] <= ({})"),
+ nxt_string("true") },
+
+ { nxt_string("[0] >= ({})"),
+ nxt_string("false") },
+
+ /**/
+
+ { nxt_string("a = 1 ? 2 : 3"),
+ nxt_string("2") },
+
+ { nxt_string("a = 1 ? 2 : 3 ? 4 : 5"),
+ nxt_string("2") },
+
+ { nxt_string("a = 0 ? 2 : 3 ? 4 : 5"),
+ nxt_string("4") },
+
+ { nxt_string("0 ? 2 ? 3 : 4 : 5"),
+ nxt_string("5") },
+
+ { nxt_string("1 ? 2 ? 3 : 4 : 5"),
+ nxt_string("3") },
+
+ { nxt_string("1 ? 0 ? 3 : 4 : 5"),
+ nxt_string("4") },
+
+ { nxt_string("(1 ? 0 : 3) ? 4 : 5"),
+ nxt_string("5") },
+
+ { nxt_string("a = (1 + 2) ? 2 ? 3 + 4 : 5 : 6"),
+ nxt_string("7") },
+
+ { nxt_string("a = (1 ? 2 : 3) + 4"),
+ nxt_string("6") },
+
+ { nxt_string("a = 1 ? b = 2 + 4 : b = 3"),
+ nxt_string("6") },
+
+ /* Increment. */
+
+ { nxt_string("var a = 1; ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = '1'; ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = [1]; ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = {}; ++a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } }; ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } }; ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } }; ++a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } }; ++a"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; a = ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = '1'; a = ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = [1]; a = ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = {}; a = ++a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } a = ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } a = ++a"),
+ nxt_string("2") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } a = ++a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } a = ++a"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; var b = ++a; a +' '+ b"),
+ nxt_string("2 2") },
+
+ { nxt_string("var a = '1'; var b = ++a; a +' '+ b"),
+ nxt_string("2 2") },
+
+ { nxt_string("var a = [1]; var b = ++a; a +' '+ b"),
+ nxt_string("2 2") },
+
+ { nxt_string("var a = {}; var b = ++a; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } \
+ var b = ++a; a +' '+ b"),
+ nxt_string("2 2") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } \
+ var b = ++a; a +' '+ b"),
+ nxt_string("2 2") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } \
+ var b = ++a; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } \
+ var b = ++a; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ /* Post increment. */
+
+ { nxt_string("var a = 1; a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = '1'; a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = [1]; a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = {}; a++"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } a++"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } a++"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; a = a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = '1'; a = a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = [1]; a = a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = {}; a = a++"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } a = a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } a = a++"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } a = a++"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } a = a++"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; var b = a++; a +' '+ b"),
+ nxt_string("2 1") },
+
+ { nxt_string("var a = '1'; var b = a++; a +' '+ b"),
+ nxt_string("2 1") },
+
+ { nxt_string("var a = [1]; var b = a++; a +' '+ b"),
+ nxt_string("2 1") },
+
+ { nxt_string("var a = {}; var b = a++; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } \
+ var b = a++; a +' '+ b"),
+ nxt_string("2 1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } \
+ var b = a++; a +' '+ b"),
+ nxt_string("2 1") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } \
+ var b = a++; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } \
+ var b = a++; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ /* Decrement. */
+
+ { nxt_string("var a = 1; --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = '1'; --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = [1]; --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = {}; --a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1} }; --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = { valueOf: function() { return '1'} }; --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = { valueOf: function() { return [1]} }; --a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } }; --a"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; a = --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = '1'; a = --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = [1]; a = --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = {}; a = --a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1} } a = --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = { valueOf: function() { return '1'} } a = --a"),
+ nxt_string("0") },
+
+ { nxt_string("var a = { valueOf: function() { return [1]} } a = --a"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } a = --a"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; var b = --a; a +' '+ b"),
+ nxt_string("0 0") },
+
+ { nxt_string("var a = '1'; var b = --a; a +' '+ b"),
+ nxt_string("0 0") },
+
+ { nxt_string("var a = [1]; var b = --a; a +' '+ b"),
+ nxt_string("0 0") },
+
+ { nxt_string("var a = {}; var b = --a; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } \
+ var b = --a; a +' '+ b"),
+ nxt_string("0 0") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } \
+ var b = --a; a +' '+ b"),
+ nxt_string("0 0") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } \
+ var b = --a; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } \
+ var b = --a; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ /* Post decrement. */
+
+ { nxt_string("var a = 1; a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = '1'; a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = [1]; a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = {}; a--"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } a--"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } a--"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; a = a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = '1'; a = a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = [1]; a = a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = {}; a = a--"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } a = a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } a = a--"),
+ nxt_string("1") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } a = a--"),
+ nxt_string("NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } a = a--"),
+ nxt_string("NaN") },
+
+ /**/
+
+ { nxt_string("var a = 1; var b = a--; a +' '+ b"),
+ nxt_string("0 1") },
+
+ { nxt_string("var a = '1'; var b = a--; a +' '+ b"),
+ nxt_string("0 1") },
+
+ { nxt_string("var a = [1]; var b = a--; a +' '+ b"),
+ nxt_string("0 1") },
+
+ { nxt_string("var a = {}; var b = a--; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return 1 } } \
+ var b = a--; a +' '+ b"),
+ nxt_string("0 1") },
+
+ { nxt_string("var a = { valueOf: function() { return '1' } } \
+ var b = a--; a +' '+ b"),
+ nxt_string("0 1") },
+
+ { nxt_string("var a = { valueOf: function() { return [1] } } \
+ var b = a--; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ { nxt_string("var a = { valueOf: function() { return {} } } \
+ var b = a--; a +' '+ b"),
+ nxt_string("NaN NaN") },
+
+ /**/
+
+ { nxt_string("a = 2; b = ++a + ++a; a + ' ' + b"),
+ nxt_string("4 7") },
+
+ { nxt_string("a = 2; b = a++ + a++; a + ' ' + b"),
+ nxt_string("4 5") },
+
+ { nxt_string("a = b = 7; a +' '+ b"),
+ nxt_string("7 7") },
+
+ { nxt_string("a = b = c = 5; a +' '+ b +' '+ c"),
+ nxt_string("5 5 5") },
+
+ { nxt_string("a = b = (c = 5) + 2; a +' '+ b +' '+ c"),
+ nxt_string("7 7 5") },
+
+ { nxt_string("1, 2 + 5, 3"),
+ nxt_string("3") },
+
+ { nxt_string("a = 1 /* YES */\n b = a + 2 \n \n + 1 \n + 3"),
+ nxt_string("7") },
+
+ { nxt_string("a = 1 // YES \n b = a + 2 \n \n + 1 \n + 3"),
+ nxt_string("7") },
+
+ { nxt_string("a = 0; ++ \n a"),
+ nxt_string("1") },
+
+ { nxt_string("a = 0; a \n ++"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("a = 1 ? 2 \n : 3"),
+ nxt_string("2") },
+
+ { nxt_string("a = 0 / 0; b = 1 / 0; c = -1 / 0; a +' '+ b +' '+ c"),
+ nxt_string("NaN Infinity -Infinity") },
+
+ { nxt_string("a = (b = 7) + 5; var c; a +' '+ b +' '+ c"),
+ nxt_string("12 7 undefined") },
+
+ { nxt_string("var a, b = 1, c; a +' '+ b +' '+ c"),
+ nxt_string("undefined 1 undefined") },
+
+ { nxt_string("var a = 1, b = a + 1; a +' '+ b"),
+ nxt_string("1 2") },
+
+ { nxt_string("a = a = 1"),
+ nxt_string("1") },
+
+ { nxt_string("var a = 1, \n b; a +' '+ b"),
+ nxt_string("1 undefined") },
+
+ { nxt_string("a = b + 1; var b; a +' '+ b"),
+ nxt_string("NaN undefined") },
+
+ { nxt_string("var a += 1"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("var a = a + 1"),
+ nxt_string("NaN") },
+
+ { nxt_string("a = b + 1; var b = 1; a +' '+ b"),
+ nxt_string("NaN 1") },
+
+ { nxt_string("(a) = 1"),
+ nxt_string("1") },
+
+ { nxt_string("a"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a + a"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a = b + 1"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a = a + 1"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a += 1"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a += 1; var a = 2"),
+ nxt_string("2") },
+
+ { nxt_string("var a = 1"),
+ nxt_string("1") },
+
+ { nxt_string("var a = 1; a = (a = 2) + a"),
+ nxt_string("4") },
+
+ { nxt_string("var a = 1; a = a + (a = 2)"),
+ nxt_string("3") },
+
+ { nxt_string("var a = 1; a += (a = 2)"),
+ nxt_string("3") },
+
+ { nxt_string("var a = b = 1; a +' '+ b"),
+ nxt_string("1 1") },
+
+ { nxt_string("var a \n if (!a) a = 3; a"),
+ nxt_string("3") },
+
+ { nxt_string("a = 3; if (true) if (false); else a = 2; a"),
+ nxt_string("2") },
+
+ { nxt_string("for (i = 0; i < 10; i++) { i += 1 } i"),
+ nxt_string("10") },
+
+ /* Factorial. */
+
+ { nxt_string("n = 5; f = 1; while (n--) f *= n + 1; f"),
+ nxt_string("120") },
+
+ { nxt_string("n = 5; f = 1; while (n) { f *= n; n-- } f"),
+ nxt_string("120") },
+
+ /* Fibonacci. */
+
+ { nxt_string("var n = 50, x; \
+ for(i=0,j=1,k=0; k<n; i=j,j=x,k++ ){ x=i+j } x"),
+ nxt_string("20365011074") },
+
+ { nxt_string("3 + 'abc' + 'def' + null + true + false + undefined"),
+ nxt_string("3abcdefnulltruefalseundefined") },
+
+ { nxt_string("a = 0; do a++; while (a < 5) if (a == 5) a = 7.33 \n\
+ else a = 8; while (a < 10) a++; a"),
+ nxt_string("10.33") },
+
+ /* typeof. */
+
+ { nxt_string("typeof null"),
+ nxt_string("object") },
+
+ { nxt_string("typeof undefined"),
+ nxt_string("undefined") },
+
+ { nxt_string("typeof false"),
+ nxt_string("boolean") },
+
+ { nxt_string("typeof true"),
+ nxt_string("boolean") },
+
+ { nxt_string("typeof 0"),
+ nxt_string("number") },
+
+ { nxt_string("typeof -1"),
+ nxt_string("number") },
+
+ { nxt_string("typeof Infinity"),
+ nxt_string("number") },
+
+ { nxt_string("typeof NaN"),
+ nxt_string("number") },
+
+ { nxt_string("typeof 'a'"),
+ nxt_string("string") },
+
+ { nxt_string("typeof {}"),
+ nxt_string("object") },
+
+ { nxt_string("typeof Object()"),
+ nxt_string("object") },
+
+ { nxt_string("typeof []"),
+ nxt_string("object") },
+
+ { nxt_string("typeof function(){}"),
+ nxt_string("function") },
+
+ { nxt_string("typeof Object"),
+ nxt_string("function") },
+
+ { nxt_string("typeof /./i"),
+ nxt_string("object") },
+
+ { nxt_string("typeof $r"),
+ nxt_string("undefined") },
+
+ { nxt_string("typeof a"),
+ nxt_string("undefined") },
+
+ { nxt_string("typeof a; a"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("typeof a; a = 1"),
+ nxt_string("1") },
+
+ /**/
+
+ { nxt_string("void 0"),
+ nxt_string("undefined") },
+
+ { nxt_string("undefined = 1"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("var a; b = a; a = 1; a +' '+ b"),
+ nxt_string("1 undefined") },
+
+ { nxt_string("a = 1"),
+ nxt_string("1") },
+
+ { nxt_string("a; a = 1; a"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a = {}; typeof a +' '+ a"),
+ nxt_string("object [object Object]") },
+
+ { nxt_string("a = {}; a.b"),
+ nxt_string("undefined") },
+
+ { nxt_string("a = {}; a.b = 1 + 2; a.b"),
+ nxt_string("3") },
+
+ { nxt_string("a = {}; a['b']"),
+ nxt_string("undefined") },
+
+ { nxt_string("a = {}; a.b.c"),
+ nxt_string("TypeError") },
+
+ { nxt_string("a = {}; a.b = 1; a.b"),
+ nxt_string("1") },
+
+ { nxt_string("a = {}; a.b = 1; a.b += 2"),
+ nxt_string("3") },
+
+ { nxt_string("a = {}; a.b = 1; a.b += a.b"),
+ nxt_string("2") },
+
+ { nxt_string("a = {}; a.b = 1; x = {}; x.b = 3; a.b += (x.b = 2)"),
+ nxt_string("3") },
+
+ { nxt_string("a = {}; a.b = 1; a.b += (a.b = 2)"),
+ nxt_string("3") },
+
+ { nxt_string("a = {}; a.b += 1"),
+ nxt_string("NaN") },
+
+ { nxt_string("a = 1; b = 2; a = b += 1"),
+ nxt_string("3") },
+
+ { nxt_string("a = 1; b = { x:2 }; a = b.x += 1"),
+ nxt_string("3") },
+
+ { nxt_string("a = 1; b = { x:2 }; a = b.x += (a = 1)"),
+ nxt_string("3") },
+
+ { nxt_string("a = 1; a.b++; a.b"),
+ nxt_string("undefined") },
+
+ { nxt_string("a = {}; a.b = {}; a.b.c = 1; a.b['c']"),
+ nxt_string("1") },
+
+ { nxt_string("a = {}; a.b = {}; a.b.c = 1; a['b']['c']"),
+ nxt_string("1") },
+
+ { nxt_string("a = {}; a.b = {}; c = 'd'; a.b.d = 1; a['b'][c]"),
+ nxt_string("1") },
+
+ { nxt_string("a = {}; a.b = 1; c = a.b++; a.b +' '+ c"),
+ nxt_string("2 1") },
+
+ { nxt_string("a = 2; a.b = 1; c = a.b++; a +' '+ a.b +' '+ c"),
+ nxt_string("2 undefined NaN") },
+
+ { nxt_string("x = { a: 1 }; x.a"),
+ nxt_string("1") },
+
+ { nxt_string("a = { x:1 }; b = { y:2 }; a.x = b.y; a.x"),
+ nxt_string("2") },
+
+ { nxt_string("a = { x:1 }; b = { y:2 }; c = a.x = b.y; c"),
+ nxt_string("2") },
+
+ { nxt_string("a = { x:1 }; b = { y:2 }; a.x = b.y"),
+ nxt_string("2") },
+
+ { nxt_string("a = { x:1 }; b = a.x = 1 + 2; a.x +' '+ b"),
+ nxt_string("3 3") },
+
+ { nxt_string("a = { x:1 }; b = { y:2 }; c = {}; c.x = a.x = b.y; c.x"),
+ nxt_string("2") },
+
+ { nxt_string("y = 2; x = { a:1, b: y + 5, c:3 }; x.a +' '+ x.b +' '+ x.c"),
+ nxt_string("1 7 3") },
+
+ { nxt_string("x = { a: 1, b: { a:2, c:5 } }; x.a +' '+ x.b.a +' '+ x.b.c"),
+ nxt_string("1 2 5") },
+
+ { nxt_string("var y = 5; x = { a:y }; x.a"),
+ nxt_string("5") },
+
+ { nxt_string("x = { a: 1, b: x.a }"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("a = { b: 2 }; a.b += 1"),
+ nxt_string("3") },
+
+ { nxt_string("o = {a:1}; c = o; o.a = o = {b:5}; \
+ o.a +' '+ o.b +' '+ c.a.b"),
+ nxt_string("undefined 5 5") },
+
+ { nxt_string("y = { a: 2 }; x = { a: 1, b: y.a }; x.a +' '+ x.b"),
+ nxt_string("1 2") },
+
+ { nxt_string("y = { a: 1 }; x = { a: y.a++, b: y.a++ }\n\
+ x.a +' '+ x.b +' '+ y.a"),
+ nxt_string("1 2 3") },
+
+ { nxt_string("var a=''; var o = {a:1, b:2} \
+ for (p in o) { a += p +':'+ o[p] +',' } a"),
+ nxt_string("a:1,b:2,") },
+
+ { nxt_string("x = { a: 1 }; b = delete x.a; x.a +' '+ b"),
+ nxt_string("undefined true") },
+
+ { nxt_string("delete null"),
+ nxt_string("true") },
+
+ { nxt_string("var a; delete a"),
+ nxt_string("false") },
+
+ { nxt_string("delete undefined"),
+ nxt_string("false") },
+
+ { nxt_string("delete NaN"),
+ nxt_string("false") },
+
+ { nxt_string("delete Infinity"),
+ nxt_string("false") },
+
+ { nxt_string("delete -Infinity"),
+ nxt_string("true") },
+
+ { nxt_string("delete (1/0)"),
+ nxt_string("true") },
+
+ { nxt_string("delete 1"),
+ nxt_string("true") },
+
+ { nxt_string("delete (a = 1); a"),
+ nxt_string("1") },
+
+ { nxt_string("delete a"),
+ nxt_string("true") },
+
+ { nxt_string("a = 1; delete a"),
+ nxt_string("true") },
+
+ { nxt_string("a = 1; delete a; typeof a"),
+ nxt_string("undefined") },
+
+ { nxt_string("a = { x:1 }; ('x' in a) +' '+ (1 in a)"),
+ nxt_string("true false") },
+
+ { nxt_string("a = {}; 1 in a"),
+ nxt_string("false") },
+
+ { nxt_string("a = 1; 1 in a"),
+ nxt_string("TypeError") },
+
+ { nxt_string("a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"),
+ nxt_string("6") },
+
+ { nxt_string("a = [ 1, 2, 3 ]; a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3]"),
+ nxt_string("1 2 3 undefined") },
+
+ { nxt_string("[] - 2"),
+ nxt_string("-2") },
+
+ { nxt_string("[1] - 2"),
+ nxt_string("-1") },
+
+ { nxt_string("[[1]] - 2"),
+ nxt_string("-1") },
+
+ { nxt_string("[[[1]]] - 2"),
+ nxt_string("-1") },
+
+ { nxt_string("a = []; a - 2"),
+ nxt_string("-2") },
+
+ { nxt_string("a = [1]; a - 2"),
+ nxt_string("-1") },
+
+ { nxt_string("a = []; a[0] = 1; a - 2"),
+ nxt_string("-1") },
+
+ { nxt_string("[] + 2 + 3"),
+ nxt_string("23") },
+
+ { nxt_string("[1] + 2 + 3"),
+ nxt_string("123") },
+
+ { nxt_string("a = []; a + 2 + 3"),
+ nxt_string("23") },
+
+ { nxt_string("a = [1]; a + 2 + 3"),
+ nxt_string("123") },
+
+ { nxt_string("a = [1,2]; i = 0; a[i++] += a[0] = 5 + i; a[0] +' '+ a[1]"),
+ nxt_string("7 2") },
+
+ { nxt_string("a = []; a[0] = 1; a + 2 + 3"),
+ nxt_string("123") },
+
+ { nxt_string("a = []; a['0'] = 1; a + 2 + 3"),
+ nxt_string("123") },
+
+ { nxt_string("a = []; a[2] = 1; a[2]"),
+ nxt_string("1") },
+
+ { nxt_string("a = [1, 2]; 1 in a"),
+ nxt_string("true") },
+
+ { nxt_string("a = [1, 2]; 2 in a"),
+ nxt_string("false") },
+
+ { nxt_string("a = [1, 2]; delete a[0]; 0 in a"),
+ nxt_string("false") },
+
+ { nxt_string("var s = '', a = [5,1,2]; \
+ a[null] = null; \
+ a[undefined] = 'defined'; \
+ a[false] = false; \
+ a[true] = true; \
+ a[-0] = 0; \
+ a[Infinity] = Infinity; \
+ a[-Infinity] = -Infinity; \
+ a[NaN] = NaN; \
+ a[-NaN] = -NaN; \
+ for (i in a) { s += i +':'+ a[i] +',' } s"),
+ nxt_string("0:0,1:1,2:2,null:null,undefined:defined,false:false,true:true,Infinity:Infinity,-Infinity:-Infinity,NaN:NaN,") },
+
+ { nxt_string("[].length"),
+ nxt_string("0") },
+
+ { nxt_string("[1,2].length"),
+ nxt_string("2") },
+
+ { nxt_string("a = [1,2]; a.length"),
+ nxt_string("2") },
+
+ { nxt_string("a = [1,2,3]; a.join()"),
+ nxt_string("1,2,3") },
+
+ { nxt_string("a = [1,2,3]; a.join(':')"),
+ nxt_string("1:2:3") },
+
+ { nxt_string("a = []; a[5] = 5; a.join()"),
+ nxt_string(",,,,,5") },
+
+ { nxt_string("a = []; a[5] = 5; a +''"),
+ nxt_string(",,,,,5") },
+
+ { nxt_string("a = []; a.concat([]) +''"),
+ nxt_string("") },
+
+ { nxt_string("a = [1,2,3]; a.concat(4, [5, 6, 7], 8) +''"),
+ nxt_string("1,2,3,4,5,6,7,8") },
+
+ { nxt_string("a = []; a[100] = a.length; a[100] +' '+ a.length"),
+ nxt_string("0 101") },
+
+ { nxt_string("a = [1,2]; a[100] = 100; a[100] +' '+ a.length"),
+ nxt_string("100 101") },
+
+ { nxt_string("a = [1,2,3,4,5]; b = a.slice(3); b[0] +' '+ b[1] +' '+ b[2]"),
+ nxt_string("4 5 undefined") },
+
+ { nxt_string("a = [1,2]; a.pop() +' '+ a.length +' '+ a"),
+ nxt_string("2 1 1") },
+
+ { nxt_string("a = [1,2]; len = a.push(3); len +' '+ a.pop() +' '+ a"),
+ nxt_string("3 3 1,2") },
+
+ { nxt_string("a = [1,2]; len = a.push(3,4,5); len +' '+ a.pop() +' '+ a"),
+ nxt_string("5 5 1,2,3,4") },
+
+ { nxt_string("a = [1,2,3]; a.shift() +' '+ a[0] +' '+ a.length"),
+ nxt_string("1 2 2") },
+
+ { nxt_string("a = [1,2]; len = a.unshift(3); len +' '+ a.shift()"),
+ nxt_string("3 3") },
+
+ { nxt_string("a = [1,2]; len = a.unshift(3,4,5); len +' '+ a.shift()"),
+ nxt_string("5 3") },
+
+ { nxt_string("var a = []; var s = { sum: 0 }; \
+ a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"),
+ nxt_string("0") },
+
+ { nxt_string("var a = new Array(3); var s = { sum: 0 }; \
+ a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"),
+ nxt_string("0") },
+
+#if 0
+ { nxt_string("var a = [,,,]; var s = { sum: 0 }; \
+ a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"),
+ nxt_string("0") },
+#endif
+
+ { nxt_string("var a = [1,2,3]; var s = { sum: 0 }; \
+ a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"),
+ nxt_string("6") },
+
+ { nxt_string("var a = [1,2,3]; \
+ a.forEach(function(v, i, a) { a[i+3] = a.length }); \
+ a +''"),
+ nxt_string("1,2,3,3,4,5") },
+
+ { nxt_string("var a = []; \
+ a.some(function(v, i, a) { return v > 1 })"),
+ nxt_string("false") },
+
+ { nxt_string("var a = [1,2,3]; \
+ a.some(function(v, i, a) { return v > 1 })"),
+ nxt_string("true") },
+
+ { nxt_string("var a = [1,2,3]; \
+ a.some(function(v, i, a) { return v > 2 })"),
+ nxt_string("true") },
+
+ { nxt_string("var a = [1,2,3]; \
+ a.some(function(v, i, a) { return v > 3 })"),
+ nxt_string("false") },
+
+ { nxt_string("var a = []; \
+ a.every(function(v, i, a) { return v > 1 })"),
+ nxt_string("true") },
+
+ { nxt_string("var a = [3,2,1]; \
+ a.every(function(v, i, a) { return v > 3 })"),
+ nxt_string("false") },
+
+ { nxt_string("var a = [3,2,1]; \
+ a.every(function(v, i, a) { return v > 2 })"),
+ nxt_string("false") },
+
+ { nxt_string("var a = [3,2,1]; \
+ a.every(function(v, i, a) { return v > 0 })"),
+ nxt_string("true") },
+
+ { nxt_string("var a = '0123456789' + '012345' \
+ var b = 'abcdefghij' + 'klmnop' \
+ a = b"),
+ nxt_string("abcdefghijklmnop") },
+
+ { nxt_string("''.length"),
+ nxt_string("0") },
+
+ { nxt_string("'abc'.length"),
+ nxt_string("3") },
+
+ { nxt_string("'絵文字'.length"),
+ nxt_string("3") },
+
+ { nxt_string("'えもじ'.length"),
+ nxt_string("3") },
+
+ { nxt_string("'囲碁織'.length"),
+ nxt_string("3") },
+
+ { nxt_string("a = 'abc'; a.length"),
+ nxt_string("3") },
+
+ { nxt_string("a = 'abc'; a['length']"),
+ nxt_string("3") },
+
+ { nxt_string("a = 'абв'; a.length"),
+ nxt_string("3") },
+
+ { nxt_string("a = 'abc' + 'абв'; a.length"),
+ nxt_string("6") },
+
+ { nxt_string("a = 'abc' + 1 + 'абв'; a +' '+ a.length"),
+ nxt_string("abc1абв 7") },
+
+ /* TODO: '\u00C2\u00B6'.bytes */
+
+ { nxt_string("a = '\xC3\x82\xC2\xB6'.bytes; u = a.utf8; \
+ a.length +' '+ a +' '+ u.length +' '+ u"),
+ nxt_string("2 \xC2\xB6 1 \xC2\xB6") },
+
+ { nxt_string("a = 1; a.length"),
+ nxt_string("undefined") },
+
+ { nxt_string("a = 'abc'; a.concat('абв', 123)"),
+ nxt_string("abcабв123") },
+
+ { nxt_string("a = $r.uri; s = a.utf8; s.length +' '+ s"),
+ nxt_string("3 АБВ") },
+
+ { nxt_string("a = $r.uri; a +' '+ a.length +' '+ a"),
+ nxt_string("АБВ 6 АБВ") },
+
+ { nxt_string("$r.uri = 'αβγ'; a = $r.uri; a.length +' '+ a"),
+ nxt_string("6 αβγ") },
+
+ { nxt_string("$r.uri.length +' '+ $r.uri"),
+ nxt_string("6 αβγ") },
+
+ { nxt_string("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"),
+ nxt_string("4 βγ") },
+
+ { nxt_string("a = $r.host; a +' '+ a.length +' '+ a"),
+ nxt_string("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") },
+
+ { nxt_string("a = $r.host; a.substr(2, 2)"),
+ nxt_string("Б") },
+
+ { nxt_string("a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"),
+ nxt_string("User-Agent|АБВ 17 User-Agent|АБВ") },
+
+ { nxt_string("var a=''; \
+ for (p in $r.header) { a += p +':'+ $r.header[p] +',' }\
+ a"),
+ nxt_string("01:01|АБВ,02:02|АБВ,03:03|АБВ,") },
+
+#if 1
+ { nxt_string("$r.nonexistent"),
+ nxt_string("undefined") },
+#endif
+
+ { nxt_string("a = 'abcdefgh'; a.substr(3, 15)"),
+ nxt_string("defgh") },
+
+ { nxt_string("'abcdefgh'.substr(3, 15)"),
+ nxt_string("defgh") },
+
+ { nxt_string("'abcdefghijklmno'.substr(3, 4)"),
+ nxt_string("defg") },
+
+ { nxt_string("'abcdefghijklmno'.substr(-3, 2)"),
+ nxt_string("mn") },
+
+ { nxt_string("'abcdefgh'.substr(100, 120)"),
+ nxt_string("") },
+
+ { nxt_string("('abc' + 'defgh').substr(1, 4)"),
+ nxt_string("bcde") },
+
+ { nxt_string("'abcdefghijklmno'.substring(3, 5)"),
+ nxt_string("de") },
+
+ { nxt_string("'abcdefgh'.substring(3)"),
+ nxt_string("defgh") },
+
+ { nxt_string("'abcdefgh'.substring(5, 3)"),
+ nxt_string("de") },
+
+ { nxt_string("'abcdefgh'.substring(100, 120)"),
+ nxt_string("") },
+
+ { nxt_string("'abcdefghijklmno'.slice(NaN, 5)"),
+ nxt_string("abcde") },
+
+ { nxt_string("'abcdefghijklmno'.slice('0', '5')"),
+ nxt_string("abcde") },
+
+ { nxt_string("'abcdefghijklmno'.slice(3, 5)"),
+ nxt_string("de") },
+
+ { nxt_string("'abcdefgh'.slice(3)"),
+ nxt_string("defgh") },
+
+ { nxt_string("'abcdefgh'.slice(3, -2)"),
+ nxt_string("def") },
+
+ { nxt_string("'abcdefgh'.slice(5, 3)"),
+ nxt_string("") },
+
+ { nxt_string("'abcdefgh'.slice(100, 120)"),
+ nxt_string("") },
+
+ { nxt_string("'abc'.charAt(1 + 1)"),
+ nxt_string("c") },
+
+ { nxt_string("'abc'.charAt(3)"),
+ nxt_string("") },
+
+ { nxt_string("'abc'.charCodeAt(1 + 1)"),
+ nxt_string("99") },
+
+ { nxt_string("'abc'.charCodeAt(3)"),
+ nxt_string("NaN") },
+
+ { nxt_string("a = 'abcdef'; a.3"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'abcdef'[3]"),
+ nxt_string("d") },
+
+ { nxt_string("'abcdef'[0]"),
+ nxt_string("a") },
+
+ { nxt_string("'abcdef'[-1]"),
+ nxt_string("undefined") },
+
+ { nxt_string("'abcdef'[NaN]"),
+ nxt_string("undefined") },
+
+ { nxt_string("'abcdef'[3.5]"),
+ nxt_string("undefined") },
+
+ { nxt_string("'abcdef'[8]"),
+ nxt_string("undefined") },
+
+ { nxt_string("a = 'abcdef'; b = 1 + 2; a[b]"),
+ nxt_string("d") },
+
+ { nxt_string("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"),
+ nxt_string("1077") },
+
+ { nxt_string("'12345абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(35)"),
+ nxt_string("1101") },
+
+ { nxt_string("'12345абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.substring(35)"),
+ nxt_string("эюя") },
+
+ { nxt_string("'abcdef'.substr(-5, 4).substring(3, 1).charAt(1)"),
+ nxt_string("d") },
+
+ { nxt_string("'abcdef'.substr(2, 4).charAt(2)"),
+ nxt_string("e") },
+
+ { nxt_string("a = 'abcdef'.substr(2, 4).charAt(2).length"),
+ nxt_string("1") },
+
+ { nxt_string("a = 'abcdef'.substr(2, 4).charAt(2) + '1234'"),
+ nxt_string("e1234") },
+
+ { nxt_string("a = ('abcdef'.substr(2, 5 * 2 - 6).charAt(2) + '1234') \
+ .length"),
+ nxt_string("5") },
+
+ { nxt_string("a = 'abcdef'; function f(a) { \
+ return a.slice(a.indexOf('cd')) } f(a)"),
+ nxt_string("cdef") },
+
+ { nxt_string("a = 'abcdef'; a.slice(a.indexOf('cd'))"),
+ nxt_string("cdef") },
+
+ { nxt_string("'abcdef'.indexOf('de', 2)"),
+ nxt_string("3") },
+
+ { nxt_string("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.indexOf('лмно', 10)"),
+ nxt_string("12") },
+
+ { nxt_string("'abcdef'.indexOf('a', 10)"),
+ nxt_string("-1") },
+
+ { nxt_string("'abcdef'.indexOf('q', 0)"),
+ nxt_string("-1") },
+
+ { nxt_string("'abcdef'.indexOf('', 10)"),
+ nxt_string("6") },
+
+ { nxt_string("'abcdef'.indexOf('', 3)"),
+ nxt_string("3") },
+
+ { nxt_string("'abc abc abc abc'.lastIndexOf('abc')"),
+ nxt_string("12") },
+
+ { nxt_string("'abc abc abc abc'.lastIndexOf('abc', 11)"),
+ nxt_string("8") },
+
+ { nxt_string("'abc abc abc abc'.lastIndexOf('abc', 0)"),
+ nxt_string("-1") },
+
+ { nxt_string("'abcdefgh'.search()"),
+ nxt_string("0") },
+
+ { nxt_string("'abcdefgh'.search(/def/)"),
+ nxt_string("3") },
+
+ { nxt_string("''.match(/^$/) +''"),
+ nxt_string("") },
+
+ { nxt_string("''.match(/^$/g) +''"),
+ nxt_string("") },
+
+ { nxt_string("'abcdefgh'.match(/def/) +''"),
+ nxt_string("def") },
+
+ { nxt_string("'abc ABC aBc'.match(/abc/ig) +''"),
+ nxt_string("abc,ABC,aBc") },
+
+ { nxt_string("var q = 1; function x(a, b, c) { q = a } x(5); q"),
+ nxt_string("5") },
+
+ { nxt_string("function x(a) { while (a < 2) a++; return a + 1 } x(1) "),
+ nxt_string("3") },
+
+ /* Recursive factorial. */
+
+ { nxt_string("function f(a) { \
+ if (a > 1) \
+ return a * f(a - 1) \
+ return 1 \
+ } \
+ f(10)"),
+ nxt_string("3628800") },
+
+ /* Recursive factorial. */
+
+ { nxt_string("function f(a) { return (a > 1) ? a * f(a - 1) : 1 } f(10)"),
+ nxt_string("3628800") },
+
+ /* Recursive fibonacci. */
+
+ { nxt_string("function fibo(n) { \
+ if (n > 1) \
+ return fibo(n-1) + fibo(n-2) \
+ return 1 \
+ } \
+ fibo(10)"),
+ nxt_string("89") },
+
+ { nxt_string("function fibo(n) { \
+ if (n > 1) \
+ return fibo(n-1) + fibo(n-2) \
+ return '.' \
+ } \
+ fibo(10).length"),
+ nxt_string("89") },
+
+ { nxt_string("function fibo(n) { \
+ if (n > 1) \
+ return fibo(n-1) + fibo(n-2) \
+ return 1 \
+ } \
+ fibo('10')"),
+ nxt_string("89") },
+
+ { nxt_string("function add(a, b) { return a + b } \
+ function mul(a, b) { return a * b } \
+ function f(a, b) { \
+ return a + mul(add(1, 2), add(2, 3)) + b \
+ } \
+ f(30, 70)"),
+ nxt_string("115") },
+
+ { nxt_string("function a(x, y) { return x + y } \
+ function b(x, y) { return x * y } \
+ a(3, b(4, 5))"),
+ nxt_string("23") },
+
+ { nxt_string("function x(n) { return n }; x('12'.substr(1))"),
+ nxt_string("2") },
+
+ { nxt_string("function f(a) { a *= 2 } f(10)"),
+ nxt_string("undefined") },
+
+ { nxt_string("function f() { return 5 } f()"),
+ nxt_string("5") },
+
+ { nxt_string("function f() { return 5 } f(1)"),
+ nxt_string("5") },
+
+ { nxt_string("function f() {} f()"),
+ nxt_string("undefined") },
+
+ { nxt_string("function f(a) { return a + 1 } b = f(2)"),
+ nxt_string("3") },
+
+ { nxt_string("f = function(a) { a *= 2; return a }; f(10)"),
+ nxt_string("20") },
+
+ { nxt_string("var f = function b(a) { a *= 2; return a }; f(10)"),
+ nxt_string("20") },
+
+ { nxt_string("var f = function b(a) { a *= 2; return a }; b(10)"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("var f = function(a) { a *= 2; return a }; f(10)"),
+ nxt_string("20") },
+
+ { nxt_string("f = function b(a) { a *= 2; return a }; b(10)"),
+ nxt_string("ReferenceError") },
+
+ { nxt_string("f = function b(a) { a *= 2; return a }; f(10)"),
+ nxt_string("20") },
+
+ { nxt_string("f = a = function(a) { a *= 2; return a }; f(10)"),
+ nxt_string("20") },
+
+ { nxt_string("f = a = function(a) { a *= 2; return a }; a(10)"),
+ nxt_string("20") },
+
+ { nxt_string("var f = function b(a) { a *= 2; return a } = 5"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("function a() { return { x:2} }; var b = a(); b.x"),
+ nxt_string("2") },
+
+ { nxt_string("a = {}; function f(a) { return a + 1 } a.b = f(2); a.b"),
+ nxt_string("3") },
+
+ { nxt_string("a = (function() { return 1 })(); a"),
+ nxt_string("1") },
+
+ { nxt_string("a = (function(a) { return a + 1 })(2); a"),
+ nxt_string("3") },
+
+ { nxt_string("a = (function(a) { return a + 1 }(2)); a"),
+ nxt_string("3") },
+
+ { nxt_string("a = !function(a) { return a + 1 }(2); a"),
+ nxt_string("false") },
+
+ { nxt_string("a = +function(a) { return a + 1 }(2); a"),
+ nxt_string("3") },
+
+ { nxt_string("a = true && function(a) { return a + 1 }(2); a"),
+ nxt_string("3") },
+
+ { nxt_string("a = 0, function(a) { return a + 1 }(2); a"),
+ nxt_string("0") },
+
+ { nxt_string("var o = { f: function(a) { return a * 2 } }; o.f(5)"),
+ nxt_string("10") },
+
+ { nxt_string("var o = {}; o.f = function(a) { return a * 2 }; o.f(5)"),
+ nxt_string("10") },
+
+ { nxt_string("var o = { x: 1, f: function() { return this.x } } o.f()"),
+ nxt_string("1") },
+
+ { nxt_string("var o = { x: 1, f: function(a) { return this.x += a } } \
+ o.f(5) +' '+ o.x"),
+ nxt_string("6 6") },
+
+ { nxt_string("var f = function(a) { return 3 } f.call()"),
+ nxt_string("3") },
+
+ { nxt_string("var f = function(a) { return this } f.call(5)"),
+ nxt_string("5") },
+
+ { nxt_string("var f = function(a, b) { return this + a } f.call(5, 1)"),
+ nxt_string("6") },
+
+ { nxt_string("var f = function(a, b) { return this + a + b } \
+ f.call(5, 1, 2)"),
+ nxt_string("8") },
+
+ { nxt_string("var f = function(a) { return 3 } f.apply()"),
+ nxt_string("3") },
+
+ { nxt_string("var f = function(a) { return this } f.apply(5)"),
+ nxt_string("5") },
+
+ { nxt_string("var f = function(a) { return this + a } f.apply(5, 1)"),
+ nxt_string("TypeError") },
+
+ { nxt_string("var f = function(a, b) { return this + a + b } \
+ f.apply(5, [1, 2])"),
+ nxt_string("8") },
+
+ { nxt_string("var f = function(a, b) { return this + a + b } \
+ f.apply(5, [1, 2], 3)"),
+ nxt_string("8") },
+
+ { nxt_string("''.concat.call()"),
+ nxt_string("TypeError") },
+
+ { nxt_string("''.concat.call('a', 'b', 'c')"),
+ nxt_string("abc") },
+
+ { nxt_string("''.concat.call('a')"),
+ nxt_string("a") },
+
+ { nxt_string("''.concat.call('a', [ 'b', 'c' ])"),
+ nxt_string("ab,c") },
+
+ { nxt_string("''.concat.call('a', [ 'b', 'c' ], 'd')"),
+ nxt_string("ab,cd") },
+
+ { nxt_string("''.concat.apply()"),
+ nxt_string("TypeError") },
+
+ { nxt_string("''.concat.apply('a')"),
+ nxt_string("a") },
+
+ { nxt_string("''.concat.apply('a', 'b')"),
+ nxt_string("TypeError") },
+
+ { nxt_string("''.concat.apply('a', [ 'b', 'c' ])"),
+ nxt_string("abc") },
+
+ { nxt_string("''.concat.apply('a', [ 'b', 'c' ], 'd')"),
+ nxt_string("abc") },
+
+ { nxt_string("[].join.call([1,2,3])"),
+ nxt_string("1,2,3") },
+
+ { nxt_string("[].join.call([1,2,3], ':')"),
+ nxt_string("1:2:3") },
+
+ { nxt_string("[].join.call()"),
+ nxt_string("TypeError") },
+
+ { nxt_string("function F(a, b) { this.a = a + b } \
+ var o = new F(1, 2) \
+ o.a"),
+ nxt_string("3") },
+
+ { nxt_string("function F(a, b) { this.a = a + b; return { a: 7 } } \
+ var o = new F(1, 2) \
+ o.a"),
+ nxt_string("7") },
+
+ { nxt_string("function a() { return function(x) { return x + 1 } } \
+ b = a(); b(2)"),
+ nxt_string("3") },
+
+ { nxt_string("/^$/.test('')"),
+ nxt_string("true") },
+
+ { nxt_string("var a = /\\d/; a.test('123')"),
+ nxt_string("true") },
+
+ { nxt_string("var a = /\\d/; a.test('abc')"),
+ nxt_string("false") },
+
+ { nxt_string("/\\d/.test('123')"),
+ nxt_string("true") },
+
+ { nxt_string("/\\d/.test('abc')"),
+ nxt_string("false") },
+
+ { nxt_string("/abc/i.test('ABC')"),
+ nxt_string("true") },
+
+ { nxt_string("/абв/i.test('АБВ')"),
+ nxt_string("true") },
+
+ /* TODO: '\u00C2\u00B6".bytes */
+
+ { nxt_string("/\xC2\xB6/.test('\xC3\x82\xC2\xB6'.bytes)"),
+ nxt_string("true") },
+
+ { nxt_string("/\\x80/.test('\x80'.bytes)"),
+ nxt_string("true") },
+
+ { nxt_string("var a = /^$/.exec(''); a.length +' '+ a"),
+ nxt_string("1 ") },
+
+ { nxt_string("var r = /бв/ig; var a = r.exec('АБВ'); r.lastIndex +' '+ a"),
+ nxt_string("3 БВ") },
+
+ { nxt_string("var r = /\\x80/g; r.exec('\x81\x80'.bytes); r.lastIndex"),
+ nxt_string("1") },
+
+ /*
+ * It seems that "/стоп/ig" fails on early PCRE versions.
+ * It fails at least in 8.1 and works at least in 8.31.
+ */
+
+ { nxt_string("var r = /Стоп/ig; \
+ var a = r.exec('АБВДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯСТОП'); \
+ r.lastIndex +' '+ a"),
+ nxt_string("35 СТОП") },
+
+ { nxt_string("var r = /quick\\s(brown).+?(jumps)/ig \
+ var a = r.exec('The Quick Brown Fox Jumps Over The Lazy Dog') \
+ a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3] +' '+ \
+ a.index +' '+ r.lastIndex +' '+ a.input"),
+ nxt_string("Quick Brown Fox Jumps Brown Jumps undefined 4 25 The Quick Brown Fox Jumps Over The Lazy Dog") },
+
+ { nxt_string("var s; var r = /./g; while (s = r.exec('abc')); s"),
+ nxt_string("null") },
+
+ { nxt_string("var r = /LS/i.exec(false); r[0]"),
+ nxt_string("ls") },
+
+ /* Non-standard ECMA-262 features. */
+
+ /* 0x10400 is not a surrogate pair of 0xD801 and 0xDC00. */
+
+ { nxt_string("var chars = '𐐀'; chars.length +' '+ chars.charCodeAt(0)"),
+ nxt_string("1 66560") },
+
+ /* es5id: 6.1, 0x104A0 is not a surrogate pair of 0xD801 and 0xDCA0. */
+
+ { nxt_string("var chars = '𐒠'; chars.length +' '+ chars.charCodeAt(0)"),
+ nxt_string("1 66720") },
+
+ /* Exceptions. */
+
+ { nxt_string("throw null"),
+ nxt_string("") },
+
+ { nxt_string("var a; try { throw null } catch (e) { a = e } a"),
+ nxt_string("null") },
+
+ { nxt_string("try { throw null } catch (e) { throw e }"),
+ nxt_string("") },
+
+ { nxt_string("var a = 0; try { a = 5 } \
+ catch (e) { a = 9 } finally { a++ } a"),
+ nxt_string("6") },
+
+ { nxt_string("var a = 0; try { throw 3 } \
+ catch (e) { a = e } finally { a++ } a"),
+ nxt_string("4") },
+
+ { nxt_string("var a = 0; try { throw 3 } \
+ catch (e) { throw e + 1 } finally { a++ }"),
+ nxt_string("") },
+
+ { nxt_string("var a = 0; try { throw 3 } \
+ catch (e) { a = e } finally { throw a }"),
+ nxt_string("") },
+
+ { nxt_string("try { throw null } catch (e) { } finally { }"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = 0; try { throw 3 } \
+ catch (e) { throw 4 } finally { throw a }"),
+ nxt_string("") },
+
+ { nxt_string("var a = 0; try { a = 5 } finally { a++ } a"),
+ nxt_string("6") },
+
+ { nxt_string("var a = 0; try { throw 5 } finally { a++ }"),
+ nxt_string("") },
+
+ { nxt_string("var a = 0; try { a = 5 } finally { throw 7 }"),
+ nxt_string("") },
+
+ { nxt_string("function f(a) { \
+ if (a > 1) return f(a - 1); \
+ throw 9; return a } \
+ var a = 0; try { a = f(5); a++ } catch(e) { a = e } a"),
+ nxt_string("9") },
+
+ { nxt_string("var a; try { try { throw 5 } catch (e) { a = e } throw 3 } \
+ catch(x) { a += x } a"),
+ nxt_string("8") },
+
+ { nxt_string("var o = { valueOf: function() { return '3' } }; --o"),
+ nxt_string("2") },
+
+ { nxt_string("var o = { valueOf: function() { return [3] } }; --o"),
+ nxt_string("NaN") },
+
+ { nxt_string("var o = { valueOf: function() { return '3' } } 10 - o"),
+ nxt_string("7") },
+
+ { nxt_string("var o = { valueOf: function() { return [3] } } 10 - o"),
+ nxt_string("NaN") },
+
+ { nxt_string("var o = { toString: function() { return 'OK' } } 'o:' + o"),
+ nxt_string("o:OK") },
+
+ { nxt_string("var o = { toString: function() { return [1] } } 'o:' + o"),
+ nxt_string("TypeError") },
+
+ { nxt_string("var a = { valueOf: function() { return '3' } } \
+ var b = { toString: function() { return 10 - a + 'OK' } } \
+ var c = { toString: function() { return b + 'YES' } } \
+ 'c:' + c"),
+ nxt_string("c:7OKYES") },
+
+ { nxt_string("[1,2,3].valueOf()"),
+ nxt_string("1,2,3") },
+
+ { nxt_string("var o = { valueOf: function() { return 'OK' } } o.valueOf()"),
+ nxt_string("OK") },
+
+ { nxt_string("[].__proto__ === [1,2].__proto__"),
+ nxt_string("true") },
+
+ { nxt_string("/./.__proto__ === /a/.__proto__"),
+ nxt_string("true") },
+
+ { nxt_string("''.__proto__ === 'abc'.__proto__"),
+ nxt_string("true") },
+
+ { nxt_string("[].__proto__.join.call([1,2,3], ':')"),
+ nxt_string("1:2:3") },
+
+ { nxt_string("''.__proto__.concat.call('a', 'b', 'c')"),
+ nxt_string("abc") },
+
+ { nxt_string("/./.__proto__.test.call(/a{2}/, 'aaa')"),
+ nxt_string("true") },
+
+ { nxt_string("({}) instanceof Object"),
+ nxt_string("true") },
+
+ { nxt_string("[] instanceof Array"),
+ nxt_string("true") },
+
+ { nxt_string("[] instanceof Object"),
+ nxt_string("true") },
+
+ { nxt_string("var o = Object(); o"),
+ nxt_string("[object Object]") },
+
+ { nxt_string("var o = new Object(); o"),
+ nxt_string("[object Object]") },
+
+ { nxt_string("var o = new Object(1); o +''"),
+ nxt_string("1") },
+
+ { nxt_string("var o = {}; o === Object(o)"),
+ nxt_string("true") },
+
+ { nxt_string("var o = {}; o === new Object(o)"),
+ nxt_string("true") },
+
+ { nxt_string("Object.name"),
+ nxt_string("Object") },
+
+ { nxt_string("Object.prototype.constructor === Object"),
+ nxt_string("true") },
+
+ { nxt_string("Object.constructor === Function"),
+ nxt_string("true") },
+
+ { nxt_string("var a = Array(3); a +''"),
+ nxt_string(",,") },
+
+ { nxt_string("var a = Array(); a.length"),
+ nxt_string("0") },
+
+ { nxt_string("var a = Array(0); a.length"),
+ nxt_string("0") },
+
+ { nxt_string("var a = Array(true); a +''"),
+ nxt_string("true") },
+
+ { nxt_string("var a = Array(1,'two',3); a +''"),
+ nxt_string("1,two,3") },
+
+ { nxt_string("var a = Array(-1)"),
+ nxt_string("RangeError") },
+
+ { nxt_string("var a = Array(2.5)"),
+ nxt_string("RangeError") },
+
+ { nxt_string("var a = Array(NaN)"),
+ nxt_string("RangeError") },
+
+ { nxt_string("var a = Array(Infinity)"),
+ nxt_string("RangeError") },
+
+ { nxt_string("var a = new Array(3); a +''"),
+ nxt_string(",,") },
+
+ { nxt_string("Array.name"),
+ nxt_string("Array") },
+
+ { nxt_string("Array.length"),
+ nxt_string("1") },
+
+ { nxt_string("Array.prototype.constructor === Array"),
+ nxt_string("true") },
+
+ { nxt_string("Array.constructor === Function"),
+ nxt_string("true") },
+
+ { nxt_string("var a = []; a.join = 'OK'; a +''"),
+ nxt_string("[object Array]") },
+
+ { nxt_string("[].__proto__ === Array.prototype"),
+ nxt_string("true") },
+
+ { nxt_string("[].__proto__.constructor === Array"),
+ nxt_string("true") },
+
+ { nxt_string("[].constructor === Array"),
+ nxt_string("true") },
+
+ /* TODO: Boolean */
+
+ { nxt_string("Number()"),
+ nxt_string("0") },
+
+ { nxt_string("typeof Number(1)"),
+ nxt_string("number") },
+
+ { nxt_string("typeof new Number(1)"),
+ nxt_string("object") },
+
+ { nxt_string("Number.name"),
+ nxt_string("Number") },
+
+ { nxt_string("Number.length"),
+ nxt_string("1") },
+
+ { nxt_string("Number.prototype.constructor === Number"),
+ nxt_string("true") },
+
+ { nxt_string("Number.constructor === Function"),
+ nxt_string("true") },
+
+ { nxt_string("String()"),
+ nxt_string("") },
+
+ { nxt_string("typeof String('abc')"),
+ nxt_string("string") },
+
+ { nxt_string("typeof new String('abc')"),
+ nxt_string("object") },
+
+ { nxt_string("String.name"),
+ nxt_string("String") },
+
+ { nxt_string("String.length"),
+ nxt_string("1") },
+
+ { nxt_string("String.prototype.length"),
+ nxt_string("0") },
+
+ { nxt_string("String.prototype.constructor === String"),
+ nxt_string("true") },
+
+ { nxt_string("String.constructor === Function"),
+ nxt_string("true") },
+
+ { nxt_string("'test'.__proto__ === String.prototype"),
+ nxt_string("true") },
+
+ { nxt_string("'test'.constructor === String"),
+ nxt_string("true") },
+
+ { nxt_string("'test'.constructor.prototype === String.prototype"),
+ nxt_string("true") },
+
+ { nxt_string("Function.name"),
+ nxt_string("Function") },
+
+ { nxt_string("Function.length"),
+ nxt_string("0") },
+
+ { nxt_string("Function.prototype.constructor === Function"),
+ nxt_string("true") },
+
+ { nxt_string("Function.constructor === Function"),
+ nxt_string("true") },
+
+ { nxt_string("RegExp.name"),
+ nxt_string("RegExp") },
+
+ { nxt_string("RegExp.length"),
+ nxt_string("2") },
+
+ { nxt_string("RegExp.prototype.constructor === RegExp"),
+ nxt_string("true") },
+
+ { nxt_string("RegExp.constructor === Function"),
+ nxt_string("true") },
+
+#if 0
+ { nxt_string("Object.prototype.toString.call()"),
+ nxt_string("[object Undefined]") },
+#endif
+
+ { nxt_string("Object.prototype.toString.call(undefined)"),
+ nxt_string("[object Undefined]") },
+
+ { nxt_string("Object.prototype.toString.call(null)"),
+ nxt_string("[object Null]") },
+
+ { nxt_string("Object.prototype.toString.call(true)"),
+ nxt_string("[object Boolean]") },
+
+ { nxt_string("Object.prototype.toString.call(1)"),
+ nxt_string("[object Number]") },
+
+ { nxt_string("Object.prototype.toString.call('')"),
+ nxt_string("[object String]") },
+
+ { nxt_string("Object.prototype.toString.call({})"),
+ nxt_string("[object Object]") },
+
+ { nxt_string("Object.prototype.toString.call([])"),
+ nxt_string("[object Array]") },
+
+ { nxt_string("Object.prototype.toString.call(new Object(true))"),
+ nxt_string("[object Boolean]") },
+
+ { nxt_string("Object.prototype.toString.call(new Number(1))"),
+ nxt_string("[object Number]") },
+
+ { nxt_string("Object.prototype.toString.call(new Object(1))"),
+ nxt_string("[object Number]") },
+
+ { nxt_string("Object.prototype.toString.call(new Object(''))"),
+ nxt_string("[object String]") },
+
+ { nxt_string("Object.prototype.toString.call(function(){})"),
+ nxt_string("[object Function]") },
+
+ { nxt_string("Object.prototype.toString.call(/./)"),
+ nxt_string("[object RegExp]") },
+
+ { nxt_string("var p = { a:5 }; var o = Object.create(p); o.a"),
+ nxt_string("5") },
+
+ { nxt_string("var p = { a:5 }; var o = Object.create(p); \
+ o.__proto__ === p"),
+ nxt_string("true") },
+
+ { nxt_string("var o = Object.create(Object.prototype); \
+ o.__proto__ === Object.prototype"),
+ nxt_string("true") },
+
+ { nxt_string("var o = Object.create(null); '__proto__' in o"),
+ nxt_string("false") },
+
+ /* es5id: 8.2_A1_T1 */
+ /* es5id: 8.2_A1_T2 */
+
+ { nxt_string("var x = null;"),
+ nxt_string("") },
+
+#if 0
+ /* es5id: 8.2_A2 */
+
+ { nxt_string("var null;"),
+ nxt_string("SyntaxError") },
+#endif
+
+ /* es5id: 8.2_A3 */
+
+ { nxt_string("typeof(null) === \"object\""),
+ nxt_string("true") },
+
+#endif /* NXT_FIBOBENCH */
+
+};
+
+
+typedef struct {
+ nxt_mem_cache_pool_t *mem_cache_pool;
+ nxt_str_t uri;
+} nxt_jscript_unit_test_req;
+
+
+static njs_ret_t
+nxt_jscript_unit_test_r_get_uri_external(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data)
+{
+ nxt_jscript_unit_test_req *r;
+
+ r = (nxt_jscript_unit_test_req *) obj;
+
+ return njs_string_create(vm, value, r->uri.data, r->uri.len, 0);
+}
+
+
+static njs_ret_t
+nxt_jscript_unit_test_r_set_uri_external(njs_vm_t *vm,
+ void *obj, uintptr_t data, nxt_str_t *value)
+{
+ nxt_jscript_unit_test_req *r;
+
+ r = (nxt_jscript_unit_test_req *) obj;
+ r->uri = *value;
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+nxt_jscript_unit_test_host_external(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data)
+{
+ return njs_string_create(vm, value, (u_char *) "АБВГДЕЁЖЗИЙ", 22, 0);
+}
+
+
+static njs_ret_t
+nxt_jscript_unit_test_header_external(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data)
+{
+ u_char *s, *p;
+ uint32_t size;
+ nxt_str_t *h;
+ nxt_jscript_unit_test_req *r;
+
+ r = (nxt_jscript_unit_test_req *) obj;
+ h = (nxt_str_t *) data;
+
+ size = 7 + h->len;
+
+ s = nxt_mem_cache_alloc(r->mem_cache_pool, size);
+ if (nxt_slow_path(s == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = memcpy(s, h->data, h->len);
+ p += h->len;
+ *p++ = '|';
+ memcpy(p, "АБВ", 6);
+
+ return njs_string_create(vm, value, s, size, 0);
+}
+
+
+static njs_ret_t
+nxt_jscript_unit_test_header_each_start_external(njs_vm_t *vm, void *obj,
+ void *each)
+{
+ u_char *s;
+
+ s = each;
+ s[0] = '0';
+ s[1] = '0';
+
+ return NXT_OK;
+}
+
+
+static njs_ret_t
+nxt_jscript_unit_test_header_each_external(njs_vm_t *vm, njs_value_t *value,
+ void *obj, void *each)
+{
+ u_char *s;
+
+ s = each;
+ s[1]++;
+
+ if (s[1] == '4') {
+ return NXT_DONE;
+ }
+
+ return njs_string_create(vm, value, s, 2, 0);
+}
+
+
+static njs_ret_t
+nxt_jscript_unit_test_undefined_external(njs_vm_t *vm, njs_value_t *value,
+ void *obj, uintptr_t data)
+{
+ njs_void_set(value);
+
+ return NJS_OK;
+}
+
+
+static njs_external_t nxt_test_r_external[] = {
+
+ { nxt_string("uri"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ nxt_jscript_unit_test_r_get_uri_external,
+ nxt_jscript_unit_test_r_set_uri_external,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("host"),
+ NJS_EXTERN_PROPERTY,
+ NULL,
+ 0,
+ nxt_jscript_unit_test_host_external,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+ { nxt_string("header"),
+ NJS_EXTERN_OBJECT,
+ NULL,
+ 0,
+ nxt_jscript_unit_test_header_external,
+ NULL,
+ NULL,
+ nxt_jscript_unit_test_header_each_start_external,
+ nxt_jscript_unit_test_header_each_external,
+ NULL,
+ 0 },
+
+};
+
+
+static njs_external_t nxt_test_external[] = {
+
+ { nxt_string("$r"),
+ NJS_EXTERN_OBJECT,
+ nxt_test_r_external,
+ nxt_nitems(nxt_test_r_external),
+ nxt_jscript_unit_test_undefined_external,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0 },
+
+};
+
+
+static nxt_int_t
+nxt_jscript_unit_test_externals(nxt_lvlhsh_t *externals, nxt_mem_cache_pool_t *mcp)
+{
+ nxt_lvlhsh_init(externals);
+
+ return njs_add_external(externals, mcp, 0, nxt_test_external,
+ nxt_nitems(nxt_test_external));
+}
+
+
+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) {
+ memset(p, 0, 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);
+}
+
+
+static const nxt_mem_proto_t nxt_jscript_mem_cache_pool_proto = {
+ njs_alloc,
+ njs_zalloc,
+ njs_align,
+ NULL,
+ njs_free,
+ NULL,
+ NULL,
+};
+
+
+static nxt_int_t
+nxt_jscript_unit_test(void)
+{
+ void *ext_object;
+ u_char *start;
+ njs_vm_t *vm, *nvm;
+ nxt_int_t ret;
+ nxt_str_t s;
+ nxt_bool_t ok;
+ nxt_uint_t i;
+ nxt_lvlhsh_t externals;
+ njs_vm_shared_t *shared;
+ nxt_mem_cache_pool_t *mcp;
+ nxt_jscript_unit_test_req r;
+
+ shared = NULL;
+
+ mcp = nxt_mem_cache_pool_create(&nxt_jscript_mem_cache_pool_proto, NULL, NULL,
+ 2 * nxt_pagesize(), 128, 512, 16);
+ if (nxt_slow_path(mcp == NULL)) {
+ return NXT_ERROR;
+ }
+
+ r.mem_cache_pool = mcp;
+ r.uri.len = 6;
+ r.uri.data = (u_char *) "АБВ";
+
+ ext_object = &r;
+
+ if (nxt_jscript_unit_test_externals(&externals, mcp) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ for (i = 0; i < nxt_nitems(js_test); i++) {
+
+#if 1
+ printf("\"%.*s\"\n",
+ (int) js_test[i].script.len, js_test[i].script.data);
+#endif
+
+ vm = njs_vm_create(mcp, &shared, &externals);
+ if (vm == NULL) {
+ return NXT_ERROR;
+ }
+
+ start = js_test[i].script.data;
+
+ ret = njs_vm_compile(vm, &start, start + js_test[i].script.len);
+
+ if (ret == NXT_OK) {
+ nvm = njs_vm_clone(vm, NULL, &ext_object);
+ if (nvm == NULL) {
+ return NXT_ERROR;
+ }
+
+ if (njs_vm_run(nvm) == NXT_OK) {
+ if (njs_vm_retval(nvm, &s) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ } else {
+ njs_vm_exception(nvm, &s);
+ }
+
+ } else {
+ njs_vm_exception(vm, &s);
+ }
+
+ ok = nxt_strstr_eq(&js_test[i].ret, &s);
+
+ if (!ok) {
+ printf("jscript(\"%.*s\") failed: \"%.*s\" vs \"%.*s\"\n",
+ (int) js_test[i].script.len, js_test[i].script.data,
+ (int) js_test[i].ret.len, js_test[i].ret.data,
+ (int) s.len, s.data);
+
+ return NXT_ERROR;
+ }
+ }
+
+ nxt_mem_cache_pool_destroy(mcp);
+
+ printf("jscript unit tests passed\n");
+
+ return NXT_OK;
+}
+
+
+int nxt_cdecl
+main(int argc, char **argv)
+{
+ return nxt_jscript_unit_test();
+}
--- /dev/null
+
+NXT_LIB = nxt
+
+
+$(NXT_BUILDDIR)/libnxt.a: \
+ $(NXT_BUILDDIR)/nxt_djb_hash.o \
+ $(NXT_BUILDDIR)/nxt_utf8.o \
+ $(NXT_BUILDDIR)/nxt_array.o \
+ $(NXT_BUILDDIR)/nxt_queue.o \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_lvlhsh.o \
+ $(NXT_BUILDDIR)/nxt_malloc.o \
+ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
+
+ ar -r -c $(NXT_BUILDDIR)/libnxt.a \
+ $(NXT_BUILDDIR)/nxt_djb_hash.o \
+ $(NXT_BUILDDIR)/nxt_utf8.o \
+ $(NXT_BUILDDIR)/nxt_array.o \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_lvlhsh.o \
+ $(NXT_BUILDDIR)/nxt_malloc.o \
+ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
+
+$(NXT_BUILDDIR)/nxt_murmur_hash.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_murmur_hash.h \
+ $(NXT_LIB)/nxt_murmur_hash.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_murmur_hash.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_murmur_hash.c
+
+$(NXT_BUILDDIR)/nxt_djb_hash.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_djb_hash.h \
+ $(NXT_LIB)/nxt_djb_hash.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_djb_hash.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_djb_hash.c
+
+$(NXT_BUILDDIR)/nxt_utf8.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_utf8.h \
+ $(NXT_LIB)/nxt_utf8.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_utf8.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_utf8.c
+
+$(NXT_BUILDDIR)/nxt_array.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_array.h \
+ $(NXT_LIB)/nxt_array.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_array.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_array.c
+
+$(NXT_BUILDDIR)/nxt_queue.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_queue.h \
+ $(NXT_LIB)/nxt_queue.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_queue.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_queue.c
+
+$(NXT_BUILDDIR)/nxt_rbtree.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_rbtree.h \
+ $(NXT_LIB)/nxt_rbtree.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_rbtree.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_rbtree.c
+
+$(NXT_BUILDDIR)/nxt_lvlhsh.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_lvlhsh.h \
+ $(NXT_LIB)/nxt_lvlhsh.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_lvlhsh.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_lvlhsh.c
+
+$(NXT_BUILDDIR)/nxt_malloc.o: \
+ $(NXT_LIB)/nxt_auto_config.h \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_malloc.h \
+ $(NXT_LIB)/nxt_malloc.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_malloc.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_malloc.c
+
+$(NXT_BUILDDIR)/nxt_mem_cache_pool.o: \
+ $(NXT_LIB)/nxt_types.h \
+ $(NXT_LIB)/nxt_clang.h \
+ $(NXT_LIB)/nxt_alignment.h \
+ $(NXT_LIB)/nxt_queue.h \
+ $(NXT_LIB)/nxt_rbtree.h \
+ $(NXT_LIB)/nxt_mem_cache_pool.h \
+ $(NXT_LIB)/nxt_mem_cache_pool.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_mem_cache_pool.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/nxt_mem_cache_pool.c
+
+include $(NXT_LIB)/test/Makefile
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+$nxt_echo checking for C compiler: $CC
+cat << END >> $NXT_AUTOCONF_ERR
+----------------------------------------
+checking for C compiler: $CC
+END
+
+
+# Allow error exit status.
+set +e
+
+if [ -z `which $CC` ]; then
+ $nxt_echo
+ $nxt_echo $0: error: $CC not found.
+ $nxt_echo
+ exit 1;
+fi
+
+
+if `/bin/sh -c "($CC -v)" 2>&1 | grep "gcc version" >> $NXT_AUTOCONF_ERR 2>&1`
+then
+ NXT_CC_NAME=gcc
+ $nxt_echo " + using GNU C compiler"
+ NXT_CC_VERSION=`/bin/sh -c "($CC -v)" 2>&1 | grep "gcc version" 2>&1`
+ $nxt_echo " + $NXT_CC_VERSION"
+
+else
+if `/bin/sh -c "($CC -v)" 2>&1 | grep "clang version" >> $NXT_AUTOCONF_ERR 2>&1`
+then
+ NXT_CC_NAME=clang
+ $nxt_echo " + using Clang C compiler"
+ NXT_CC_VERSION=`/bin/sh -c "($CC -v)" 2>&1 | grep "clang version" 2>&1`
+ $nxt_echo " + $NXT_CC_VERSION"
+
+else
+if `/bin/sh -c "($CC -v)" 2>&1 \
+ | grep "Apple LLVM version" >> $NXT_AUTOCONF_ERR 2>&1`
+then
+ NXT_CC_NAME=clang
+ $nxt_echo " + using Clang C compiler"
+ NXT_CC_VERSION=`/bin/sh -c "($CC -v)" 2>&1 | grep "Apple LLVM version" 2>&1`
+ $nxt_echo " + $NXT_CC_VERSION"
+
+else
+if `/bin/sh -c "($CC -V)" 2>&1 | grep "Sun C" >> $NXT_AUTOCONF_ERR 2>&1`
+then
+ NXT_CC_NAME=SunC
+ $nxt_echo " + using Sun C compiler"
+ NXT_CC_VERSION=`/bin/sh -c "($CC -V)" 2>&1 | grep "Sun C" 2>&1`
+ $nxt_echo " + $NXT_CC_VERSION"
+
+fi # SunC
+fi # Apple LLVM clang
+fi # clang
+fi # gcc
+
+
+case $NXT_CC_NAME in
+
+ gcc)
+ nxt_define=NXT_GCC . ${NXT_AUTO}define
+
+ NXT_CFLAGS="$NXT_CFLAGS -pipe"
+ NXT_CFLAGS="$NXT_CFLAGS -fPIC"
+
+ # Do not export symbols except explicitly marked with NXT_EXPORT.
+ NXT_CFLAGS="$NXT_CFLAGS -fvisibility=hidden"
+
+ # c99/gnu99 conflict with Solaris XOPEN.
+ #NXT_CFLAGS="$NXT_CFLAGS -std=gnu99"
+
+ NXT_CFLAGS="$NXT_CFLAGS -O"
+ #NXT_CFLAGS="$NXT_CFLAGS -O0"
+ NXT_CFLAGS="$NXT_CFLAGS -W -Wall -Wextra"
+
+ #NXT_CFLAGS="$NXT_CFLAGS -Wunused-result"
+ NXT_CFLAGS="$NXT_CFLAGS -Wno-unused-parameter"
+ #NXT_CFLAGS="$NXT_CFLAGS -Wshorten-64-to-32"
+ NXT_CFLAGS="$NXT_CFLAGS -Wwrite-strings"
+
+ # -O2 enables -fstrict-aliasing and -fstrict-overflow.
+ #NXT_CFLAGS="$NXT_CFLAGS -O2"
+ #NXT_CFLAGS="$NXT_CFLAGS -Wno-strict-aliasing"
+
+ #NXT_CFLAGS="$NXT_CFLAGS -fomit-frame-pointer"
+ #NXT_CFLAGS="$NXT_CFLAGS -momit-leaf-frame-pointer"
+
+ # -Wstrict-overflow is supported by GCC 4.2+.
+ #NXT_CFLAGS="$NXT_CFLAGS -Wstrict-overflow=5"
+
+ NXT_CFLAGS="$NXT_CFLAGS -Wmissing-prototypes"
+
+ # Stop on warning.
+ NXT_CFLAGS="$NXT_CFLAGS -Werror"
+
+ # Debug.
+ NXT_CFLAGS="$NXT_CFLAGS -g"
+ ;;
+
+ clang)
+ nxt_define=NXT_CLANG . ${NXT_AUTO}define
+
+ NXT_CFLAGS="$NXT_CFLAGS -pipe"
+ NXT_CFLAGS="$NXT_CFLAGS -fPIC"
+
+ # Do not export symbols except explicitly marked with NXT_EXPORT.
+ NXT_CFLAGS="$NXT_CFLAGS -fvisibility=hidden"
+
+ NXT_CFLAGS="$NXT_CFLAGS -O"
+ #NXT_CFLAGS="$NXT_CFLAGS -O0"
+ NXT_CFLAGS="$NXT_CFLAGS -W -Wall -Wextra"
+
+ #NXT_CFLAGS="$NXT_CFLAGS -Wunused-result"
+ NXT_CFLAGS="$NXT_CFLAGS -Wno-unused-parameter"
+ #NXT_CFLAGS="$NXT_CFLAGS -Wshorten-64-to-32"
+ NXT_CFLAGS="$NXT_CFLAGS -Wwrite-strings"
+ #NXT_CFLAGS="$NXT_CFLAGS -O2"
+ #NXT_CFLAGS="$NXT_CFLAGS -fomit-frame-pointer"
+ NXT_CFLAGS="$NXT_CFLAGS -fstrict-aliasing"
+ NXT_CFLAGS="$NXT_CFLAGS -Wstrict-overflow=5"
+
+ NXT_CFLAGS="$NXT_CFLAGS -Wmissing-prototypes"
+
+ # Stop on warning.
+ NXT_CFLAGS="$NXT_CFLAGS -Werror"
+
+ # Debug.
+
+ if [ "$NXT_SYSTEM_PLATFORM" != "powerpc" ]; then
+ # "-g" flag causes the "unknown pseudo-op: `.cfi_sections'"
+ # error on PowerPC Clang.
+ NXT_CFLAGS="$NXT_CFLAGS -g"
+ fi
+ ;;
+
+ SunC)
+ nxt_define=NXT_SUNC . ${NXT_AUTO}define
+
+ NXT_CFLAGS="$NXT_CFLAGS -fPIC"
+ # Optimization.
+ NXT_CFLAGS="$NXT_CFLAGS -O -fast"
+ # Stop on warning.
+ NXT_CFLAGS="$NXT_CFLAGS -errwarn=%all"
+ # Debug.
+ NXT_CFLAGS="$NXT_CFLAGS -g"
+ ;;
+
+ *)
+ ;;
+
+esac
+
+# Stop on error exit status again.
+set -e
+
+cat << END >> $NXT_MAKEFILE_CONF
+
+NXT_CC = ${CC}
+NXT_CFLAGS = ${CFLAGS} ${NXT_CFLAGS}
+END
--- /dev/null
+#!/bin/sh
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+# Disable localized program messages.
+LANG=C
+export LANG
+
+# Stop on error exit status.
+set -e
+# Stop on uninitialized variable.
+set -u
+
+
+# Initialize variables with null values if they are not defined.
+CFLAGS=${CFLAGS=}
+NXT_TEST_CFLAGS=${NXT_TEST_CFLAGS=}
+NXT_TEST_LIBS=${NXT_TEST_LIBS=}
+
+
+# Initialize variables with default if they are not defined.
+CC=${CC:-cc}
+NXT_CFLAGS=${NXT_CFLAGS=}
+NXT_CC_OPT=${NXT_CC_OPT:--O}
+NXT_LD_OPT=${NXT_CC_OPT:--O}
+NXT_AUTO=${NXT_AUTO:-auto/}
+NXT_AUTO_CONFIG_H=nxt_auto_config.h
+NXT_MAKEFILE_CONF=Makefile.conf
+
+NXT_BUILDDIR=${NXT_BUILDDIR:-build}
+NXT_AUTOTEST=$NXT_BUILDDIR/autotest
+NXT_AUTOCONF_ERR=$NXT_BUILDDIR/autoconf.err
+
+test -d $NXT_BUILDDIR || mkdir $NXT_BUILDDIR
+
+> $NXT_AUTOCONF_ERR
+
+cat << END > $NXT_AUTO_CONFIG_H
+
+/* This file is auto-generated by configure */
+
+END
+
+cat << END > $NXT_MAKEFILE_CONF
+
+# This file is auto-generated by configure
+END
+
+
+. ${NXT_AUTO}os
+. ${NXT_AUTO}clang
+. ${NXT_AUTO}memalign
+. ${NXT_AUTO}pcre
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+cat << END >> $NXT_AUTO_CONFIG_H
+
+#ifndef $nxt_define
+#define $nxt_define 1
+#endif
+
+END
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+if [ "x$1" = x-n ]; then
+ shift
+ echo "$*\c"
+
+else
+ echo "$*"
+fi
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+$nxt_echo -n "checking for $nxt_feature ..."
+
+cat << END >> $NXT_AUTOCONF_ERR
+----------------------------------------
+checking for $nxt_feature
+END
+
+
+nxt_found=no
+nxt_feature_value=
+nxt_feature_inc_path=
+
+if test -n "$nxt_feature_incs"; then
+ case "$nxt_feature_incs" in
+ -*)
+ nxt_feature_inc_path="$nxt_feature_incs"
+ ;;
+
+ *)
+ for nxt_temp in $nxt_feature_incs; do
+ nxt_feature_inc_path="$nxt_feature_inc_path -I $nxt_temp"
+ done
+ ;;
+ esac
+fi
+
+
+cat << END > $NXT_AUTOTEST.c
+$nxt_feature_test
+END
+
+
+nxt_test="$CC $CFLAGS $NXT_CFLAGS $NXT_CC_OPT $NXT_TEST_CFLAGS \
+ $nxt_feature_inc_path -o $NXT_AUTOTEST $NXT_AUTOTEST.c \
+ $NXT_LD_OPT $NXT_TEST_LIBS $nxt_feature_libs"
+
+# /bin/sh -c "(...)" is to intercept "Killed", "Abort trap",
+# "Segmentation fault", or other shell messages.
+# "|| true" is to bypass "set -e" setting.
+
+/bin/sh -c "($nxt_test || true)" >> $NXT_AUTOCONF_ERR 2>&1
+
+
+if [ -x $NXT_AUTOTEST ]; then
+
+ case "$nxt_feature_run" in
+
+ value)
+ if /bin/sh -c "($NXT_AUTOTEST)" >> $NXT_AUTOCONF_ERR 2>&1; then
+ $nxt_echo >> $NXT_AUTOCONF_ERR
+ nxt_found=yes
+ nxt_feature_value=`$NXT_AUTOTEST`
+ $nxt_echo " $nxt_feature_value"
+ if [ -n "$nxt_feature_name" ]; then
+ cat << END >> $NXT_AUTO_CONFIG_H
+
+#ifndef $nxt_feature_name
+#define $nxt_feature_name $nxt_feature_value
+#endif
+
+END
+ fi
+ else
+ $nxt_echo " not found"
+ fi
+ ;;
+
+ yes)
+ if /bin/sh -c "($NXT_AUTOTEST)" >> $NXT_AUTOCONF_ERR 2>&1; then
+ $nxt_echo " found"
+ nxt_found=yes
+ cat << END >> $NXT_AUTO_CONFIG_H
+
+#ifndef $nxt_feature_name
+#define $nxt_feature_name 1
+#endif
+
+END
+ else
+ $nxt_echo " found but is not working"
+ fi
+ ;;
+
+ *)
+ $nxt_echo " found"
+ nxt_found=yes
+ cat << END >> $NXT_AUTO_CONFIG_H
+
+#ifndef $nxt_feature_name
+#define $nxt_feature_name 1
+#endif
+
+END
+ ;;
+ esac
+
+else
+ $nxt_echo " not found"
+
+ $nxt_echo "----------" >> $NXT_AUTOCONF_ERR
+ cat $NXT_AUTOTEST.c >> $NXT_AUTOCONF_ERR
+ $nxt_echo "----------" >> $NXT_AUTOCONF_ERR
+ $nxt_echo $nxt_test >> $NXT_AUTOCONF_ERR
+ $nxt_echo "----------" >> $NXT_AUTOCONF_ERR
+fi
+
+rm -rf $NXT_AUTOTEST*
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+# Linux glibc 2.1.91, FreeBSD 7.0, Solaris 11,
+# MacOSX 10.6 (Snow Leopard), NetBSD 5.0.
+
+nxt_feature="posix_memalign()"
+nxt_feature_name=NXT_HAVE_POSIX_MEMALIGN
+nxt_feature_run=yes
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <stdlib.h>
+
+ int main() {
+ void *p;
+
+ if (posix_memalign(&p, 4096, 4096) != 0)
+ return 1;
+ return 0;
+ }"
+. ${NXT_AUTO}feature
+
+
+if [ $nxt_found = no ]; then
+
+ # Solaris, HP-UX.
+
+ nxt_feature="memalign()"
+ nxt_feature_name=NXT_HAVE_MEMALIGN
+ nxt_feature_run=yes
+ nxt_feature_incs=
+ nxt_feature_libs=
+ nxt_feature_test="#include <stdlib.h>
+
+ int main() {
+ if (memalign(4096, 4096) == NULL)
+ return 1;
+ return 0;
+ }"
+ . ${NXT_AUTO}feature
+fi
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+NXT_SYSTEM=`uname -s 2>/dev/null`
+
+
+case "$NXT_SYSTEM" in
+
+ Linux)
+ NXT_SYSTEM_VERSION=`uname -r 2>/dev/null`
+ # Linux uname -p can return "unknown".
+ NXT_SYSTEM_PLATFORM=`uname -m 2>/dev/null`
+ nxt_echo=echo
+ CC=${CC:-cc}
+
+ # NAN and INFINITY require _GNU_SOURCE on old Linux.
+ NXT_CFLAGS="$NXT_CFLAGS -D_GNU_SOURCE"
+ ;;
+
+ FreeBSD | NetBSD | OpenBSD)
+ NXT_SYSTEM_VERSION=`uname -r 2>/dev/null`
+ NXT_SYSTEM_PLATFORM=`uname -m 2>/dev/null`
+ nxt_echo=echo
+ CC=${CC:-cc}
+ ;;
+
+ SunOS)
+ nxt_define=NXT_SOLARIS . ${NXT_AUTO}define
+ NXT_SYSTEM_VERSION=`uname -r 2>/dev/null`
+ NXT_SYSTEM_PLATFORM=`uname -p 2>/dev/null`
+ # Solaris /bin/sh and /bin/echo do not support "-n" option.
+ nxt_echo=auto/echo
+ CC=${CC:-gcc}
+ ;;
+
+ Darwin)
+ NXT_SYSTEM_VERSION=`uname -r 2>/dev/null`
+ NXT_SYSTEM_PLATFORM=`uname -m 2>/dev/null`
+ # MacOSX /bin/sh is bash and its embedded "echo" command
+ # does not support "-n" option.
+ nxt_echo=/bin/echo
+ CC=${CC:-cc}
+ ;;
+
+ *)
+ NXT_SYSTEM_VERSION=`uname -r 2>/dev/null`
+ NXT_SYSTEM_PLATFORM=`uname -p 2>/dev/null`
+ nxt_echo=echo
+ CC=${CC:-gcc}
+ ;;
+
+esac
+
+$nxt_echo configuring for $NXT_SYSTEM $NXT_SYSTEM_VERSION $NXT_SYSTEM_PLATFORM
--- /dev/null
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+NXT_PCRE_CFLAGS=
+NXT_PCRE_LIB=
+
+nxt_found=no
+
+if /bin/sh -c "(pcre-config --version)" >> $NXT_AUTOCONF_ERR 2>&1; then
+
+ NXT_PCRE_CFLAGS=`pcre-config --cflags`
+ NXT_PCRE_LIB=`pcre-config --libs`
+
+ nxt_feature="PCRE library"
+ nxt_feature_name=NXT_HAVE_PCRE
+ nxt_feature_run=no
+ nxt_feature_incs=$NXT_PCRE_CFLAGS
+ nxt_feature_libs=$NXT_PCRE_LIB
+ nxt_feature_test="#include <pcre.h>
+
+ int main() {
+ pcre *re;
+
+ re = pcre_compile(NULL, 0, NULL, 0, NULL);
+ if (re == NULL)
+ return 1;
+ return 0;
+ }"
+ . ${NXT_AUTO}feature
+fi
+
+if [ $nxt_found = no ]; then
+ $nxt_echo
+ $nxt_echo $0: error: no PCRE library found.
+ $nxt_echo
+ exit 1;
+fi
+
+$nxt_echo " + PCRE version: `pcre-config --version`"
+
+cat << END >> $NXT_MAKEFILE_CONF
+
+NXT_PCRE_CFLAGS = ${NXT_PCRE_CFLAGS}
+NXT_PCRE_LIB = ${NXT_PCRE_LIB}
+END
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_ALIGNMENT_H_INCLUDED_
+#define _NXT_ALIGNMENT_H_INCLUDED_
+
+
+#ifndef NXT_MAX_ALIGNMENT
+
+#if (NXT_SOLARIS)
+/* x86_64: 16, i386: 4, sparcv9: 16, sparcv8: 8. */
+#define NXT_MAX_ALIGNMENT _MAX_ALIGNMENT
+
+#elif (NXT_WINDOWS)
+/* Win64: 16, Win32: 8. */
+#define NXT_MAX_ALIGNMENT MEMORY_ALLOCATION_ALIGNMENT
+
+#elif (__amd64__)
+#define NXT_MAX_ALIGNMENT 16
+
+#elif (__i386__ || __i386)
+#define NXT_MAX_ALIGNMENT 4
+
+#elif (__arm__)
+#define NXT_MAX_ALIGNMENT 16
+
+#else
+#define NXT_MAX_ALIGNMENT 16
+#endif
+
+#endif
+
+
+#define nxt_align_size(size, a) \
+ (((size) + ((size_t) (a) - 1)) & ~((size_t) (a) - 1))
+
+
+#define nxt_align_ptr(p, a) \
+ (u_char *) (((uintptr_t) (p) + ((uintptr_t) (a) - 1)) \
+ & ~((uintptr_t) (a) - 1))
+
+#define nxt_trunc_ptr(p, a) \
+ (u_char *) ((uintptr_t) (p) & ~((uintptr_t) (a) - 1))
+
+
+#endif /* _NXT_ALIGNMENT_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_array.h>
+#include <string.h>
+
+
+nxt_vector_t *
+nxt_vector_create(nxt_uint_t items, size_t item_size,
+ const nxt_mem_proto_t *proto, void *pool)
+{
+ nxt_vector_t *vector;
+
+ vector = proto->alloc(pool, sizeof(nxt_vector_t) + items * item_size);
+
+ if (nxt_fast_path(vector != NULL)) {
+ vector->start = (char *) vector + sizeof(nxt_vector_t);
+ vector->items = 0;
+ vector->item_size = item_size;
+ vector->avalaible = items;
+ vector->type = NXT_VECTOR_EMBEDDED;
+ }
+
+ return vector;
+}
+
+
+void *
+nxt_vector_init(nxt_vector_t *vector, nxt_uint_t items, size_t item_size,
+ const nxt_mem_proto_t *proto, void *pool)
+{
+ vector->start = proto->alloc(pool, items * item_size);
+
+ if (nxt_fast_path(vector->start != NULL)) {
+ vector->items = 0;
+ vector->item_size = item_size;
+ vector->avalaible = items;
+ vector->type = NXT_VECTOR_INITED;
+ }
+
+ return vector->start;
+}
+
+
+void
+nxt_vector_destroy(nxt_vector_t *vector, const nxt_mem_proto_t *proto,
+ void *pool)
+{
+ switch (vector->type) {
+
+ case NXT_VECTOR_INITED:
+ proto->free(pool, vector->start);
+#if (NXT_DEBUG)
+ vector->start = NULL;
+ vector->items = 0;
+ vector->avalaible = 0;
+#endif
+ break;
+
+ case NXT_VECTOR_DESCRETE:
+ proto->free(pool, vector->start);
+
+ /* Fall through. */
+
+ case NXT_VECTOR_EMBEDDED:
+ proto->free(pool, vector);
+ break;
+ }
+}
+
+
+void *
+nxt_vector_add(nxt_vector_t *vector, const nxt_mem_proto_t *proto, void *pool)
+{
+ void *item, *start, *old;
+ size_t size;
+ uint32_t n;
+
+ n = vector->avalaible;
+
+ if (n == vector->items) {
+
+ if (n < 16) {
+ /* Allocate new vector twice as much as current. */
+ n *= 2;
+
+ } else {
+ /* Allocate new vector half as much as current. */
+ n += n / 2;
+ }
+
+ size = n * vector->item_size;
+
+ start = proto->alloc(pool, size);
+ if (nxt_slow_path(start == NULL)) {
+ return NULL;
+ }
+
+ vector->avalaible = n;
+ old = vector->start;
+ vector->start = start;
+
+ memcpy(start, old, size);
+
+ if (vector->type == NXT_VECTOR_EMBEDDED) {
+ vector->type = NXT_VECTOR_DESCRETE;
+
+ } else {
+ proto->free(pool, old);
+ }
+ }
+
+ item = (char *) vector->start + vector->item_size * vector->items;
+
+ vector->items++;
+
+ return item;
+}
+
+
+void *
+nxt_vector_zero_add(nxt_vector_t *vector, const nxt_mem_proto_t *proto,
+ void *pool)
+{
+ void *item;
+
+ item = nxt_vector_add(vector, proto, pool);
+
+ if (nxt_fast_path(item != NULL)) {
+ memset(item, 0, vector->item_size);
+ }
+
+ return item;
+}
+
+
+void
+nxt_vector_remove(nxt_vector_t *vector, void *item)
+{
+ u_char *next, *last, *end;
+ uint32_t item_size;
+
+ item_size = vector->item_size;
+ end = (u_char *) vector->start + item_size * vector->items;
+ last = end - item_size;
+
+ if (item != last) {
+ next = (u_char *) item + item_size;
+
+ memmove(item, next, end - next);
+ }
+
+ vector->items--;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_ARRAY_H_INCLUDED_
+#define _NXT_ARRAY_H_INCLUDED_
+
+
+typedef enum {
+ NXT_VECTOR_INITED = 0,
+ NXT_VECTOR_DESCRETE,
+ NXT_VECTOR_EMBEDDED,
+} nxt_vector_type_t;
+
+
+typedef struct {
+ void *start;
+ /*
+ * A vector can hold no more than 65536 items.
+ * The item size is no more than 64K.
+ */
+ uint16_t items;
+ uint16_t avalaible;
+ uint16_t item_size;
+ nxt_vector_type_t type:8;
+} nxt_vector_t;
+
+
+NXT_EXPORT nxt_vector_t *nxt_vector_create(nxt_uint_t items, size_t item_size,
+ const nxt_mem_proto_t *proto, void *pool);
+NXT_EXPORT void *nxt_vector_init(nxt_vector_t *vector, nxt_uint_t items,
+ size_t item_size, const nxt_mem_proto_t *proto, void *pool);
+NXT_EXPORT void nxt_vector_destroy(nxt_vector_t *vector,
+ const nxt_mem_proto_t *proto, void *pool);
+NXT_EXPORT void *nxt_vector_add(nxt_vector_t *vector,
+ const nxt_mem_proto_t *proto, void *pool);
+NXT_EXPORT void *nxt_vector_zero_add(nxt_vector_t *vector,
+ const nxt_mem_proto_t *proto, void *pool);
+NXT_EXPORT void nxt_vector_remove(nxt_vector_t *vector, void *item);
+
+
+#define nxt_vector_last(vector) \
+ ((void *) \
+ ((char *) (vector)->start \
+ + (vector)->item_size * ((vector)->items - 1)))
+
+
+#define nxt_vector_reset(vector) \
+ (vector)->items = 0;
+
+
+#define nxt_vector_is_empty(vector) \
+ ((vector)->items == 0)
+
+
+nxt_inline void *
+nxt_vector_remove_last(nxt_vector_t *vector)
+{
+ vector->items--;
+ return (char *) vector->start + vector->item_size * vector->items;
+}
+
+
+#endif /* _NXT_ARRAY_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_CLANG_H_INCLUDED_
+#define _NXT_CLANG_H_INCLUDED_
+
+
+#include <stddef.h> /* offsetof(). */
+#include <unistd.h> /* NULL. */
+
+
+#define nxt_inline static inline __attribute__((always_inline))
+#define nxt_noinline __attribute__((noinline))
+#define nxt_cdecl
+
+
+#define nxt_container_of(p, type, field) \
+ (type *) ((u_char *) (p) - offsetof(type, field))
+
+#define nxt_nitems(x) \
+ (sizeof(x) / sizeof((x)[0]))
+
+
+
+#if (NXT_HAVE_BUILTIN_EXPECT)
+#define nxt_fast_path(x) __builtin_expect((long) (x), 1)
+#define nxt_slow_path(x) __builtin_expect((long) (x), 0)
+
+#else
+#define nxt_fast_path(x) (x)
+#define nxt_slow_path(x) (x)
+#endif
+
+
+#if (NXT_HAVE_BUILTIN_UNREACHABLE)
+#define nxt_unreachable() __builtin_unreachable()
+
+#else
+#define nxt_unreachable()
+#endif
+
+
+#if (NXT_HAVE_BUILTIN_PREFETCH)
+#define nxt_prefetch(a) __builtin_prefetch(a)
+
+#else
+#define nxt_prefetch(a)
+#endif
+
+
+#if (NXT_HAVE_GCC_ATTRIBUTE_VISIBILITY)
+#define NXT_EXPORT __attribute__((visibility("default")))
+
+#else
+#define NXT_EXPORT
+#endif
+
+
+#if (NXT_HAVE_GCC_ATTRIBUTE_MALLOC)
+#define NXT_MALLOC_LIKE __attribute__((__malloc__))
+
+#else
+#define NXT_MALLOC_LIKE
+#endif
+
+
+#if (NXT_HAVE_GCC_ATTRIBUTE_ALIGNED)
+#define nxt_aligned(x) __attribute__((aligned(x)))
+
+#else
+#define nxt_aligned(x)
+#endif
+
+
+#if (NXT_CLANG)
+/* Any __asm__ directive disables loop vectorization in GCC and Clang. */
+#define nxt_pragma_loop_disable_vectorization __asm__("")
+
+#else
+#define nxt_pragma_loop_disable_vectorization
+#endif
+
+
+#endif /* _NXT_CLANG_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+
+
+uint32_t
+nxt_djb_hash(const void *data, size_t len)
+{
+ uint32_t hash;
+ const u_char *p;
+
+ p = data;
+ hash = NXT_DJB_HASH_INIT;
+
+ while (len != 0) {
+ hash = nxt_djb_hash_add(hash, *p++);
+ len--;
+ }
+
+ return hash;
+}
+
+
+uint32_t
+nxt_djb_hash_lowcase(const void *data, size_t len)
+{
+ u_char c;
+ uint32_t hash;
+ const u_char *p;
+
+ p = data;
+ hash = NXT_DJB_HASH_INIT;
+
+ while (len != 0) {
+ c = *p++;
+ hash = nxt_djb_hash_add(hash, nxt_lowcase(c));
+ len--;
+ }
+
+ return hash;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_DJB_HASH_H_INCLUDED_
+#define _NXT_DJB_HASH_H_INCLUDED_
+
+
+/* A fast and simple hash function by Daniel J. Bernstein. */
+
+
+NXT_EXPORT uint32_t nxt_djb_hash(const void *data, size_t len);
+NXT_EXPORT uint32_t nxt_djb_hash_lowcase(const void *data, size_t len);
+
+
+#define NXT_DJB_HASH_INIT 5381
+
+
+#define nxt_djb_hash_add(hash, val) \
+ ((uint32_t) ((((hash) << 5) + (hash)) ^ (uint32_t) (val)))
+
+
+#endif /* _NXT_DJB_HASH_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_lvlhsh.h>
+#include <string.h>
+
+
+/*
+ * The level hash consists of hierarchical levels of arrays of pointers.
+ * The pointers may point to another level, a bucket, or NULL.
+ * The levels and buckets must be allocated in manner alike posix_memalign()
+ * to bookkeep additional information in pointer low bits.
+ *
+ * A level is an array of pointers. Its size is a power of 2. Levels
+ * may be different sizes, but on the same level the sizes are the same.
+ * Level sizes are specified by number of bits per level in lvlhsh->shift
+ * array. A hash may have up to 7 levels. There are two predefined
+ * shift arrays given by the first two shift array values:
+ *
+ * 1) [0, 0]: [4, 4, 4, 4, 4, 4, 4] on a 64-bit platform or
+ * [5, 5, 5, 5, 5, 5, 0] on a 32-bit platform,
+ * so default size of levels is 128 bytes.
+ *
+ * 2) [0, 10]: [10, 4, 4, 4, 4, 4, 0] on a 64-bit platform or
+ * [10, 5, 5, 5, 5, 0, 0] on a 32-bit platform,
+ * so default size of levels is 128 bytes on all levels except
+ * the first level. The first level is 8K or 4K on 64-bit or 32-bit
+ * platforms respectively.
+ *
+ * All buckets in a hash are the same size which is a power of 2.
+ * A bucket contains several entries stored and tested sequentially.
+ * The bucket size should be one or two CPU cache line size, a minimum
+ * allowed size is 32 bytes. A default 128-byte bucket contains 10 64-bit
+ * entries or 15 32-bit entries. Each entry consists of pointer to value
+ * data and 32-bit key. If an entry value pointer is NULL, the entry is free.
+ * On a 64-bit platform entry value pointers are no aligned, therefore they
+ * are accessed as two 32-bit integers. The rest trailing space in a bucket
+ * is used as pointer to next bucket and this pointer is always aligned.
+ * Although the level hash allows to store a lot of values in a bucket chain,
+ * this is non optimal way. The large data set should be stored using
+ * several levels.
+ */
+
+#define nxt_lvlhsh_is_bucket(p) \
+ ((uintptr_t) (p) & 1)
+
+
+#define nxt_lvlhsh_count_inc(n) \
+ n = (void *) ((uintptr_t) (n) + 2)
+
+
+#define nxt_lvlhsh_count_dec(n) \
+ n = (void *) ((uintptr_t) (n) - 2)
+
+
+#define nxt_lvlhsh_level_size(proto, nlvl) \
+ ((uintptr_t) 1 << proto->shift[nlvl])
+
+
+#define nxt_lvlhsh_level(lvl, mask) \
+ (void **) ((uintptr_t) lvl & (~mask << 2))
+
+
+#define nxt_lvlhsh_level_entries(lvl, mask) \
+ ((uintptr_t) lvl & (mask << 1))
+
+
+#define nxt_lvlhsh_store_bucket(slot, bkt) \
+ slot = (void **) ((uintptr_t) bkt | 2 | 1)
+
+
+#define nxt_lvlhsh_bucket_size(proto) \
+ proto->bucket_size
+
+
+#define nxt_lvlhsh_bucket(proto, bkt) \
+ (uint32_t *) ((uintptr_t) bkt & ~(uintptr_t) proto->bucket_mask)
+
+
+#define nxt_lvlhsh_bucket_entries(proto, bkt) \
+ (((uintptr_t) bkt & (uintptr_t) proto->bucket_mask) >> 1)
+
+
+#define nxt_lvlhsh_bucket_end(proto, bkt) \
+ &bkt[proto->bucket_end]
+
+
+#define nxt_lvlhsh_free_entry(e) \
+ (!(nxt_lvlhsh_valid_entry(e)))
+
+
+#define nxt_lvlhsh_next_bucket(proto, bkt) \
+ ((void **) &bkt[proto->bucket_end])
+
+#if (NXT_64BIT)
+
+#define nxt_lvlhsh_valid_entry(e) \
+ (((e)[0] | (e)[1]) != 0)
+
+
+#define nxt_lvlhsh_entry_value(e) \
+ (void *) (((uintptr_t) (e)[1] << 32) + (e)[0])
+
+
+#define nxt_lvlhsh_set_entry_value(e, n) \
+ (e)[0] = (uint32_t) (uintptr_t) n; \
+ (e)[1] = (uint32_t) ((uintptr_t) n >> 32)
+
+
+#define nxt_lvlhsh_entry_key(e) \
+ (e)[2]
+
+
+#define nxt_lvlhsh_set_entry_key(e, n) \
+ (e)[2] = n
+
+#else
+
+#define nxt_lvlhsh_valid_entry(e) \
+ ((e)[0] != 0)
+
+
+#define nxt_lvlhsh_entry_value(e) \
+ (void *) (e)[0]
+
+
+#define nxt_lvlhsh_set_entry_value(e, n) \
+ (e)[0] = (uint32_t) n
+
+
+#define nxt_lvlhsh_entry_key(e) \
+ (e)[1]
+
+
+#define nxt_lvlhsh_set_entry_key(e, n) \
+ (e)[1] = n
+
+#endif
+
+
+#define NXT_LVLHSH_BUCKET_DONE ((void *) -1)
+
+
+static nxt_int_t nxt_lvlhsh_level_find(nxt_lvlhsh_query_t *lhq, void **lvl,
+ uint32_t key, nxt_uint_t nlvl);
+static nxt_int_t nxt_lvlhsh_bucket_find(nxt_lvlhsh_query_t *lhq, void **bkt);
+static nxt_int_t nxt_lvlhsh_new_bucket(nxt_lvlhsh_query_t *lhq, void **slot);
+static nxt_int_t nxt_lvlhsh_level_insert(nxt_lvlhsh_query_t *lhq,
+ void **slot, uint32_t key, nxt_uint_t nlvl);
+static nxt_int_t nxt_lvlhsh_bucket_insert(nxt_lvlhsh_query_t *lhq,
+ void **slot, uint32_t key, nxt_int_t nlvl);
+static nxt_int_t nxt_lvlhsh_convert_bucket_to_level(nxt_lvlhsh_query_t *lhq,
+ void **slot, nxt_uint_t nlvl, uint32_t *bucket);
+static nxt_int_t nxt_lvlhsh_level_convertion_insert(nxt_lvlhsh_query_t *lhq,
+ void **parent, uint32_t key, nxt_uint_t nlvl);
+static nxt_int_t nxt_lvlhsh_bucket_convertion_insert(nxt_lvlhsh_query_t *lhq,
+ void **slot, uint32_t key, nxt_int_t nlvl);
+static nxt_int_t nxt_lvlhsh_free_level(nxt_lvlhsh_query_t *lhq, void **level,
+ nxt_uint_t size);
+static nxt_int_t nxt_lvlhsh_level_delete(nxt_lvlhsh_query_t *lhq, void **slot,
+ uint32_t key, nxt_uint_t nlvl);
+static nxt_int_t nxt_lvlhsh_bucket_delete(nxt_lvlhsh_query_t *lhq, void **bkt);
+static void *nxt_lvlhsh_level_each(nxt_lvlhsh_each_t *lhe, void **level,
+ nxt_uint_t nlvl, nxt_uint_t shift);
+static void *nxt_lvlhsh_bucket_each(nxt_lvlhsh_each_t *lhe);
+
+
+nxt_int_t
+nxt_lvlhsh_find(nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq)
+{
+ void *slot;
+
+ slot = lh->slot;
+
+ if (nxt_fast_path(slot != NULL)) {
+
+ if (nxt_lvlhsh_is_bucket(slot)) {
+ return nxt_lvlhsh_bucket_find(lhq, slot);
+ }
+
+ return nxt_lvlhsh_level_find(lhq, slot, lhq->key_hash, 0);
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_level_find(nxt_lvlhsh_query_t *lhq, void **lvl, uint32_t key,
+ nxt_uint_t nlvl)
+{
+ void **slot;
+ uintptr_t mask;
+ nxt_uint_t shift;
+
+ shift = lhq->proto->shift[nlvl];
+ mask = ((uintptr_t) 1 << shift) - 1;
+
+ lvl = nxt_lvlhsh_level(lvl, mask);
+ slot = lvl[key & mask];
+
+ if (slot != NULL) {
+
+ if (nxt_lvlhsh_is_bucket(slot)) {
+ return nxt_lvlhsh_bucket_find(lhq, slot);
+ }
+
+ return nxt_lvlhsh_level_find(lhq, slot, key >> shift, nlvl + 1);
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_bucket_find(nxt_lvlhsh_query_t *lhq, void **bkt)
+{
+ void *value;
+ uint32_t *bucket, *e;
+ nxt_uint_t n;
+
+ do {
+ bucket = nxt_lvlhsh_bucket(lhq->proto, bkt);
+ n = nxt_lvlhsh_bucket_entries(lhq->proto, bkt);
+ e = bucket;
+
+ do {
+ if (nxt_lvlhsh_valid_entry(e)) {
+ n--;
+
+ if (nxt_lvlhsh_entry_key(e) == lhq->key_hash) {
+
+ value = nxt_lvlhsh_entry_value(e);
+
+ if (lhq->proto->test(lhq, value) == NXT_OK) {
+ lhq->value = value;
+
+ return NXT_OK;
+ }
+ }
+ }
+
+ e += NXT_LVLHSH_ENTRY_SIZE;
+
+ } while (n != 0);
+
+ bkt = *nxt_lvlhsh_next_bucket(lhq->proto, bucket);
+
+ } while (bkt != NULL);
+
+ return NXT_DECLINED;
+}
+
+
+nxt_int_t
+nxt_lvlhsh_insert(nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq)
+{
+ uint32_t key;
+
+ if (nxt_fast_path(lh->slot != NULL)) {
+
+ key = lhq->key_hash;
+
+ if (nxt_lvlhsh_is_bucket(lh->slot)) {
+ return nxt_lvlhsh_bucket_insert(lhq, &lh->slot, key, -1);
+ }
+
+ return nxt_lvlhsh_level_insert(lhq, &lh->slot, key, 0);
+ }
+
+ return nxt_lvlhsh_new_bucket(lhq, &lh->slot);
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_new_bucket(nxt_lvlhsh_query_t *lhq, void **slot)
+{
+ uint32_t *bucket;
+
+ bucket = lhq->proto->alloc(lhq->pool, nxt_lvlhsh_bucket_size(lhq->proto),
+ lhq->proto->nalloc);
+
+ if (nxt_fast_path(bucket != NULL)) {
+
+ nxt_lvlhsh_set_entry_value(bucket, lhq->value);
+ nxt_lvlhsh_set_entry_key(bucket, lhq->key_hash);
+
+ *nxt_lvlhsh_next_bucket(lhq->proto, bucket) = NULL;
+
+ nxt_lvlhsh_store_bucket(*slot, bucket);
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_level_insert(nxt_lvlhsh_query_t *lhq, void **parent, uint32_t key,
+ nxt_uint_t nlvl)
+{
+ void **slot, **lvl;
+ nxt_int_t ret;
+ uintptr_t mask;
+ nxt_uint_t shift;
+
+ shift = lhq->proto->shift[nlvl];
+ mask = ((uintptr_t) 1 << shift) - 1;
+
+ lvl = nxt_lvlhsh_level(*parent, mask);
+ slot = &lvl[key & mask];
+
+ if (*slot != NULL) {
+ key >>= shift;
+
+ if (nxt_lvlhsh_is_bucket(*slot)) {
+ return nxt_lvlhsh_bucket_insert(lhq, slot, key, nlvl);
+ }
+
+ return nxt_lvlhsh_level_insert(lhq, slot, key, nlvl + 1);
+ }
+
+ ret = nxt_lvlhsh_new_bucket(lhq, slot);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ nxt_lvlhsh_count_inc(*parent);
+ }
+
+ return ret;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_bucket_insert(nxt_lvlhsh_query_t *lhq, void **slot, uint32_t key,
+ nxt_int_t nlvl)
+{
+ void **bkt, **vacant_bucket, *value;
+ uint32_t *bucket, *e, *vacant_entry;
+ nxt_int_t ret;
+ uintptr_t n;
+ const void *new_value;
+ const nxt_lvlhsh_proto_t *proto;
+
+ bkt = slot;
+ vacant_entry = NULL;
+ vacant_bucket = NULL;
+ proto = lhq->proto;
+
+ /* Search for duplicate entry in bucket chain. */
+
+ do {
+ bucket = nxt_lvlhsh_bucket(proto, *bkt);
+ n = nxt_lvlhsh_bucket_entries(proto, *bkt);
+ e = bucket;
+
+ do {
+ if (nxt_lvlhsh_valid_entry(e)) {
+
+ if (nxt_lvlhsh_entry_key(e) == lhq->key_hash) {
+
+ value = nxt_lvlhsh_entry_value(e);
+
+ if (proto->test(lhq, value) == NXT_OK) {
+
+ new_value = lhq->value;
+ lhq->value = value;
+
+ if (lhq->replace) {
+ nxt_lvlhsh_set_entry_value(e, new_value);
+
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+ }
+ }
+
+ n--;
+
+ } else {
+ /*
+ * Save a hole vacant position in bucket
+ * and continue to search for duplicate entry.
+ */
+ if (vacant_entry == NULL) {
+ vacant_entry = e;
+ vacant_bucket = bkt;
+ }
+ }
+
+ e += NXT_LVLHSH_ENTRY_SIZE;
+
+ } while (n != 0);
+
+ if (e < nxt_lvlhsh_bucket_end(proto, bucket)) {
+ /*
+ * Save a vacant position on incomplete bucket's end
+ * and continue to search for duplicate entry.
+ */
+ if (vacant_entry == NULL) {
+ vacant_entry = e;
+ vacant_bucket = bkt;
+ }
+ }
+
+ bkt = nxt_lvlhsh_next_bucket(proto, bucket);
+
+ } while (*bkt != NULL);
+
+ if (vacant_entry != NULL) {
+ nxt_lvlhsh_set_entry_value(vacant_entry, lhq->value);
+ nxt_lvlhsh_set_entry_key(vacant_entry, lhq->key_hash);
+ nxt_lvlhsh_count_inc(*vacant_bucket);
+
+ return NXT_OK;
+ }
+
+ /* All buckets are full. */
+
+ nlvl++;
+
+ if (nxt_fast_path(proto->shift[nlvl] != 0)) {
+
+ ret = nxt_lvlhsh_convert_bucket_to_level(lhq, slot, nlvl, bucket);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return nxt_lvlhsh_level_insert(lhq, slot, key, nlvl);
+ }
+
+ return ret;
+ }
+
+ /* The last allowed level, only buckets may be allocated here. */
+
+ return nxt_lvlhsh_new_bucket(lhq, bkt);
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_convert_bucket_to_level(nxt_lvlhsh_query_t *lhq, void **slot,
+ nxt_uint_t nlvl, uint32_t *bucket)
+{
+ void *lvl, **level;
+ uint32_t *e, *end, key;
+ nxt_int_t ret;
+ nxt_uint_t i, shift, size;
+ nxt_lvlhsh_query_t q;
+ const nxt_lvlhsh_proto_t *proto;
+
+ proto = lhq->proto;
+ size = nxt_lvlhsh_level_size(proto, nlvl);
+
+ lvl = proto->alloc(lhq->pool, size * (sizeof(void *)), proto->nalloc);
+
+ if (nxt_slow_path(lvl == NULL)) {
+ return NXT_ERROR;
+ }
+
+ memset(lvl, 0, size * (sizeof(void *)));
+
+ level = lvl;
+ shift = 0;
+
+ for (i = 0; i < nlvl; i++) {
+ /*
+ * Using SIMD operations in this trivial loop with maximum
+ * 8 iterations may increase code size by 170 bytes.
+ */
+ nxt_pragma_loop_disable_vectorization;
+
+ shift += proto->shift[i];
+ }
+
+ end = nxt_lvlhsh_bucket_end(proto, bucket);
+
+ for (e = bucket; e < end; e += NXT_LVLHSH_ENTRY_SIZE) {
+
+ q.proto = proto;
+ q.pool = lhq->pool;
+ q.value = nxt_lvlhsh_entry_value(e);
+ key = nxt_lvlhsh_entry_key(e);
+ q.key_hash = key;
+
+ ret = nxt_lvlhsh_level_convertion_insert(&q, &lvl, key >> shift, nlvl);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return nxt_lvlhsh_free_level(lhq, level, size);
+ }
+ }
+
+ *slot = lvl;
+
+ proto->free(lhq->pool, bucket, nxt_lvlhsh_bucket_size(proto));
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_level_convertion_insert(nxt_lvlhsh_query_t *lhq, void **parent,
+ uint32_t key, nxt_uint_t nlvl)
+{
+ void **slot, **lvl;
+ nxt_int_t ret;
+ uintptr_t mask;
+ nxt_uint_t shift;
+
+ shift = lhq->proto->shift[nlvl];
+ mask = ((uintptr_t) 1 << shift) - 1;
+
+ lvl = nxt_lvlhsh_level(*parent, mask);
+ slot = &lvl[key & mask];
+
+ if (*slot == NULL) {
+ ret = nxt_lvlhsh_new_bucket(lhq, slot);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ nxt_lvlhsh_count_inc(*parent);
+ }
+
+ return ret;
+ }
+
+ /* Only backets can be here. */
+
+ return nxt_lvlhsh_bucket_convertion_insert(lhq, slot, key >> shift, nlvl);
+}
+
+
+/*
+ * The special bucket insertion procedure is required because during
+ * convertion lhq->key contains garbage values and the test function
+ * cannot be called. Besides, the procedure can be simpler because
+ * a new entry is inserted just after occupied entries.
+ */
+
+static nxt_int_t
+nxt_lvlhsh_bucket_convertion_insert(nxt_lvlhsh_query_t *lhq, void **slot,
+ uint32_t key, nxt_int_t nlvl)
+{
+ void **bkt;
+ uint32_t *bucket, *e;
+ nxt_int_t ret;
+ uintptr_t n;
+ const nxt_lvlhsh_proto_t *proto;
+
+ bkt = slot;
+ proto = lhq->proto;
+
+ do {
+ bucket = nxt_lvlhsh_bucket(proto, *bkt);
+ n = nxt_lvlhsh_bucket_entries(proto, *bkt);
+ e = bucket + n * NXT_LVLHSH_ENTRY_SIZE;
+
+ if (nxt_fast_path(e < nxt_lvlhsh_bucket_end(proto, bucket))) {
+
+ nxt_lvlhsh_set_entry_value(e, lhq->value);
+ nxt_lvlhsh_set_entry_key(e, lhq->key_hash);
+ nxt_lvlhsh_count_inc(*bkt);
+
+ return NXT_OK;
+ }
+
+ bkt = nxt_lvlhsh_next_bucket(proto, bucket);
+
+ } while (*bkt != NULL);
+
+ /* All buckets are full. */
+
+ nlvl++;
+
+ if (nxt_fast_path(proto->shift[nlvl] != 0)) {
+
+ ret = nxt_lvlhsh_convert_bucket_to_level(lhq, slot, nlvl, bucket);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return nxt_lvlhsh_level_insert(lhq, slot, key, nlvl);
+ }
+
+ return ret;
+ }
+
+ /* The last allowed level, only buckets may be allocated here. */
+
+ return nxt_lvlhsh_new_bucket(lhq, bkt);
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_free_level(nxt_lvlhsh_query_t *lhq, void **level, nxt_uint_t size)
+{
+ size_t bsize;
+ nxt_uint_t i;
+ const nxt_lvlhsh_proto_t *proto;
+
+ proto = lhq->proto;
+ bsize = nxt_lvlhsh_bucket_size(proto);
+
+ for (i = 0; i < size; i++) {
+
+ if (level[i] != NULL) {
+ /*
+ * Chained buckets are not possible here, since even
+ * in the worst case one bucket cannot be converted
+ * in two chained buckets but remains the same bucket.
+ */
+ proto->free(lhq->pool, nxt_lvlhsh_bucket(proto, level[i]), bsize);
+ }
+ }
+
+ proto->free(lhq->pool, level, size * (sizeof(void *)));
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_lvlhsh_delete(nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq)
+{
+ if (nxt_fast_path(lh->slot != NULL)) {
+
+ if (nxt_lvlhsh_is_bucket(lh->slot)) {
+ return nxt_lvlhsh_bucket_delete(lhq, &lh->slot);
+ }
+
+ return nxt_lvlhsh_level_delete(lhq, &lh->slot, lhq->key_hash, 0);
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_level_delete(nxt_lvlhsh_query_t *lhq, void **parent, uint32_t key,
+ nxt_uint_t nlvl)
+{
+ size_t size;
+ void **slot, **lvl;
+ uintptr_t mask;
+ nxt_int_t ret;
+ nxt_uint_t shift;
+
+ shift = lhq->proto->shift[nlvl];
+ mask = ((uintptr_t) 1 << shift) - 1;
+
+ lvl = nxt_lvlhsh_level(*parent, mask);
+ slot = &lvl[key & mask];
+
+ if (*slot != NULL) {
+
+ if (nxt_lvlhsh_is_bucket(*slot)) {
+ ret = nxt_lvlhsh_bucket_delete(lhq, slot);
+
+ } else {
+ key >>= shift;
+ ret = nxt_lvlhsh_level_delete(lhq, slot, key, nlvl + 1);
+ }
+
+ if (*slot == NULL) {
+ nxt_lvlhsh_count_dec(*parent);
+
+ if (nxt_lvlhsh_level_entries(*parent, mask) == 0) {
+ *parent = NULL;
+ size = nxt_lvlhsh_level_size(lhq->proto, nlvl);
+ lhq->proto->free(lhq->pool, lvl, size * sizeof(void *));
+ }
+ }
+
+ return ret;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static nxt_int_t
+nxt_lvlhsh_bucket_delete(nxt_lvlhsh_query_t *lhq, void **bkt)
+{
+ void *value;
+ size_t size;
+ uint32_t *bucket, *e;
+ uintptr_t n;
+ const nxt_lvlhsh_proto_t *proto;
+
+ proto = lhq->proto;
+
+ do {
+ bucket = nxt_lvlhsh_bucket(proto, *bkt);
+ n = nxt_lvlhsh_bucket_entries(proto, *bkt);
+ e = bucket;
+
+ do {
+ if (nxt_lvlhsh_valid_entry(e)) {
+
+ if (nxt_lvlhsh_entry_key(e) == lhq->key_hash) {
+
+ value = nxt_lvlhsh_entry_value(e);
+
+ if (proto->test(lhq, value) == NXT_OK) {
+
+ if (nxt_lvlhsh_bucket_entries(proto, *bkt) == 1) {
+ *bkt = *nxt_lvlhsh_next_bucket(proto, bucket);
+ size = nxt_lvlhsh_bucket_size(proto);
+ proto->free(lhq->pool, bucket, size);
+
+ } else {
+ nxt_lvlhsh_count_dec(*bkt);
+ nxt_lvlhsh_set_entry_value(e, NULL);
+ }
+
+ lhq->value = value;
+
+ return NXT_OK;
+ }
+ }
+
+ n--;
+ }
+
+ e += NXT_LVLHSH_ENTRY_SIZE;
+
+ } while (n != 0);
+
+ bkt = nxt_lvlhsh_next_bucket(proto, bucket);
+
+ } while (*bkt != NULL);
+
+ return NXT_DECLINED;
+}
+
+
+void *
+nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *lhe)
+{
+ void **slot;
+
+ if (lhe->bucket == NXT_LVLHSH_BUCKET_DONE) {
+ slot = lh->slot;
+
+ if (nxt_lvlhsh_is_bucket(slot)) {
+ return NULL;
+ }
+
+ } else {
+ if (nxt_slow_path(lhe->bucket == NULL)) {
+
+ /* The first iteration only. */
+
+ slot = lh->slot;
+
+ if (slot == NULL) {
+ return NULL;
+ }
+
+ if (!nxt_lvlhsh_is_bucket(slot)) {
+ goto level;
+ }
+
+ lhe->bucket = nxt_lvlhsh_bucket(lhe->proto, slot);
+ lhe->entries = nxt_lvlhsh_bucket_entries(lhe->proto, slot);
+ }
+
+ return nxt_lvlhsh_bucket_each(lhe);
+ }
+
+level:
+
+ return nxt_lvlhsh_level_each(lhe, slot, 0, 0);
+}
+
+
+static void *
+nxt_lvlhsh_level_each(nxt_lvlhsh_each_t *lhe, void **level, nxt_uint_t nlvl,
+ nxt_uint_t shift)
+{
+ void **slot, *value;
+ uintptr_t mask;
+ nxt_uint_t n, level_shift;
+
+ level_shift = lhe->proto->shift[nlvl];
+ mask = ((uintptr_t) 1 << level_shift) - 1;
+
+ level = nxt_lvlhsh_level(level, mask);
+
+ do {
+ n = (lhe->current >> shift) & mask;
+ slot = level[n];
+
+ if (slot != NULL) {
+ if (nxt_lvlhsh_is_bucket(slot)) {
+
+ if (lhe->bucket != NXT_LVLHSH_BUCKET_DONE) {
+
+ lhe->bucket = nxt_lvlhsh_bucket(lhe->proto, slot);
+ lhe->entries = nxt_lvlhsh_bucket_entries(lhe->proto, slot);
+ lhe->entry = 0;
+
+ return nxt_lvlhsh_bucket_each(lhe);
+ }
+
+ lhe->bucket = NULL;
+
+ } else {
+ value = nxt_lvlhsh_level_each(lhe, slot, nlvl + 1,
+ shift + level_shift);
+ if (value != NULL) {
+ return value;
+ }
+ }
+ }
+
+ lhe->current &= ~(mask << shift);
+ n = ((n + 1) & mask) << shift;
+ lhe->current |= n;
+
+ } while (n != 0);
+
+ return NULL;
+}
+
+
+static nxt_noinline void *
+nxt_lvlhsh_bucket_each(nxt_lvlhsh_each_t *lhe)
+{
+ void *value, **next;
+ uint32_t *bucket;
+
+ /* At least one valid entry must present here. */
+ do {
+ bucket = &lhe->bucket[lhe->entry];
+ lhe->entry += NXT_LVLHSH_ENTRY_SIZE;
+
+ } while (nxt_lvlhsh_free_entry(bucket));
+
+ value = nxt_lvlhsh_entry_value(bucket);
+
+ lhe->entries--;
+
+ if (lhe->entries == 0) {
+ next = *nxt_lvlhsh_next_bucket(lhe->proto, lhe->bucket);
+
+ lhe->bucket = (next == NULL) ? NXT_LVLHSH_BUCKET_DONE:
+ nxt_lvlhsh_bucket(lhe->proto, next);
+
+ lhe->entries = nxt_lvlhsh_bucket_entries(lhe->proto, next);
+ lhe->entry = 0;
+ }
+
+ return value;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_LVLHSH_H_INCLUDED_
+#define _NXT_LVLHSH_H_INCLUDED_
+
+
+typedef struct nxt_lvlhsh_query_s nxt_lvlhsh_query_t;
+
+typedef nxt_int_t (*nxt_lvlhsh_test_t)(nxt_lvlhsh_query_t *lhq, void *data);
+typedef void *(*nxt_lvlhsh_alloc_t)(void *ctx, size_t size, nxt_uint_t nalloc);
+typedef void (*nxt_lvlhsh_free_t)(void *ctx, void *p, size_t size);
+
+
+#if (NXT_64BIT)
+
+#define NXT_LVLHSH_DEFAULT_BUCKET_SIZE 128
+#define NXT_LVLHSH_ENTRY_SIZE 3
+#define NXT_LVLHSH_BATCH_ALLOC 16
+
+/* 3 is shift of 64-bit pointer. */
+#define NXT_LVLHSH_MEMALIGN_SHIFT (NXT_MAX_MEMALIGN_SHIFT - 3)
+
+#else
+
+#define NXT_LVLHSH_DEFAULT_BUCKET_SIZE 64
+#define NXT_LVLHSH_ENTRY_SIZE 2
+#define NXT_LVLHSH_BATCH_ALLOC 8
+
+/* 2 is shift of 32-bit pointer. */
+#define NXT_LVLHSH_MEMALIGN_SHIFT (NXT_MAX_MEMALIGN_SHIFT - 2)
+
+#endif
+
+
+#if (NXT_LVLHSH_MEMALIGN_SHIFT < 10)
+#define NXT_LVLHSH_MAX_MEMALIGN_SHIFT NXT_LVLHSH_MEMALIGN_SHIFT
+#else
+#define NXT_LVLHSH_MAX_MEMALIGN_SHIFT 10
+#endif
+
+
+#define NXT_LVLHSH_BUCKET_END(bucket_size) \
+ (((bucket_size) - sizeof(void *)) \
+ / (NXT_LVLHSH_ENTRY_SIZE * sizeof(uint32_t)) \
+ * NXT_LVLHSH_ENTRY_SIZE)
+
+
+#define NXT_LVLHSH_BUCKET_SIZE(bucket_size) \
+ NXT_LVLHSH_BUCKET_END(bucket_size), bucket_size, (bucket_size - 1)
+
+
+#define NXT_LVLHSH_DEFAULT \
+ NXT_LVLHSH_BUCKET_SIZE(NXT_LVLHSH_DEFAULT_BUCKET_SIZE), \
+ { 4, 4, 4, 4, 4, 4, 4, 0 }
+
+
+#define NXT_LVLHSH_LARGE_SLAB \
+ NXT_LVLHSH_BUCKET_SIZE(NXT_LVLHSH_DEFAULT_BUCKET_SIZE), \
+ { 10, 4, 4, 4, 4, 4, 4, 0 }
+
+
+#define NXT_LVLHSH_LARGE_MEMALIGN \
+ NXT_LVLHSH_BUCKET_SIZE(NXT_LVLHSH_DEFAULT_BUCKET_SIZE), \
+ { NXT_LVLHSH_MAX_MEMALIGN_SHIFT, 4, 4, 4, 4, 0, 0, 0 }
+
+
+typedef struct {
+ uint32_t bucket_end;
+ uint32_t bucket_size;
+ uint32_t bucket_mask;
+ uint8_t shift[8];
+ uint32_t nalloc;
+
+ nxt_lvlhsh_test_t test;
+ nxt_lvlhsh_alloc_t alloc;
+ nxt_lvlhsh_free_t free;
+} nxt_lvlhsh_proto_t;
+
+
+typedef struct {
+ nxt_lvlhsh_test_t test;
+ nxt_lvlhsh_alloc_t alloc;
+ nxt_lvlhsh_free_t free;
+
+ /* The maximum allowed aligned shift. */
+ uint32_t max_shift;
+ uint32_t nalloc;
+} nxt_lvlhsh_ctx_t;
+
+
+typedef struct {
+ void *slot;
+} nxt_lvlhsh_t;
+
+
+struct nxt_lvlhsh_query_s {
+ uint32_t key_hash;
+ nxt_str_t key;
+#if 0
+ uint32_t key_length;
+ void *key_start;
+#endif
+
+ uint8_t replace; /* 1 bit */
+ void *value;
+
+ const nxt_lvlhsh_proto_t *proto;
+ void *pool;
+
+ /* Opaque data passed for the test function. */
+ void *data;
+};
+
+
+#define \
+nxt_lvlhsh_is_empty(lh) \
+ ((lh)->slot == NULL)
+
+
+#define \
+nxt_lvlhsh_init(lh) \
+ (lh)->slot = NULL
+
+/*
+ * nxt_lvlhsh_find() finds a hash element. If the element has been
+ * found then it is stored in the lhq->value and nxt_lvlhsh_find()
+ * returns NXT_OK. Otherwise NXT_DECLINED is returned.
+ *
+ * The required nxt_lvlhsh_query_t fields: key_hash, key, proto.
+ */
+NXT_EXPORT nxt_int_t nxt_lvlhsh_find(nxt_lvlhsh_t *lh, nxt_lvlhsh_query_t *lhq);
+
+/*
+ * nxt_lvlhsh_insert() adds a hash element. If the element already
+ * presents in lvlhsh and the lhq->replace flag is zero, then lhq->value
+ * is updated with the old element and NXT_DECLINED is returned.
+ * If the element already presents in lvlhsh and the lhq->replace flag
+ * is non-zero, then the old element is replaced with the new element.
+ * lhq->value is updated with the old element, and NXT_OK is returned.
+ * If the element is not present in lvlhsh, then it is inserted and
+ * NXT_OK is returned. The lhq->value is not changed.
+ * On memory allocation failure NXT_ERROR is returned.
+ *
+ * The required nxt_lvlhsh_query_t fields: key_hash, key, proto, replace, value.
+ * The optional nxt_lvlhsh_query_t fields: pool.
+ */
+NXT_EXPORT nxt_int_t nxt_lvlhsh_insert(nxt_lvlhsh_t *lh,
+ nxt_lvlhsh_query_t *lhq);
+
+/*
+ * nxt_lvlhsh_delete() deletes a hash element. If the element has been
+ * found then it is removed from lvlhsh and is stored in the lhq->value,
+ * and NXT_OK is returned. Otherwise NXT_DECLINED is returned.
+ *
+ * The required nxt_lvlhsh_query_t fields: key_hash, key, proto.
+ * The optional nxt_lvlhsh_query_t fields: pool.
+ */
+NXT_EXPORT nxt_int_t nxt_lvlhsh_delete(nxt_lvlhsh_t *lh,
+ nxt_lvlhsh_query_t *lhq);
+
+
+typedef struct {
+ const nxt_lvlhsh_proto_t *proto;
+
+ /*
+ * Fields to store current bucket entry position. They cannot be
+ * combined in a single bucket pointer with number of entries in low
+ * bits, because entry positions are not aligned. A current level is
+ * stored as key bit path from the root.
+ */
+ uint32_t *bucket;
+ uint32_t current;
+ uint32_t entry;
+ uint32_t entries;
+} nxt_lvlhsh_each_t;
+
+
+NXT_EXPORT void *nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *le);
+
+
+NXT_EXPORT void *nxt_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc);
+NXT_EXPORT void nxt_lvlhsh_free(void *data, void *p, size_t size);
+
+NXT_EXPORT void *nxt_lvlhsh_pool_alloc(void *ctx, size_t size,
+ nxt_uint_t nalloc);
+NXT_EXPORT void nxt_lvlhsh_pool_free(void *ctx, void *p, size_t size);
+
+
+#endif /* _NXT_LVLHSH_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_malloc.h>
+
+
+#if (NXT_HAVE_POSIX_MEMALIGN)
+
+/*
+ * posix_memalign() presents in Linux glibc 2.1.91, FreeBSD 7.0,
+ * Solaris 11, MacOSX 10.6 (Snow Leopard), NetBSD 5.0, OpenBSD 4.8.
+ */
+
+void *
+nxt_memalign(size_t alignment, size_t size)
+{
+ int err;
+ void *p;
+
+ err = posix_memalign(&p, alignment, size);
+
+ if (nxt_fast_path(err == 0)) {
+ return p;
+ }
+
+ // STUB
+ //nxt_errno_set(err);
+
+ return NULL;
+}
+
+#elif (NXT_HAVE_MEMALIGN)
+
+/* memalign() presents in Solaris, HP-UX. */
+
+void *
+nxt_memalign(size_t alignment, size_t size)
+{
+ return memalign(alignment, size);
+}
+
+#else
+
+#error no memalign() implementation.
+
+#endif
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_MALLOC_H_INCLUDED_
+#define _NXT_MALLOC_H_INCLUDED_
+
+#include <stdlib.h>
+
+/*
+ * alloca() is defined in stdlib.h in Linux, FreeBSD and MacOSX
+ * and in alloca.h in Linux, Solaris and MacOSX.
+ */
+#if (NXT_SOLARIS)
+#include <alloca.h>
+#endif
+
+
+#define nxt_malloc(size) malloc(size)
+#define nxt_free(p) free(p)
+
+
+NXT_EXPORT void *nxt_memalign(size_t alignment, size_t size);
+
+
+#endif /* _NXT_MALLOC_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_queue.h>
+#include <nxt_rbtree.h>
+#include <nxt_mem_cache_pool.h>
+#include <string.h>
+
+
+/*
+ * A memory cache pool allocates memory in clusters of specified size and
+ * aligned to page_alignment. A cluster is divided on pages of specified
+ * size. Page size must be a power of 2. A page can be used entirely or
+ * can be divided on chunks of equal size. Chunk size must be a power of 2.
+ * A cluster can contains pages with different chunk sizes. Cluster size
+ * must be multiple of page size and may be not a power of 2. Allocations
+ * greater than page are allocated outside clusters. Start addresses and
+ * sizes of clusters and large allocations are stored in rbtree to find
+ * them on free operations. The rbtree nodes are sorted by start addresses.
+ */
+
+
+typedef struct nxt_mem_cache_page_s nxt_mem_cache_page_t;
+
+struct nxt_mem_cache_page_s {
+ /* Chunk bitmap. There can be no more than 32 chunks in a page. */
+ uint8_t map[4];
+
+ /* Number of free chunks of a chunked page. */
+ uint8_t chunks;
+
+ /*
+ * Size of chunks or page shifted by pool->chunk_size_shift.
+ * Zero means that page is free.
+ */
+ uint8_t size;
+
+ /*
+ * Page number in page cluster.
+ * There can be no more than 65536 pages in a cluster.
+ */
+ uint16_t number;
+
+ /*
+ * Used to link pages with free chunks in pool chunk slot list
+ * or to link free pages in clusters.
+ */
+ nxt_queue_link_t link;
+};
+
+
+typedef struct {
+ NXT_RBTREE_NODE (node);
+ uint8_t type;
+ uint32_t size;
+
+ u_char *start;
+ nxt_mem_cache_page_t pages[];
+} nxt_mem_cache_block_t;
+
+
+typedef struct {
+ nxt_queue_t pages;
+#if (NXT_64BIT)
+ uint32_t size;
+ uint32_t chunks;
+#else
+ uint16_t size;
+ uint16_t chunks;
+#endif
+} nxt_mem_cache_slot_t;
+
+
+struct nxt_mem_cache_pool_s {
+ /* rbtree of nxt_mem_cache_block_t. */
+ nxt_rbtree_t blocks;
+
+ nxt_queue_t free_pages;
+
+ uint8_t chunk_size_shift;
+ uint8_t page_size_shift;
+ uint32_t page_size;
+ uint32_t page_alignment;
+ uint32_t cluster_size;
+
+ const nxt_mem_proto_t *proto;
+ void *mem;
+ void *trace;
+
+ nxt_mem_cache_slot_t slots[];
+};
+
+
+/* A cluster cache block. */
+#define NXT_MEM_CACHE_CLUSTER_BLOCK 0
+
+/* A discrete cache block of large allocation. */
+#define NXT_MEM_CACHE_DISCRETE_BLOCK 1
+/*
+ * An embedded cache block allocated together with large allocation
+ * just after the allocation.
+ */
+#define NXT_MEM_CACHE_EMBEDDED_BLOCK 2
+
+
+#define nxt_mem_cache_chunk_is_free(map, chunk) \
+ ((map[chunk / 8] & (0x80 >> (chunk & 7))) == 0)
+
+
+#define nxt_mem_cache_chunk_set_free(map, chunk) \
+ map[chunk / 8] &= ~(0x80 >> (chunk & 7))
+
+
+#define nxt_mem_cache_free_junk(p, size) \
+ memset((p), 0x5A, size)
+
+
+static nxt_uint_t nxt_mem_cache_shift(nxt_uint_t n);
+static void *nxt_mem_cache_alloc_small(nxt_mem_cache_pool_t *pool, size_t size);
+static nxt_uint_t nxt_mem_cache_alloc_chunk(u_char *map, nxt_uint_t size);
+static nxt_mem_cache_page_t *
+ nxt_mem_cache_alloc_page(nxt_mem_cache_pool_t *pool);
+static nxt_mem_cache_block_t *
+ nxt_mem_cache_alloc_cluster(nxt_mem_cache_pool_t *pool);
+static void *nxt_mem_cache_alloc_large(nxt_mem_cache_pool_t *pool,
+ size_t alignment, size_t size);
+static nxt_int_t nxt_mem_cache_rbtree_compare(nxt_rbtree_node_t *node1,
+ nxt_rbtree_node_t *node2);
+static nxt_mem_cache_block_t *nxt_mem_cache_find_block(nxt_rbtree_t *tree,
+ u_char *p);
+static const char *nxt_mem_cache_chunk_free(nxt_mem_cache_pool_t *pool,
+ nxt_mem_cache_block_t *cluster, u_char *p);
+
+
+nxt_mem_cache_pool_t *
+nxt_mem_cache_pool_create(const nxt_mem_proto_t *proto, void *mem,
+ void *trace, size_t cluster_size, size_t page_alignment, size_t page_size,
+ size_t min_chunk_size)
+{
+ /* Alignment and sizes must be a power of 2. */
+
+ if (nxt_slow_path((page_alignment & (page_alignment - 1)) != 0
+ || (page_size & (page_size - 1)) != 0
+ || (min_chunk_size & (min_chunk_size - 1)) != 0))
+ {
+ return NULL;
+ }
+
+ page_alignment = nxt_max(page_alignment, NXT_MAX_ALIGNMENT);
+
+ if (nxt_slow_path(page_size < 64
+ || page_size < page_alignment
+ || page_size < min_chunk_size
+ || min_chunk_size * 32 < page_size
+ || cluster_size < page_size
+ || cluster_size % page_size != 0))
+ {
+ return NULL;
+ }
+
+ return nxt_mem_cache_pool_fast_create(proto, mem, trace,
+ cluster_size, page_alignment,
+ page_size, min_chunk_size);
+}
+
+
+nxt_mem_cache_pool_t *
+nxt_mem_cache_pool_fast_create(const nxt_mem_proto_t *proto, void *mem,
+ void *trace, size_t cluster_size, size_t page_alignment, size_t page_size,
+ size_t min_chunk_size)
+{
+ nxt_uint_t slots, chunk_size;
+ nxt_mem_cache_slot_t *slot;
+ nxt_mem_cache_pool_t *pool;
+
+ slots = 0;
+ chunk_size = page_size;
+
+ do {
+ slots++;
+ chunk_size /= 2;
+ } while (chunk_size > min_chunk_size);
+
+ pool = proto->zalloc(mem, sizeof(nxt_mem_cache_pool_t)
+ + slots * sizeof(nxt_mem_cache_slot_t));
+
+ if (nxt_fast_path(pool != NULL)) {
+
+ pool->proto = proto;
+ pool->mem = mem;
+ pool->trace = trace;
+
+ pool->page_size = page_size;
+ pool->page_alignment = nxt_max(page_alignment, NXT_MAX_ALIGNMENT);
+ pool->cluster_size = cluster_size;
+
+ slot = pool->slots;
+
+ do {
+ nxt_queue_init(&slot->pages);
+
+ slot->size = chunk_size;
+ /* slot->chunks should be one less than actual number of chunks. */
+ slot->chunks = (page_size / chunk_size) - 1;
+
+ slot++;
+ chunk_size *= 2;
+ } while (chunk_size < page_size);
+
+ pool->chunk_size_shift = nxt_mem_cache_shift(min_chunk_size);
+ pool->page_size_shift = nxt_mem_cache_shift(page_size);
+
+ nxt_rbtree_init(&pool->blocks, nxt_mem_cache_rbtree_compare);
+
+ nxt_queue_init(&pool->free_pages);
+ }
+
+ return pool;
+}
+
+
+static nxt_uint_t
+nxt_mem_cache_shift(nxt_uint_t n)
+{
+ nxt_uint_t shift;
+
+ shift = 0;
+ n /= 2;
+
+ do {
+ shift++;
+ n /= 2;
+ } while (n != 0);
+
+ return shift;
+}
+
+
+nxt_bool_t
+nxt_mem_cache_pool_is_empty(nxt_mem_cache_pool_t *pool)
+{
+ return (nxt_rbtree_is_empty(&pool->blocks)
+ && nxt_queue_is_empty(&pool->free_pages));
+}
+
+
+void
+nxt_mem_cache_pool_destroy(nxt_mem_cache_pool_t *pool)
+{
+ void *p;
+ nxt_rbtree_node_t *node, *next;
+ nxt_mem_cache_block_t *block;
+
+ for (node = nxt_rbtree_min(&pool->blocks);
+ nxt_rbtree_is_there_successor(&pool->blocks, node);
+ node = next)
+ {
+ next = nxt_rbtree_node_successor(&pool->blocks, node);
+
+ block = (nxt_mem_cache_block_t *) node;
+
+ nxt_rbtree_delete(&pool->blocks, &block->node);
+
+ p = block->start;
+
+ if (block->type != NXT_MEM_CACHE_EMBEDDED_BLOCK) {
+ pool->proto->free(pool->mem, block);
+ }
+
+ pool->proto->free(pool->mem, p);
+ }
+
+ pool->proto->free(pool->mem, pool);
+}
+
+
+nxt_inline u_char *
+nxt_mem_cache_page_addr(nxt_mem_cache_pool_t *pool, nxt_mem_cache_page_t *page)
+{
+ nxt_mem_cache_block_t *block;
+
+ block = (nxt_mem_cache_block_t *)
+ ((u_char *) page - page->number * sizeof(nxt_mem_cache_page_t)
+ - offsetof(nxt_mem_cache_block_t, pages));
+
+ return block->start + (page->number << pool->page_size_shift);
+}
+
+
+void *
+nxt_mem_cache_alloc(nxt_mem_cache_pool_t *pool, size_t size)
+{
+ if (pool->proto->trace != NULL) {
+ pool->proto->trace(pool->trace, "mem cache alloc: %zd", size);
+ }
+
+ if (size <= pool->page_size) {
+ return nxt_mem_cache_alloc_small(pool, size);
+ }
+
+ return nxt_mem_cache_alloc_large(pool, NXT_MAX_ALIGNMENT, size);
+}
+
+
+void *
+nxt_mem_cache_zalloc(nxt_mem_cache_pool_t *pool, size_t size)
+{
+ void *p;
+
+ p = nxt_mem_cache_alloc(pool, size);
+
+ if (nxt_fast_path(p != NULL)) {
+ memset(p, 0, size);
+ }
+
+ return p;
+}
+
+
+void *
+nxt_mem_cache_align(nxt_mem_cache_pool_t *pool, size_t alignment, size_t size)
+{
+ if (pool->proto->trace != NULL) {
+ pool->proto->trace(pool->trace,
+ "mem cache align: @%zd:%zd", alignment, size);
+ }
+
+ /* Alignment must be a power of 2. */
+
+ if (nxt_fast_path((alignment - 1) & alignment) == 0) {
+
+ if (size <= pool->page_size && alignment <= pool->page_alignment) {
+ size = nxt_max(size, alignment);
+
+ if (size <= pool->page_size) {
+ return nxt_mem_cache_alloc_small(pool, size);
+ }
+ }
+
+ return nxt_mem_cache_alloc_large(pool, alignment, size);
+ }
+
+ return NULL;
+}
+
+
+void *
+nxt_mem_cache_zalign(nxt_mem_cache_pool_t *pool, size_t alignment, size_t size)
+{
+ void *p;
+
+ p = nxt_mem_cache_align(pool, alignment, size);
+
+ if (nxt_fast_path(p != NULL)) {
+ memset(p, 0, size);
+ }
+
+ return p;
+}
+
+
+static void *
+nxt_mem_cache_alloc_small(nxt_mem_cache_pool_t *pool, size_t size)
+{
+ u_char *p;
+ nxt_queue_link_t *link;
+ nxt_mem_cache_page_t *page;
+ nxt_mem_cache_slot_t *slot;
+
+ p = NULL;
+
+ if (size <= pool->page_size / 2) {
+
+ /* Find a slot with appropriate chunk size. */
+ for (slot = pool->slots; slot->size < size; slot++) { /* void */ }
+
+ size = slot->size;
+
+ if (nxt_fast_path(!nxt_queue_is_empty(&slot->pages))) {
+
+ link = nxt_queue_first(&slot->pages);
+ page = nxt_queue_link_data(link, nxt_mem_cache_page_t, link);
+
+ p = nxt_mem_cache_page_addr(pool, page);
+ p += nxt_mem_cache_alloc_chunk(page->map, size);
+
+ page->chunks--;
+
+ if (page->chunks == 0) {
+ /*
+ * Remove full page from the pool chunk slot list
+ * of pages with free chunks.
+ */
+ nxt_queue_remove(&page->link);
+ }
+
+ } else {
+ page = nxt_mem_cache_alloc_page(pool);
+
+ if (nxt_fast_path(page != NULL)) {
+
+ nxt_queue_insert_head(&slot->pages, &page->link);
+
+ /* Mark the first chunk as busy. */
+ page->map[0] = 0x80;
+ page->map[1] = 0;
+ page->map[2] = 0;
+ page->map[3] = 0;
+
+ /* slot->chunks are already one less. */
+ page->chunks = slot->chunks;
+ page->size = size >> pool->chunk_size_shift;
+
+ p = nxt_mem_cache_page_addr(pool, page);
+ }
+ }
+
+ } else {
+ page = nxt_mem_cache_alloc_page(pool);
+
+ if (nxt_fast_path(page != NULL)) {
+ page->size = pool->page_size >> pool->chunk_size_shift;
+
+ p = nxt_mem_cache_page_addr(pool, page);
+ }
+
+#if (NXT_DEBUG)
+ size = pool->page_size;
+#endif
+ }
+
+ if (pool->proto->trace != NULL) {
+ pool->proto->trace(pool->trace, "mem cache chunk:%uz alloc: %p",
+ size, p);
+ }
+
+ return p;
+}
+
+
+static nxt_uint_t
+nxt_mem_cache_alloc_chunk(uint8_t *map, nxt_uint_t size)
+{
+ uint8_t mask;
+ nxt_uint_t n, offset;
+
+ offset = 0;
+ n = 0;
+
+ /* The page must have at least one free chunk. */
+
+ for ( ;; ) {
+ if (map[n] != 0xff) {
+
+ mask = 0x80;
+
+ do {
+ if ((map[n] & mask) == 0) {
+ /* A free chunk is found. */
+ map[n] |= mask;
+ return offset;
+ }
+
+ offset += size;
+ mask >>= 1;
+
+ } while (mask != 0);
+
+ } else {
+ /* Fast-forward: all 8 chunks are occupied. */
+ offset += size * 8;
+ }
+
+ n++;
+ }
+}
+
+
+static nxt_mem_cache_page_t *
+nxt_mem_cache_alloc_page(nxt_mem_cache_pool_t *pool)
+{
+ nxt_queue_link_t *link;
+ nxt_mem_cache_page_t *page;
+ nxt_mem_cache_block_t *cluster;
+
+ if (nxt_queue_is_empty(&pool->free_pages)) {
+ cluster = nxt_mem_cache_alloc_cluster(pool);
+ if (nxt_slow_path(cluster == NULL)) {
+ return NULL;
+ }
+ }
+
+ link = nxt_queue_first(&pool->free_pages);
+ nxt_queue_remove(link);
+
+ page = nxt_queue_link_data(link, nxt_mem_cache_page_t, link);
+
+ return page;
+}
+
+
+static nxt_mem_cache_block_t *
+nxt_mem_cache_alloc_cluster(nxt_mem_cache_pool_t *pool)
+{
+ nxt_uint_t n;
+ nxt_mem_cache_block_t *cluster;
+
+ n = pool->cluster_size >> pool->page_size_shift;
+
+ cluster = pool->proto->zalloc(pool->mem, sizeof(nxt_mem_cache_block_t)
+ + n * sizeof(nxt_mem_cache_page_t));
+
+ if (nxt_slow_path(cluster == NULL)) {
+ return NULL;
+ }
+
+ /* NXT_MEM_CACHE_CLUSTER_BLOCK type is zero. */
+
+ cluster->size = pool->cluster_size;
+
+ cluster->start = pool->proto->align(pool->mem, pool->page_alignment,
+ pool->cluster_size);
+ if (nxt_slow_path(cluster->start == NULL)) {
+ pool->proto->free(pool->mem, cluster);
+ return NULL;
+ }
+
+ n--;
+ cluster->pages[n].number = n;
+ nxt_queue_insert_head(&pool->free_pages, &cluster->pages[n].link);
+
+ while (n != 0) {
+ n--;
+ cluster->pages[n].number = n;
+ nxt_queue_insert_before(&cluster->pages[n + 1].link,
+ &cluster->pages[n].link);
+ }
+
+ nxt_rbtree_insert(&pool->blocks, &cluster->node);
+
+ return cluster;
+}
+
+
+static void *
+nxt_mem_cache_alloc_large(nxt_mem_cache_pool_t *pool, size_t alignment,
+ size_t size)
+{
+ u_char *p;
+ size_t aligned_size;
+ uint8_t type;
+ nxt_mem_cache_block_t *block;
+
+ if (nxt_slow_path((size - 1) & size) != 0) {
+ aligned_size = nxt_align_size(size, sizeof(uintptr_t));
+
+ p = pool->proto->align(pool->mem, alignment,
+ aligned_size + sizeof(nxt_mem_cache_block_t));
+
+ if (nxt_slow_path(p == NULL)) {
+ return NULL;
+ }
+
+ block = (nxt_mem_cache_block_t *) (p + aligned_size);
+ type = NXT_MEM_CACHE_EMBEDDED_BLOCK;
+
+ } else {
+ block = pool->proto->alloc(pool->mem, sizeof(nxt_mem_cache_block_t));
+
+ if (nxt_slow_path(block == NULL)) {
+ pool->proto->free(pool->mem, block);
+ return NULL;
+ }
+
+ p = pool->proto->align(pool->mem, alignment, size);
+ if (nxt_slow_path(p == NULL)) {
+ return NULL;
+ }
+
+ type = NXT_MEM_CACHE_DISCRETE_BLOCK;
+ }
+
+ block->type = type;
+ block->size = size;
+ block->start = p;
+
+ nxt_rbtree_insert(&pool->blocks, &block->node);
+
+ return p;
+}
+
+
+static nxt_int_t
+nxt_mem_cache_rbtree_compare(nxt_rbtree_node_t *node1, nxt_rbtree_node_t *node2)
+{
+ nxt_mem_cache_block_t *block1, *block2;
+
+ block1 = (nxt_mem_cache_block_t *) node1;
+ block2 = (nxt_mem_cache_block_t *) node2;
+
+ return (uintptr_t) block1->start - (uintptr_t) block2->start;
+}
+
+
+void
+nxt_mem_cache_free(nxt_mem_cache_pool_t *pool, void *p)
+{
+ const char *err;
+ nxt_mem_cache_block_t *block;
+
+ if (pool->proto->trace != NULL) {
+ pool->proto->trace(pool->trace, "mem cache free %p", p);
+ }
+
+ block = nxt_mem_cache_find_block(&pool->blocks, p);
+
+ if (nxt_fast_path(block != NULL)) {
+
+ if (block->type == NXT_MEM_CACHE_CLUSTER_BLOCK) {
+ err = nxt_mem_cache_chunk_free(pool, block, p);
+
+ if (nxt_fast_path(err == NULL)) {
+ return;
+ }
+
+ } else if (nxt_fast_path(p == block->start)) {
+ nxt_rbtree_delete(&pool->blocks, &block->node);
+
+ if (block->type == NXT_MEM_CACHE_DISCRETE_BLOCK) {
+ pool->proto->free(pool->mem, block);
+ }
+
+ pool->proto->free(pool->mem, p);
+
+ return;
+
+ } else {
+ err = "freed pointer points to middle of block: %p";
+ }
+
+ } else {
+ err = "freed pointer is out of pool: %p";
+ }
+
+ if (pool->proto->alert != NULL) {
+ pool->proto->alert(pool->trace, err, p);
+ }
+}
+
+
+static nxt_mem_cache_block_t *
+nxt_mem_cache_find_block(nxt_rbtree_t *tree, u_char *p)
+{
+ nxt_rbtree_node_t *node, *sentinel;
+ nxt_mem_cache_block_t *block;
+
+ node = nxt_rbtree_root(tree);
+ sentinel = nxt_rbtree_sentinel(tree);
+
+ while (node != sentinel) {
+
+ block = (nxt_mem_cache_block_t *) node;
+
+ if (p < block->start) {
+ node = node->left;
+
+ } else if (p >= block->start + block->size) {
+ node = node->right;
+
+ } else {
+ return block;
+ }
+ }
+
+ return NULL;
+}
+
+
+static const char *
+nxt_mem_cache_chunk_free(nxt_mem_cache_pool_t *pool,
+ nxt_mem_cache_block_t *cluster, u_char *p)
+{
+ u_char *start;
+ uintptr_t offset;
+ nxt_uint_t n, size, chunk;
+ nxt_mem_cache_page_t *page;
+ nxt_mem_cache_slot_t *slot;
+
+ n = (p - cluster->start) >> pool->page_size_shift;
+ start = cluster->start + (n << pool->page_size_shift);
+
+ page = &cluster->pages[n];
+
+ if (page->size == 0) {
+ return "freed pointer points to already free page: %p";
+ }
+
+ size = page->size << pool->chunk_size_shift;
+
+ if (size != pool->page_size) {
+
+ offset = (uintptr_t) (p - start) & (pool->page_size - 1);
+ chunk = offset / size;
+
+ if (nxt_slow_path(offset != chunk * size)) {
+ return "freed pointer points to wrong chunk: %p";
+ }
+
+ if (nxt_slow_path(nxt_mem_cache_chunk_is_free(page->map, chunk))) {
+ return "freed pointer points to already free chunk: %p";
+ }
+
+ nxt_mem_cache_chunk_set_free(page->map, chunk);
+
+ /* Find a slot with appropriate chunk size. */
+ for (slot = pool->slots; slot->size < size; slot++) { /* void */ }
+
+ if (page->chunks != slot->chunks) {
+ page->chunks++;
+
+ if (page->chunks == 1) {
+ /*
+ * Add the page to the head of pool chunk slot list
+ * of pages with free chunks.
+ */
+ nxt_queue_insert_head(&slot->pages, &page->link);
+ }
+
+ nxt_mem_cache_free_junk(p, size);
+
+ return NULL;
+
+ } else {
+ /*
+ * All chunks are free, remove the page from pool chunk slot
+ * list of pages with free chunks.
+ */
+ nxt_queue_remove(&page->link);
+ }
+
+ } else if (nxt_slow_path(p != start)) {
+ return "invalid pointer to chunk: %p";
+ }
+
+ /* Add the free page to the pool's free pages tree. */
+
+ page->size = 0;
+ nxt_queue_insert_head(&pool->free_pages, &page->link);
+
+ nxt_mem_cache_free_junk(p, size);
+
+ /* Test if all pages in the cluster are free. */
+
+ page = cluster->pages;
+ n = pool->cluster_size >> pool->page_size_shift;
+
+ do {
+ if (page->size != 0) {
+ return NULL;
+ }
+
+ page++;
+ n--;
+ } while (n != 0);
+
+ /* Free cluster. */
+
+ page = cluster->pages;
+ n = pool->cluster_size >> pool->page_size_shift;
+
+ do {
+ nxt_queue_remove(&page->link);
+ page++;
+ n--;
+ } while (n != 0);
+
+ nxt_rbtree_delete(&pool->blocks, &cluster->node);
+
+ p = cluster->start;
+
+ pool->proto->free(pool->mem, cluster);
+ pool->proto->free(pool->mem, p);
+
+ return NULL;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_MEM_CACHE_POOL_H_INCLUDED_
+#define _NXT_MEM_CACHE_POOL_H_INCLUDED_
+
+
+typedef struct nxt_mem_cache_pool_s nxt_mem_cache_pool_t;
+
+
+NXT_EXPORT nxt_mem_cache_pool_t *
+ nxt_mem_cache_pool_create(const nxt_mem_proto_t *proto, void *mem,
+ void *trace, size_t cluster_size, size_t page_alignment, size_t page_size,
+ size_t min_chunk_size)
+ NXT_MALLOC_LIKE;
+NXT_EXPORT nxt_mem_cache_pool_t *
+ nxt_mem_cache_pool_fast_create(const nxt_mem_proto_t *proto, void *mem,
+ void *trace, size_t cluster_size, size_t page_alignment, size_t page_size,
+ size_t min_chunk_size)
+ NXT_MALLOC_LIKE;
+NXT_EXPORT nxt_bool_t nxt_mem_cache_pool_is_empty(nxt_mem_cache_pool_t *pool);
+NXT_EXPORT void nxt_mem_cache_pool_destroy(nxt_mem_cache_pool_t *pool);
+
+NXT_EXPORT void *nxt_mem_cache_alloc(nxt_mem_cache_pool_t *pool, size_t size)
+ NXT_MALLOC_LIKE;
+NXT_EXPORT void *nxt_mem_cache_zalloc(nxt_mem_cache_pool_t *pool, size_t size)
+ NXT_MALLOC_LIKE;
+NXT_EXPORT void *nxt_mem_cache_align(nxt_mem_cache_pool_t *pool,
+ size_t alignment, size_t size)
+ NXT_MALLOC_LIKE;
+NXT_EXPORT void *nxt_mem_cache_zalign(nxt_mem_cache_pool_t *pool,
+ size_t alignment, size_t size)
+ NXT_MALLOC_LIKE;
+NXT_EXPORT void nxt_mem_cache_free(nxt_mem_cache_pool_t *pool, void *p);
+
+
+#endif /* _NXT_MEM_CACHE_POOL_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * The code is based on the code by Austin Appleby,
+ * released to the public domain.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_murmur_hash.h>
+
+
+uint32_t
+nxt_murmur_hash2(const void *data, size_t len)
+{
+ uint32_t h, k;
+ const u_char *p;
+ const uint32_t m = 0x5bd1e995;
+
+ p = data;
+ h = 0 ^ (uint32_t) len;
+
+ while (len >= 4) {
+ k = p[0];
+ k |= p[1] << 8;
+ k |= p[2] << 16;
+ k |= p[3] << 24;
+
+ k *= m;
+ k ^= k >> 24;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ p += 4;
+ len -= 4;
+ }
+
+ switch (len) {
+ case 3:
+ h ^= p[2] << 16;
+ case 2:
+ h ^= p[1] << 8;
+ case 1:
+ h ^= p[0];
+ h *= m;
+ }
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+
+
+/* The MurmurHash2 for fixed 4 byte length. */
+
+uint32_t
+nxt_murmur_hash2_uint32(const void *data)
+{
+ uint32_t h, k;
+ const u_char *p;
+ const uint32_t m = 0x5bd1e995;
+
+ p = data;
+
+ k = p[0];
+ k |= p[1] << 8;
+ k |= p[2] << 16;
+ k |= p[3] << 24;
+
+ k *= m;
+ k ^= k >> 24;
+ k *= m;
+
+ h = 0 ^ 4;
+ h *= m;
+ h ^= k;
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_MURMUR_HASH_H_INCLUDED_
+#define _NXT_MURMUR_HASH_H_INCLUDED_
+
+
+NXT_EXPORT uint32_t nxt_murmur_hash2(const void *data, size_t len);
+NXT_EXPORT uint32_t nxt_murmur_hash2_uint32(const void *data);
+
+
+#endif /* _NXT_MURMUR_HASH_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_queue.h>
+
+
+/*
+ * Find the middle queue element if the queue has odd number of elements,
+ * or the first element of the queue's second part otherwise.
+ */
+
+nxt_queue_link_t *
+nxt_queue_middle(nxt_queue_t *queue)
+{
+ nxt_queue_link_t *middle, *next;
+
+ middle = nxt_queue_first(queue);
+
+ if (middle == nxt_queue_last(queue)) {
+ return middle;
+ }
+
+ next = middle;
+
+ for ( ;; ) {
+ middle = nxt_queue_next(middle);
+
+ next = nxt_queue_next(next);
+
+ if (next == nxt_queue_last(queue)) {
+ return middle;
+ }
+
+ next = nxt_queue_next(next);
+
+ if (next == nxt_queue_last(queue)) {
+ return middle;
+ }
+ }
+}
+
+
+/*
+ * nxt_queue_sort() provides a stable sort because it uses the insertion
+ * sort algorithm. Its worst and average computational complexity is O^2.
+ */
+
+void
+nxt_queue_sort(nxt_queue_t *queue,
+ nxt_int_t (*compare)(const void *data, const nxt_queue_link_t *,
+ const nxt_queue_link_t *), const void *data)
+{
+ nxt_queue_link_t *link, *prev, *next;
+
+ link = nxt_queue_first(queue);
+
+ if (link == nxt_queue_last(queue)) {
+ return;
+ }
+
+ for (link = nxt_queue_next(link);
+ link != nxt_queue_tail(queue);
+ link = next)
+ {
+ prev = nxt_queue_prev(link);
+ next = nxt_queue_next(link);
+
+ nxt_queue_remove(link);
+
+ do {
+ if (compare(data, prev, link) <= 0) {
+ break;
+ }
+
+ prev = nxt_queue_prev(prev);
+
+ } while (prev != nxt_queue_head(queue));
+
+ nxt_queue_insert_after(prev, link);
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_QUEUE_H_INCLUDED_
+#define _NXT_QUEUE_H_INCLUDED_
+
+
+typedef struct nxt_queue_link_s nxt_queue_link_t;
+
+struct nxt_queue_link_s {
+ nxt_queue_link_t *prev;
+ nxt_queue_link_t *next;
+};
+
+
+typedef struct {
+ nxt_queue_link_t head;
+} nxt_queue_t;
+
+
+#define nxt_queue_init(queue) \
+ do { \
+ (queue)->head.prev = &(queue)->head; \
+ (queue)->head.next = &(queue)->head; \
+ } while (0)
+
+
+#define nxt_queue_sentinel(link) \
+ do { \
+ (link)->prev = (link); \
+ (link)->next = (link); \
+ } while (0)
+
+
+/*
+ * Short-circuit a queue link to itself to allow once remove safely it
+ * using nxt_queue_remove().
+ */
+
+#define nxt_queue_self(link) \
+ nxt_queue_sentinel(link)
+
+
+#define nxt_queue_is_empty(queue) \
+ (&(queue)->head == (queue)->head.prev)
+
+/*
+ * A loop to iterate all queue links starting from head:
+ *
+ * nxt_queue_link_t link;
+ * } nxt_type_t *tp;
+ *
+ *
+ * for (lnk = nxt_queue_first(queue);
+ * lnk != nxt_queue_tail(queue);
+ * lnk = nxt_queue_next(lnk))
+ * {
+ * tp = nxt_queue_link_data(lnk, nxt_type_t, link);
+ *
+ * or starting from tail:
+ *
+ * for (lnk = nxt_queue_last(queue);
+ * lnk != nxt_queue_head(queue);
+ * lnk = nxt_queue_next(lnk))
+ * {
+ * tp = nxt_queue_link_data(lnk, nxt_type_t, link);
+ */
+
+#define nxt_queue_first(queue) \
+ (queue)->head.next
+
+
+#define nxt_queue_last(queue) \
+ (queue)->head.prev
+
+
+#define nxt_queue_head(queue) \
+ (&(queue)->head)
+
+
+#define nxt_queue_tail(queue) \
+ (&(queue)->head)
+
+
+#define nxt_queue_next(link) \
+ (link)->next
+
+
+#define nxt_queue_prev(link) \
+ (link)->prev
+
+
+#define nxt_queue_insert_head(queue, link) \
+ do { \
+ (link)->next = (queue)->head.next; \
+ (link)->next->prev = (link); \
+ (link)->prev = &(queue)->head; \
+ (queue)->head.next = (link); \
+ } while (0)
+
+
+#define nxt_queue_insert_tail(queue, link) \
+ do { \
+ (link)->prev = (queue)->head.prev; \
+ (link)->prev->next = (link); \
+ (link)->next = &(queue)->head; \
+ (queue)->head.prev = (link); \
+ } while (0)
+
+
+#define nxt_queue_insert_after(target, link) \
+ do { \
+ (link)->next = (target)->next; \
+ (link)->next->prev = (link); \
+ (link)->prev = (target); \
+ (target)->next = (link); \
+ } while (0)
+
+
+#define nxt_queue_insert_before(target, link) \
+ do { \
+ (link)->next = (target); \
+ (link)->prev = (target)->prev; \
+ (target)->prev = (link); \
+ (link)->prev->next = (link); \
+ } while (0)
+
+
+#if (NXT_DEBUG)
+
+#define nxt_queue_remove(link) \
+ do { \
+ (link)->next->prev = (link)->prev; \
+ (link)->prev->next = (link)->next; \
+ (link)->prev = NULL; \
+ (link)->next = NULL; \
+ } while (0)
+
+#else
+
+#define nxt_queue_remove(link) \
+ do { \
+ (link)->next->prev = (link)->prev; \
+ (link)->prev->next = (link)->next; \
+ } while (0)
+
+#endif
+
+
+/*
+ * Split the queue "queue" starting at the element "link",
+ * the "tail" is the new tail queue.
+ */
+
+#define nxt_queue_split(queue, link, tail) \
+ do { \
+ (tail)->head.prev = (queue)->head.prev; \
+ (tail)->head.prev->next = &(tail)->head; \
+ (tail)->head.next = (link); \
+ (queue)->head.prev = (link)->prev; \
+ (queue)->head.prev->next = &(queue)->head; \
+ (link)->prev = &(tail)->head; \
+ } while (0)
+
+
+/* Truncate the queue "queue" starting at element "link". */
+
+#define nxt_queue_truncate(queue, link) \
+ do { \
+ (queue)->head.prev = (link)->prev; \
+ (queue)->head.prev->next = &(queue)->head; \
+ } while (0)
+
+
+/* Add the queue "tail" to the queue "queue". */
+
+#define nxt_queue_add(queue, tail) \
+ do { \
+ (queue)->head.prev->next = (tail)->head.next; \
+ (tail)->head.next->prev = (queue)->head.prev; \
+ (queue)->head.prev = (tail)->head.prev; \
+ (queue)->head.prev->next = &(queue)->head; \
+ } while (0)
+
+
+#define nxt_queue_link_data(lnk, type, link) \
+ nxt_container_of(lnk, type, link)
+
+
+NXT_EXPORT nxt_queue_link_t *nxt_queue_middle(nxt_queue_t *queue);
+NXT_EXPORT void nxt_queue_sort(nxt_queue_t *queue,
+ nxt_int_t (*compare)(const void *, const nxt_queue_link_t *,
+ const nxt_queue_link_t *), const void *data);
+
+
+#endif /* _NXT_QUEUE_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_rbtree.h>
+
+
+/*
+ * The red-black tree code is based on the algorithm described in
+ * the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
+ */
+
+
+static void nxt_rbtree_insert_fixup(nxt_rbtree_node_t *node);
+static void nxt_rbtree_delete_fixup(nxt_rbtree_t *tree, nxt_rbtree_node_t *node);
+nxt_inline void nxt_rbtree_left_rotate(nxt_rbtree_node_t *node);
+nxt_inline void nxt_rbtree_right_rotate(nxt_rbtree_node_t *node);
+nxt_inline void nxt_rbtree_parent_relink(nxt_rbtree_node_t *subst,
+ nxt_rbtree_node_t *node);
+
+
+#define NXT_RBTREE_BLACK 0
+#define NXT_RBTREE_RED 1
+
+
+#define nxt_rbtree_set_callback_type(tree, type) \
+ (tree)->sentinel.spare = type
+
+#define nxt_rbtree_has_insertion_callback(tree) \
+ ((tree)->sentinel.spare != 0)
+
+#define nxt_rbtree_comparison_callback(tree) \
+ ((nxt_rbtree_compare_t) (tree)->sentinel.right)
+
+
+void
+nxt_rbtree_init(nxt_rbtree_t *tree, nxt_rbtree_compare_t compare)
+{
+ /*
+ * The sentinel is used as a leaf node sentinel and as a tree root
+ * sentinel: it is a parent of a root node and the root node is
+ * the left child of the sentinel. Combining two sentinels in one
+ * entry and the fact that the sentinel's left child is a root node
+ * simplifies nxt_rbtree_node_successor() and eliminates explicit
+ * root node test before or inside nxt_rbtree_min().
+ */
+
+ /* The root is empty. */
+ tree->sentinel.left = &tree->sentinel;
+
+ /*
+ * The sentinel's right child is never used so
+ * or comparison callback can be safely stored here.
+ */
+ tree->sentinel.right = (void *) compare;
+
+ /* The root and leaf sentinel must be black. */
+ tree->sentinel.color = NXT_RBTREE_BLACK;
+}
+
+
+void
+nxt_rbtree_insert(nxt_rbtree_t *tree, nxt_rbtree_part_t *part)
+{
+ nxt_rbtree_node_t *node, *new_node, *sentinel, **child;
+ nxt_rbtree_compare_t compare;
+
+ new_node = (nxt_rbtree_node_t *) part;
+
+ node = nxt_rbtree_root(tree);
+ sentinel = nxt_rbtree_sentinel(tree);
+
+ new_node->left = sentinel;
+ new_node->right = sentinel;
+ new_node->color = NXT_RBTREE_RED;
+
+ compare = (nxt_rbtree_compare_t) tree->sentinel.right;
+ child = &nxt_rbtree_root(tree);
+
+ while (*child != sentinel) {
+ node = *child;
+
+ nxt_prefetch(node->left);
+ nxt_prefetch(node->right);
+
+ child = (compare(new_node, node) < 0) ? &node->left : &node->right;
+ }
+
+ *child = new_node;
+ new_node->parent = node;
+
+ nxt_rbtree_insert_fixup(new_node);
+
+ node = nxt_rbtree_root(tree);
+ node->color = NXT_RBTREE_BLACK;
+}
+
+
+static void
+nxt_rbtree_insert_fixup(nxt_rbtree_node_t *node)
+{
+ nxt_rbtree_node_t *parent, *grandparent, *uncle;
+
+ /*
+ * Prefetching parent nodes does not help here because they are
+ * already traversed during insertion.
+ */
+
+ for ( ;; ) {
+ parent = node->parent;
+
+ /*
+ * Testing whether a node is a tree root is not required here since
+ * a root node's parent is the sentinel and it is always black.
+ */
+ if (parent->color == NXT_RBTREE_BLACK) {
+ return;
+ }
+
+ grandparent = parent->parent;
+
+ if (parent == grandparent->left) {
+ uncle = grandparent->right;
+
+ if (uncle->color == NXT_RBTREE_BLACK) {
+
+ if (node == parent->right) {
+ node = parent;
+ nxt_rbtree_left_rotate(node);
+ }
+
+ parent = node->parent;
+ parent->color = NXT_RBTREE_BLACK;
+
+ grandparent = parent->parent;
+ grandparent->color = NXT_RBTREE_RED;
+ nxt_rbtree_right_rotate(grandparent);
+
+ continue;
+ }
+
+ } else {
+ uncle = grandparent->left;
+
+ if (uncle->color == NXT_RBTREE_BLACK) {
+
+ if (node == parent->left) {
+ node = parent;
+ nxt_rbtree_right_rotate(node);
+ }
+
+ parent = node->parent;
+ parent->color = NXT_RBTREE_BLACK;
+
+ grandparent = parent->parent;
+ grandparent->color = NXT_RBTREE_RED;
+ nxt_rbtree_left_rotate(grandparent);
+
+ continue;
+ }
+ }
+
+ uncle->color = NXT_RBTREE_BLACK;
+ parent->color = NXT_RBTREE_BLACK;
+ grandparent->color = NXT_RBTREE_RED;
+
+ node = grandparent;
+ }
+}
+
+
+nxt_rbtree_node_t *
+nxt_rbtree_find(nxt_rbtree_t *tree, nxt_rbtree_part_t *part)
+{
+ nxt_int_t n;
+ nxt_rbtree_node_t *node, *next, *sentinel;
+ nxt_rbtree_compare_t compare;
+
+ node = (nxt_rbtree_node_t *) part;
+
+ next = nxt_rbtree_root(tree);
+ sentinel = nxt_rbtree_sentinel(tree);
+ compare = nxt_rbtree_comparison_callback(tree);
+
+ while (next != sentinel) {
+ nxt_prefetch(next->left);
+ nxt_prefetch(next->right);
+
+ n = compare(node, next);
+
+ if (n < 0) {
+ next = next->left;
+
+ } else if (n > 0) {
+ next = next->right;
+
+ } else {
+ return next;
+ }
+ }
+
+ return NULL;
+}
+
+
+nxt_rbtree_node_t *
+nxt_rbtree_find_less_or_equal(nxt_rbtree_t *tree, nxt_rbtree_part_t *part)
+{
+ nxt_int_t n;
+ nxt_rbtree_node_t *node, *retval, *next, *sentinel;
+ nxt_rbtree_compare_t compare;
+
+ node = (nxt_rbtree_node_t *) part;
+
+ retval = NULL;
+ next = nxt_rbtree_root(tree);
+ sentinel = nxt_rbtree_sentinel(tree);
+ compare = nxt_rbtree_comparison_callback(tree);
+
+ while (next != sentinel) {
+ nxt_prefetch(next->left);
+ nxt_prefetch(next->right);
+
+ n = compare(node, next);
+
+ if (n < 0) {
+ next = next->left;
+
+ } else if (n > 0) {
+ retval = next;
+ next = next->right;
+
+ } else {
+ /* Exact match. */
+ return next;
+ }
+ }
+
+ return retval;
+}
+
+
+nxt_rbtree_node_t *
+nxt_rbtree_find_greater_or_equal(nxt_rbtree_t *tree, nxt_rbtree_part_t *part)
+{
+ nxt_int_t n;
+ nxt_rbtree_node_t *node, *retval, *next, *sentinel;
+ nxt_rbtree_compare_t compare;
+
+ node = (nxt_rbtree_node_t *) part;
+
+ retval = NULL;
+ next = nxt_rbtree_root(tree);
+ sentinel = nxt_rbtree_sentinel(tree);
+ compare = nxt_rbtree_comparison_callback(tree);
+
+ while (next != sentinel) {
+ nxt_prefetch(next->left);
+ nxt_prefetch(next->right);
+
+ n = compare(node, next);
+
+ if (n < 0) {
+ retval = next;
+ next = next->left;
+
+ } else if (n > 0) {
+ next = next->right;
+
+ } else {
+ /* Exact match. */
+ return next;
+ }
+ }
+
+ return retval;
+}
+
+
+void
+nxt_rbtree_delete(nxt_rbtree_t *tree, nxt_rbtree_part_t *part)
+{
+ nxt_uint_t color;
+ nxt_rbtree_node_t *node, *sentinel, *subst, *child;
+
+ node = (nxt_rbtree_node_t *) part;
+
+ subst = node;
+ sentinel = nxt_rbtree_sentinel(tree);
+
+ if (node->left == sentinel) {
+ child = node->right;
+
+ } else if (node->right == sentinel) {
+ child = node->left;
+
+ } else {
+ subst = nxt_rbtree_branch_min(tree, node->right);
+ child = subst->right;
+ }
+
+ nxt_rbtree_parent_relink(child, subst);
+
+ color = subst->color;
+
+ if (subst != node) {
+ /* Move the subst node to the deleted node position in the tree. */
+
+ subst->color = node->color;
+
+ subst->left = node->left;
+ subst->left->parent = subst;
+
+ subst->right = node->right;
+ subst->right->parent = subst;
+
+ nxt_rbtree_parent_relink(subst, node);
+ }
+
+#if (NXT_DEBUG)
+ node->left = NULL;
+ node->right = NULL;
+ node->parent = NULL;
+#endif
+
+ if (color == NXT_RBTREE_BLACK) {
+ nxt_rbtree_delete_fixup(tree, child);
+ }
+}
+
+
+static void
+nxt_rbtree_delete_fixup(nxt_rbtree_t *tree, nxt_rbtree_node_t *node)
+{
+ nxt_rbtree_node_t *parent, *sibling;
+
+ while (node != nxt_rbtree_root(tree) && node->color == NXT_RBTREE_BLACK) {
+ /*
+ * Prefetching parent nodes does not help here according
+ * to microbenchmarks.
+ */
+
+ parent = node->parent;
+
+ if (node == parent->left) {
+ sibling = parent->right;
+
+ if (sibling->color != NXT_RBTREE_BLACK) {
+
+ sibling->color = NXT_RBTREE_BLACK;
+ parent->color = NXT_RBTREE_RED;
+
+ nxt_rbtree_left_rotate(parent);
+
+ sibling = parent->right;
+ }
+
+ if (sibling->right->color == NXT_RBTREE_BLACK) {
+
+ sibling->color = NXT_RBTREE_RED;
+
+ if (sibling->left->color == NXT_RBTREE_BLACK) {
+ node = parent;
+ continue;
+ }
+
+ sibling->left->color = NXT_RBTREE_BLACK;
+
+ nxt_rbtree_right_rotate(sibling);
+ /*
+ * If the node is the leaf sentinel then the right
+ * rotate above changes its parent so a sibling below
+ * becames the leaf sentinel as well and this causes
+ * segmentation fault. This is the reason why usual
+ * red-black tree implementations with a leaf sentinel
+ * which does not require to test leaf nodes at all
+ * nevertheless test the leaf sentinel in the left and
+ * right rotate procedures. Since according to the
+ * algorithm node->parent must not be changed by both
+ * the left and right rotates above, it can be cached
+ * in a local variable. This not only eliminates the
+ * sentinel test in nxt_rbtree_parent_relink() but also
+ * decreases the code size because C forces to reload
+ * non-restrict pointers.
+ */
+ sibling = parent->right;
+ }
+
+ sibling->color = parent->color;
+ parent->color = NXT_RBTREE_BLACK;
+ sibling->right->color = NXT_RBTREE_BLACK;
+
+ nxt_rbtree_left_rotate(parent);
+
+ break;
+
+ } else {
+ sibling = parent->left;
+
+ if (sibling->color != NXT_RBTREE_BLACK) {
+
+ sibling->color = NXT_RBTREE_BLACK;
+ parent->color = NXT_RBTREE_RED;
+
+ nxt_rbtree_right_rotate(parent);
+
+ sibling = parent->left;
+ }
+
+ if (sibling->left->color == NXT_RBTREE_BLACK) {
+
+ sibling->color = NXT_RBTREE_RED;
+
+ if (sibling->right->color == NXT_RBTREE_BLACK) {
+ node = parent;
+ continue;
+ }
+
+ sibling->right->color = NXT_RBTREE_BLACK;
+
+ nxt_rbtree_left_rotate(sibling);
+
+ /* See the comment in the symmetric branch above. */
+ sibling = parent->left;
+ }
+
+ sibling->color = parent->color;
+ parent->color = NXT_RBTREE_BLACK;
+ sibling->left->color = NXT_RBTREE_BLACK;
+
+ nxt_rbtree_right_rotate(parent);
+
+ break;
+ }
+ }
+
+ node->color = NXT_RBTREE_BLACK;
+}
+
+
+nxt_inline void
+nxt_rbtree_left_rotate(nxt_rbtree_node_t *node)
+{
+ nxt_rbtree_node_t *child;
+
+ child = node->right;
+ node->right = child->left;
+ child->left->parent = node;
+ child->left = node;
+
+ nxt_rbtree_parent_relink(child, node);
+
+ node->parent = child;
+}
+
+
+nxt_inline void
+nxt_rbtree_right_rotate(nxt_rbtree_node_t *node)
+{
+ nxt_rbtree_node_t *child;
+
+ child = node->left;
+ node->left = child->right;
+ child->right->parent = node;
+ child->right = node;
+
+ nxt_rbtree_parent_relink(child, node);
+
+ node->parent = child;
+}
+
+
+/* Relink a parent from the node to the subst node. */
+
+nxt_inline void
+nxt_rbtree_parent_relink(nxt_rbtree_node_t *subst, nxt_rbtree_node_t *node)
+{
+ nxt_rbtree_node_t *parent, **link;
+
+ parent = node->parent;
+ /*
+ * The leaf sentinel's parent can be safely changed here.
+ * See the comment in nxt_rbtree_delete_fixup() for details.
+ */
+ subst->parent = parent;
+ /*
+ * If the node's parent is the root sentinel it is safely changed
+ * because the root sentinel's left child is the tree root.
+ */
+ link = (node == parent->left) ? &parent->left : &parent->right;
+ *link = subst;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_RBTREE_H_INCLUDED_
+#define _NXT_RBTREE_H_INCLUDED_
+
+
+typedef struct nxt_rbtree_node_s nxt_rbtree_node_t;
+
+struct nxt_rbtree_node_s {
+ nxt_rbtree_node_t *left;
+ nxt_rbtree_node_t *right;
+ nxt_rbtree_node_t *parent;
+
+ uint8_t color;
+};
+
+
+typedef struct {
+ nxt_rbtree_node_t *left;
+ nxt_rbtree_node_t *right;
+ nxt_rbtree_node_t *parent;
+} nxt_rbtree_part_t;
+
+
+#define NXT_RBTREE_NODE(node) \
+ nxt_rbtree_part_t node; \
+ uint8_t node##_color
+
+
+#define NXT_RBTREE_NODE_INIT { NULL, NULL, NULL }, 0
+
+
+typedef struct {
+ nxt_rbtree_node_t sentinel;
+} nxt_rbtree_t;
+
+
+typedef nxt_int_t (*nxt_rbtree_compare_t)(nxt_rbtree_node_t *node1,
+ nxt_rbtree_node_t *node2);
+
+
+#define nxt_rbtree_root(tree) \
+ ((tree)->sentinel.left)
+
+
+#define nxt_rbtree_sentinel(tree) \
+ (&(tree)->sentinel)
+
+
+#define nxt_rbtree_is_empty(tree) \
+ (nxt_rbtree_root(tree) == nxt_rbtree_sentinel(tree))
+
+
+#define nxt_rbtree_min(tree) \
+ nxt_rbtree_branch_min(tree, &(tree)->sentinel)
+
+
+nxt_inline nxt_rbtree_node_t *
+nxt_rbtree_branch_min(nxt_rbtree_t *tree, nxt_rbtree_node_t *node)
+{
+ while (node->left != nxt_rbtree_sentinel(tree)) {
+ node = node->left;
+ }
+
+ return node;
+}
+
+
+#define nxt_rbtree_is_there_successor(tree, node) \
+ ((node) != nxt_rbtree_sentinel(tree))
+
+
+nxt_inline nxt_rbtree_node_t *
+nxt_rbtree_node_successor(nxt_rbtree_t *tree, nxt_rbtree_node_t *node)
+{
+ nxt_rbtree_node_t *parent;
+
+ if (node->right != nxt_rbtree_sentinel(tree)) {
+ return nxt_rbtree_branch_min(tree, node->right);
+ }
+
+ for ( ;; ) {
+ parent = node->parent;
+
+ /*
+ * Explicit test for a root node is not required here, because
+ * the root node is always the left child of the sentinel.
+ */
+ if (node == parent->left) {
+ return parent;
+ }
+
+ node = parent;
+ }
+}
+
+
+NXT_EXPORT void nxt_rbtree_init(nxt_rbtree_t *tree,
+ nxt_rbtree_compare_t compare);
+NXT_EXPORT void nxt_rbtree_insert(nxt_rbtree_t *tree, nxt_rbtree_part_t *node);
+NXT_EXPORT nxt_rbtree_node_t *nxt_rbtree_find(nxt_rbtree_t *tree,
+ nxt_rbtree_part_t *node);
+NXT_EXPORT nxt_rbtree_node_t *nxt_rbtree_find_less_or_equal(nxt_rbtree_t *tree,
+ nxt_rbtree_part_t *node);
+NXT_EXPORT nxt_rbtree_node_t
+ *nxt_rbtree_find_greater_or_equal(nxt_rbtree_t *tree,
+ nxt_rbtree_part_t *node);
+NXT_EXPORT void nxt_rbtree_delete(nxt_rbtree_t *tree, nxt_rbtree_part_t *node);
+
+
+#endif /* _NXT_RBTREE_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_STUB_H_INCLUDED_
+#define _NXT_STUB_H_INCLUDED_
+
+
+#define nxt_max(val1, val2) \
+ ((val1 < val2) ? (val2) : (val1))
+
+#define nxt_min(val1, val2) \
+ ((val1 < val2) ? (val1) : (val2))
+
+#define nxt_lowcase(c) \
+ (u_char) ((c >= 'A' && c <= 'Z') ? c | 0x20 : c)
+
+#define nxt_strstr_eq(s1, s2) \
+ (((s1)->len == (s2)->len) \
+ && (memcmp((s1)->data, (s2)->data, (s1)->len) == 0))
+
+
+#define NXT_OK 0
+#define NXT_ERROR (-1)
+#define NXT_AGAIN (-2)
+#define NXT_DECLINED (-3)
+#define NXT_DONE (-4)
+
+
+typedef struct {
+ void *(*alloc)(void *mem, size_t size);
+ void *(*zalloc)(void *mem, size_t size);
+ void *(*align)(void *mem, size_t alignment, size_t size);
+ void *(*zalign)(void *mem, size_t alignment, size_t size);
+ void (*free)(void *mem, void *p);
+ void (*alert)(void *trace, const char *fmt, ...);
+ void nxt_cdecl (*trace)(void *trace, const char *fmt, ...);
+} nxt_mem_proto_t;
+
+
+typedef struct {
+ size_t len;
+ u_char *data;
+} nxt_str_t;
+
+
+#define nxt_string(str) { sizeof(str) - 1, (u_char *) str }
+#define nxt_string_zero(str) { sizeof(str), (u_char *) str }
+#define nxt_null_string { 0, NULL }
+
+
+#define nxt_thread_log_alert(...)
+#define nxt_thread_log_error(...)
+#define nxt_log_error(...)
+#define nxt_thread_log_debug(...)
+#define nxt_number_parse(a, b) 1
+
+#define NXT_DOUBLE_LEN 1024
+
+#include <unistd.h>
+#define nxt_pagesize() getpagesize()
+
+
+#endif /* _NXT_STUB_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_TYPES_H_INCLUDED_
+#define _NXT_TYPES_H_INCLUDED_
+
+
+/*
+ * off_t is 32 bit on Linux, Solaris and HP-UX by default.
+ * Must be before <sys/types.h>.
+ */
+#define _FILE_OFFSET_BITS 64
+
+/* u_char, u_int, int8_t, int32_t, int64_t, size_t, off_t. */
+#include <sys/types.h>
+#include <inttypes.h>
+
+
+#if (__LP64__)
+#define NXT_64BIT 1
+#define NXT_PTR_SIZE 8
+#else
+#define NXT_64BIT 0
+#define NXT_PTR_SIZE 4
+#endif
+
+
+/*
+ * nxt_int_t corresponds to the most efficient integer type, an architecture
+ * word. It is usually the long type, however on Win64 the long is int32_t,
+ * so pointer size suits better. nxt_int_t must be no less than int32_t.
+ */
+
+#if (__amd64__)
+/*
+ * AMD64 64-bit multiplication and division operations are slower and 64-bit
+ * instructions are longer.
+ */
+#define NXT_INT_T_SIZE 4
+typedef int nxt_int_t;
+typedef u_int nxt_uint_t;
+
+#else
+#define NXT_INT_T_SIZE NXT_PTR_SIZE
+typedef intptr_t nxt_int_t;
+typedef uintptr_t nxt_uint_t;
+#endif
+
+
+typedef nxt_uint_t nxt_bool_t;
+
+
+/*
+ * nxt_off_t corresponds to OS's off_t, a file offset type. Although Linux,
+ * Solaris, and HP-UX define both off_t and off64_t, setting _FILE_OFFSET_BITS
+ * to 64 defines off_t as off64_t.
+ */
+#if (NXT_WINDOWS)
+/* Windows defines off_t as a 32-bit "long". */
+typedef __int64 nxt_off_t;
+
+#else
+typedef off_t nxt_off_t;
+#endif
+
+
+/*
+ * nxt_time_t corresponds to OS's time_t, time in seconds. nxt_time_t is
+ * a signed integer. OS's time_t may be an integer or real-floating type,
+ * though it is usually a signed 32-bit or 64-bit integer depending on
+ * platform bits length. There are however exceptions, e.g., time_t is:
+ * 32-bit on 64-bit NetBSD prior to 6.0 version;
+ * 64-bit on 32-bit NetBSD 6.0;
+ * 32-bit on 64-bit OpenBSD;
+ * 64-bit in Linux x32 ABI;
+ * 64-bit in 32-bit Visual Studio C++ 2005.
+ *
+ * Besides, QNX defines time_t as uint32_t.
+ */
+#if (NXT_QNX)
+/* Y2038 fix: "typedef int64_t nxt_time_t". */
+typedef int32_t nxt_time_t;
+
+#else
+/* Y2038, if time_t is 32-bit integer. */
+typedef time_t nxt_time_t;
+#endif
+
+
+#endif /* _NXT_TYPES_H_INCLUDED_ */
--- /dev/null
+
+/*
+ * 26 128-bytes blocks, 521 pointers.
+ * 14920 bytes on 32-bit platforms, 17000 bytes on 64-bit platforms.
+ */
+
+#define NXT_UNICODE_MAX_LOWCASE 0x10427
+
+#define NXT_UNICODE_BLOCK_SIZE 128
+
+
+static const uint32_t nxt_unicode_block_000[128] nxt_aligned(64) = {
+ 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007,
+ 0x00008, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x0000e, 0x0000f,
+ 0x00010, 0x00011, 0x00012, 0x00013, 0x00014, 0x00015, 0x00016, 0x00017,
+ 0x00018, 0x00019, 0x0001a, 0x0001b, 0x0001c, 0x0001d, 0x0001e, 0x0001f,
+ 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027,
+ 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f,
+ 0x00040, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067,
+ 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f,
+ 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077,
+ 0x00078, 0x00079, 0x0007a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f,
+ 0x00060, 0x00061, 0x00062, 0x00063, 0x00064, 0x00065, 0x00066, 0x00067,
+ 0x00068, 0x00069, 0x0006a, 0x0006b, 0x0006c, 0x0006d, 0x0006e, 0x0006f,
+ 0x00070, 0x00071, 0x00072, 0x00073, 0x00074, 0x00075, 0x00076, 0x00077,
+ 0x00078, 0x00079, 0x0007a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f,
+};
+
+
+static const uint32_t nxt_unicode_block_001[128] nxt_aligned(64) = {
+ 0x00080, 0x00081, 0x00082, 0x00083, 0x00084, 0x00085, 0x00086, 0x00087,
+ 0x00088, 0x00089, 0x0008a, 0x0008b, 0x0008c, 0x0008d, 0x0008e, 0x0008f,
+ 0x00090, 0x00091, 0x00092, 0x00093, 0x00094, 0x00095, 0x00096, 0x00097,
+ 0x00098, 0x00099, 0x0009a, 0x0009b, 0x0009c, 0x0009d, 0x0009e, 0x0009f,
+ 0x000a0, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7,
+ 0x000a8, 0x000a9, 0x000aa, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af,
+ 0x000b0, 0x000b1, 0x000b2, 0x000b3, 0x000b4, 0x003bc, 0x000b6, 0x000b7,
+ 0x000b8, 0x000b9, 0x000ba, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf,
+ 0x000e0, 0x000e1, 0x000e2, 0x000e3, 0x000e4, 0x000e5, 0x000e6, 0x000e7,
+ 0x000e8, 0x000e9, 0x000ea, 0x000eb, 0x000ec, 0x000ed, 0x000ee, 0x000ef,
+ 0x000f0, 0x000f1, 0x000f2, 0x000f3, 0x000f4, 0x000f5, 0x000f6, 0x000d7,
+ 0x000f8, 0x000f9, 0x000fa, 0x000fb, 0x000fc, 0x000fd, 0x000fe, 0x000df,
+ 0x000e0, 0x000e1, 0x000e2, 0x000e3, 0x000e4, 0x000e5, 0x000e6, 0x000e7,
+ 0x000e8, 0x000e9, 0x000ea, 0x000eb, 0x000ec, 0x000ed, 0x000ee, 0x000ef,
+ 0x000f0, 0x000f1, 0x000f2, 0x000f3, 0x000f4, 0x000f5, 0x000f6, 0x000f7,
+ 0x000f8, 0x000f9, 0x000fa, 0x000fb, 0x000fc, 0x000fd, 0x000fe, 0x000ff,
+};
+
+
+static const uint32_t nxt_unicode_block_002[128] nxt_aligned(64) = {
+ 0x00101, 0x00101, 0x00103, 0x00103, 0x00105, 0x00105, 0x00107, 0x00107,
+ 0x00109, 0x00109, 0x0010b, 0x0010b, 0x0010d, 0x0010d, 0x0010f, 0x0010f,
+ 0x00111, 0x00111, 0x00113, 0x00113, 0x00115, 0x00115, 0x00117, 0x00117,
+ 0x00119, 0x00119, 0x0011b, 0x0011b, 0x0011d, 0x0011d, 0x0011f, 0x0011f,
+ 0x00121, 0x00121, 0x00123, 0x00123, 0x00125, 0x00125, 0x00127, 0x00127,
+ 0x00129, 0x00129, 0x0012b, 0x0012b, 0x0012d, 0x0012d, 0x0012f, 0x0012f,
+ 0x00130, 0x00131, 0x00133, 0x00133, 0x00135, 0x00135, 0x00137, 0x00137,
+ 0x00138, 0x0013a, 0x0013a, 0x0013c, 0x0013c, 0x0013e, 0x0013e, 0x00140,
+ 0x00140, 0x00142, 0x00142, 0x00144, 0x00144, 0x00146, 0x00146, 0x00148,
+ 0x00148, 0x00149, 0x0014b, 0x0014b, 0x0014d, 0x0014d, 0x0014f, 0x0014f,
+ 0x00151, 0x00151, 0x00153, 0x00153, 0x00155, 0x00155, 0x00157, 0x00157,
+ 0x00159, 0x00159, 0x0015b, 0x0015b, 0x0015d, 0x0015d, 0x0015f, 0x0015f,
+ 0x00161, 0x00161, 0x00163, 0x00163, 0x00165, 0x00165, 0x00167, 0x00167,
+ 0x00169, 0x00169, 0x0016b, 0x0016b, 0x0016d, 0x0016d, 0x0016f, 0x0016f,
+ 0x00171, 0x00171, 0x00173, 0x00173, 0x00175, 0x00175, 0x00177, 0x00177,
+ 0x000ff, 0x0017a, 0x0017a, 0x0017c, 0x0017c, 0x0017e, 0x0017e, 0x00073,
+};
+
+
+static const uint32_t nxt_unicode_block_003[128] nxt_aligned(64) = {
+ 0x00180, 0x00253, 0x00183, 0x00183, 0x00185, 0x00185, 0x00254, 0x00188,
+ 0x00188, 0x00256, 0x00257, 0x0018c, 0x0018c, 0x0018d, 0x001dd, 0x00259,
+ 0x0025b, 0x00192, 0x00192, 0x00260, 0x00263, 0x00195, 0x00269, 0x00268,
+ 0x00199, 0x00199, 0x0019a, 0x0019b, 0x0026f, 0x00272, 0x0019e, 0x00275,
+ 0x001a1, 0x001a1, 0x001a3, 0x001a3, 0x001a5, 0x001a5, 0x00280, 0x001a8,
+ 0x001a8, 0x00283, 0x001aa, 0x001ab, 0x001ad, 0x001ad, 0x00288, 0x001b0,
+ 0x001b0, 0x0028a, 0x0028b, 0x001b4, 0x001b4, 0x001b6, 0x001b6, 0x00292,
+ 0x001b9, 0x001b9, 0x001ba, 0x001bb, 0x001bd, 0x001bd, 0x001be, 0x001bf,
+ 0x001c0, 0x001c1, 0x001c2, 0x001c3, 0x001c6, 0x001c6, 0x001c6, 0x001c9,
+ 0x001c9, 0x001c9, 0x001cc, 0x001cc, 0x001cc, 0x001ce, 0x001ce, 0x001d0,
+ 0x001d0, 0x001d2, 0x001d2, 0x001d4, 0x001d4, 0x001d6, 0x001d6, 0x001d8,
+ 0x001d8, 0x001da, 0x001da, 0x001dc, 0x001dc, 0x001dd, 0x001df, 0x001df,
+ 0x001e1, 0x001e1, 0x001e3, 0x001e3, 0x001e5, 0x001e5, 0x001e7, 0x001e7,
+ 0x001e9, 0x001e9, 0x001eb, 0x001eb, 0x001ed, 0x001ed, 0x001ef, 0x001ef,
+ 0x001f0, 0x001f3, 0x001f3, 0x001f3, 0x001f5, 0x001f5, 0x00195, 0x001bf,
+ 0x001f9, 0x001f9, 0x001fb, 0x001fb, 0x001fd, 0x001fd, 0x001ff, 0x001ff,
+};
+
+
+static const uint32_t nxt_unicode_block_004[128] nxt_aligned(64) = {
+ 0x00201, 0x00201, 0x00203, 0x00203, 0x00205, 0x00205, 0x00207, 0x00207,
+ 0x00209, 0x00209, 0x0020b, 0x0020b, 0x0020d, 0x0020d, 0x0020f, 0x0020f,
+ 0x00211, 0x00211, 0x00213, 0x00213, 0x00215, 0x00215, 0x00217, 0x00217,
+ 0x00219, 0x00219, 0x0021b, 0x0021b, 0x0021d, 0x0021d, 0x0021f, 0x0021f,
+ 0x0019e, 0x00221, 0x00223, 0x00223, 0x00225, 0x00225, 0x00227, 0x00227,
+ 0x00229, 0x00229, 0x0022b, 0x0022b, 0x0022d, 0x0022d, 0x0022f, 0x0022f,
+ 0x00231, 0x00231, 0x00233, 0x00233, 0x00234, 0x00235, 0x00236, 0x00237,
+ 0x00238, 0x00239, 0x02c65, 0x0023c, 0x0023c, 0x0019a, 0x02c66, 0x0023f,
+ 0x00240, 0x00242, 0x00242, 0x00180, 0x00289, 0x0028c, 0x00247, 0x00247,
+ 0x00249, 0x00249, 0x0024b, 0x0024b, 0x0024d, 0x0024d, 0x0024f, 0x0024f,
+ 0x00250, 0x00251, 0x00252, 0x00253, 0x00254, 0x00255, 0x00256, 0x00257,
+ 0x00258, 0x00259, 0x0025a, 0x0025b, 0x0025c, 0x0025d, 0x0025e, 0x0025f,
+ 0x00260, 0x00261, 0x00262, 0x00263, 0x00264, 0x00265, 0x00266, 0x00267,
+ 0x00268, 0x00269, 0x0026a, 0x0026b, 0x0026c, 0x0026d, 0x0026e, 0x0026f,
+ 0x00270, 0x00271, 0x00272, 0x00273, 0x00274, 0x00275, 0x00276, 0x00277,
+ 0x00278, 0x00279, 0x0027a, 0x0027b, 0x0027c, 0x0027d, 0x0027e, 0x0027f,
+};
+
+
+static const uint32_t nxt_unicode_block_006[128] nxt_aligned(64) = {
+ 0x00300, 0x00301, 0x00302, 0x00303, 0x00304, 0x00305, 0x00306, 0x00307,
+ 0x00308, 0x00309, 0x0030a, 0x0030b, 0x0030c, 0x0030d, 0x0030e, 0x0030f,
+ 0x00310, 0x00311, 0x00312, 0x00313, 0x00314, 0x00315, 0x00316, 0x00317,
+ 0x00318, 0x00319, 0x0031a, 0x0031b, 0x0031c, 0x0031d, 0x0031e, 0x0031f,
+ 0x00320, 0x00321, 0x00322, 0x00323, 0x00324, 0x00325, 0x00326, 0x00327,
+ 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f,
+ 0x00330, 0x00331, 0x00332, 0x00333, 0x00334, 0x00335, 0x00336, 0x00337,
+ 0x00338, 0x00339, 0x0033a, 0x0033b, 0x0033c, 0x0033d, 0x0033e, 0x0033f,
+ 0x00340, 0x00341, 0x00342, 0x00343, 0x00344, 0x003b9, 0x00346, 0x00347,
+ 0x00348, 0x00349, 0x0034a, 0x0034b, 0x0034c, 0x0034d, 0x0034e, 0x0034f,
+ 0x00350, 0x00351, 0x00352, 0x00353, 0x00354, 0x00355, 0x00356, 0x00357,
+ 0x00358, 0x00359, 0x0035a, 0x0035b, 0x0035c, 0x0035d, 0x0035e, 0x0035f,
+ 0x00360, 0x00361, 0x00362, 0x00363, 0x00364, 0x00365, 0x00366, 0x00367,
+ 0x00368, 0x00369, 0x0036a, 0x0036b, 0x0036c, 0x0036d, 0x0036e, 0x0036f,
+ 0x00371, 0x00371, 0x00373, 0x00373, 0x00374, 0x00375, 0x00377, 0x00377,
+ 0x00378, 0x00379, 0x0037a, 0x0037b, 0x0037c, 0x0037d, 0x0037e, 0x0037f,
+};
+
+
+static const uint32_t nxt_unicode_block_007[128] nxt_aligned(64) = {
+ 0x00380, 0x00381, 0x00382, 0x00383, 0x00384, 0x00385, 0x003ac, 0x00387,
+ 0x003ad, 0x003ae, 0x003af, 0x0038b, 0x003cc, 0x0038d, 0x003cd, 0x003ce,
+ 0x00390, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7,
+ 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf,
+ 0x003c0, 0x003c1, 0x003a2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7,
+ 0x003c8, 0x003c9, 0x003ca, 0x003cb, 0x003ac, 0x003ad, 0x003ae, 0x003af,
+ 0x003b0, 0x003b1, 0x003b2, 0x003b3, 0x003b4, 0x003b5, 0x003b6, 0x003b7,
+ 0x003b8, 0x003b9, 0x003ba, 0x003bb, 0x003bc, 0x003bd, 0x003be, 0x003bf,
+ 0x003c0, 0x003c1, 0x003c3, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7,
+ 0x003c8, 0x003c9, 0x003ca, 0x003cb, 0x003cc, 0x003cd, 0x003ce, 0x003d7,
+ 0x003b2, 0x003b8, 0x003d2, 0x003d3, 0x003d4, 0x003c6, 0x003c0, 0x003d7,
+ 0x003d9, 0x003d9, 0x003db, 0x003db, 0x003dd, 0x003dd, 0x003df, 0x003df,
+ 0x003e1, 0x003e1, 0x003e3, 0x003e3, 0x003e5, 0x003e5, 0x003e7, 0x003e7,
+ 0x003e9, 0x003e9, 0x003eb, 0x003eb, 0x003ed, 0x003ed, 0x003ef, 0x003ef,
+ 0x003ba, 0x003c1, 0x003f2, 0x003f3, 0x003b8, 0x003b5, 0x003f6, 0x003f8,
+ 0x003f8, 0x003f2, 0x003fb, 0x003fb, 0x003fc, 0x0037b, 0x0037c, 0x0037d,
+};
+
+
+static const uint32_t nxt_unicode_block_008[128] nxt_aligned(64) = {
+ 0x00450, 0x00451, 0x00452, 0x00453, 0x00454, 0x00455, 0x00456, 0x00457,
+ 0x00458, 0x00459, 0x0045a, 0x0045b, 0x0045c, 0x0045d, 0x0045e, 0x0045f,
+ 0x00430, 0x00431, 0x00432, 0x00433, 0x00434, 0x00435, 0x00436, 0x00437,
+ 0x00438, 0x00439, 0x0043a, 0x0043b, 0x0043c, 0x0043d, 0x0043e, 0x0043f,
+ 0x00440, 0x00441, 0x00442, 0x00443, 0x00444, 0x00445, 0x00446, 0x00447,
+ 0x00448, 0x00449, 0x0044a, 0x0044b, 0x0044c, 0x0044d, 0x0044e, 0x0044f,
+ 0x00430, 0x00431, 0x00432, 0x00433, 0x00434, 0x00435, 0x00436, 0x00437,
+ 0x00438, 0x00439, 0x0043a, 0x0043b, 0x0043c, 0x0043d, 0x0043e, 0x0043f,
+ 0x00440, 0x00441, 0x00442, 0x00443, 0x00444, 0x00445, 0x00446, 0x00447,
+ 0x00448, 0x00449, 0x0044a, 0x0044b, 0x0044c, 0x0044d, 0x0044e, 0x0044f,
+ 0x00450, 0x00451, 0x00452, 0x00453, 0x00454, 0x00455, 0x00456, 0x00457,
+ 0x00458, 0x00459, 0x0045a, 0x0045b, 0x0045c, 0x0045d, 0x0045e, 0x0045f,
+ 0x00461, 0x00461, 0x00463, 0x00463, 0x00465, 0x00465, 0x00467, 0x00467,
+ 0x00469, 0x00469, 0x0046b, 0x0046b, 0x0046d, 0x0046d, 0x0046f, 0x0046f,
+ 0x00471, 0x00471, 0x00473, 0x00473, 0x00475, 0x00475, 0x00477, 0x00477,
+ 0x00479, 0x00479, 0x0047b, 0x0047b, 0x0047d, 0x0047d, 0x0047f, 0x0047f,
+};
+
+
+static const uint32_t nxt_unicode_block_009[128] nxt_aligned(64) = {
+ 0x00481, 0x00481, 0x00482, 0x00483, 0x00484, 0x00485, 0x00486, 0x00487,
+ 0x00488, 0x00489, 0x0048b, 0x0048b, 0x0048d, 0x0048d, 0x0048f, 0x0048f,
+ 0x00491, 0x00491, 0x00493, 0x00493, 0x00495, 0x00495, 0x00497, 0x00497,
+ 0x00499, 0x00499, 0x0049b, 0x0049b, 0x0049d, 0x0049d, 0x0049f, 0x0049f,
+ 0x004a1, 0x004a1, 0x004a3, 0x004a3, 0x004a5, 0x004a5, 0x004a7, 0x004a7,
+ 0x004a9, 0x004a9, 0x004ab, 0x004ab, 0x004ad, 0x004ad, 0x004af, 0x004af,
+ 0x004b1, 0x004b1, 0x004b3, 0x004b3, 0x004b5, 0x004b5, 0x004b7, 0x004b7,
+ 0x004b9, 0x004b9, 0x004bb, 0x004bb, 0x004bd, 0x004bd, 0x004bf, 0x004bf,
+ 0x004cf, 0x004c2, 0x004c2, 0x004c4, 0x004c4, 0x004c6, 0x004c6, 0x004c8,
+ 0x004c8, 0x004ca, 0x004ca, 0x004cc, 0x004cc, 0x004ce, 0x004ce, 0x004cf,
+ 0x004d1, 0x004d1, 0x004d3, 0x004d3, 0x004d5, 0x004d5, 0x004d7, 0x004d7,
+ 0x004d9, 0x004d9, 0x004db, 0x004db, 0x004dd, 0x004dd, 0x004df, 0x004df,
+ 0x004e1, 0x004e1, 0x004e3, 0x004e3, 0x004e5, 0x004e5, 0x004e7, 0x004e7,
+ 0x004e9, 0x004e9, 0x004eb, 0x004eb, 0x004ed, 0x004ed, 0x004ef, 0x004ef,
+ 0x004f1, 0x004f1, 0x004f3, 0x004f3, 0x004f5, 0x004f5, 0x004f7, 0x004f7,
+ 0x004f9, 0x004f9, 0x004fb, 0x004fb, 0x004fd, 0x004fd, 0x004ff, 0x004ff,
+};
+
+
+static const uint32_t nxt_unicode_block_00a[128] nxt_aligned(64) = {
+ 0x00501, 0x00501, 0x00503, 0x00503, 0x00505, 0x00505, 0x00507, 0x00507,
+ 0x00509, 0x00509, 0x0050b, 0x0050b, 0x0050d, 0x0050d, 0x0050f, 0x0050f,
+ 0x00511, 0x00511, 0x00513, 0x00513, 0x00515, 0x00515, 0x00517, 0x00517,
+ 0x00519, 0x00519, 0x0051b, 0x0051b, 0x0051d, 0x0051d, 0x0051f, 0x0051f,
+ 0x00521, 0x00521, 0x00523, 0x00523, 0x00525, 0x00525, 0x00527, 0x00527,
+ 0x00528, 0x00529, 0x0052a, 0x0052b, 0x0052c, 0x0052d, 0x0052e, 0x0052f,
+ 0x00530, 0x00561, 0x00562, 0x00563, 0x00564, 0x00565, 0x00566, 0x00567,
+ 0x00568, 0x00569, 0x0056a, 0x0056b, 0x0056c, 0x0056d, 0x0056e, 0x0056f,
+ 0x00570, 0x00571, 0x00572, 0x00573, 0x00574, 0x00575, 0x00576, 0x00577,
+ 0x00578, 0x00579, 0x0057a, 0x0057b, 0x0057c, 0x0057d, 0x0057e, 0x0057f,
+ 0x00580, 0x00581, 0x00582, 0x00583, 0x00584, 0x00585, 0x00586, 0x00557,
+ 0x00558, 0x00559, 0x0055a, 0x0055b, 0x0055c, 0x0055d, 0x0055e, 0x0055f,
+ 0x00560, 0x00561, 0x00562, 0x00563, 0x00564, 0x00565, 0x00566, 0x00567,
+ 0x00568, 0x00569, 0x0056a, 0x0056b, 0x0056c, 0x0056d, 0x0056e, 0x0056f,
+ 0x00570, 0x00571, 0x00572, 0x00573, 0x00574, 0x00575, 0x00576, 0x00577,
+ 0x00578, 0x00579, 0x0057a, 0x0057b, 0x0057c, 0x0057d, 0x0057e, 0x0057f,
+};
+
+
+static const uint32_t nxt_unicode_block_021[128] nxt_aligned(64) = {
+ 0x01080, 0x01081, 0x01082, 0x01083, 0x01084, 0x01085, 0x01086, 0x01087,
+ 0x01088, 0x01089, 0x0108a, 0x0108b, 0x0108c, 0x0108d, 0x0108e, 0x0108f,
+ 0x01090, 0x01091, 0x01092, 0x01093, 0x01094, 0x01095, 0x01096, 0x01097,
+ 0x01098, 0x01099, 0x0109a, 0x0109b, 0x0109c, 0x0109d, 0x0109e, 0x0109f,
+ 0x02d00, 0x02d01, 0x02d02, 0x02d03, 0x02d04, 0x02d05, 0x02d06, 0x02d07,
+ 0x02d08, 0x02d09, 0x02d0a, 0x02d0b, 0x02d0c, 0x02d0d, 0x02d0e, 0x02d0f,
+ 0x02d10, 0x02d11, 0x02d12, 0x02d13, 0x02d14, 0x02d15, 0x02d16, 0x02d17,
+ 0x02d18, 0x02d19, 0x02d1a, 0x02d1b, 0x02d1c, 0x02d1d, 0x02d1e, 0x02d1f,
+ 0x02d20, 0x02d21, 0x02d22, 0x02d23, 0x02d24, 0x02d25, 0x010c6, 0x02d27,
+ 0x010c8, 0x010c9, 0x010ca, 0x010cb, 0x010cc, 0x02d2d, 0x010ce, 0x010cf,
+ 0x010d0, 0x010d1, 0x010d2, 0x010d3, 0x010d4, 0x010d5, 0x010d6, 0x010d7,
+ 0x010d8, 0x010d9, 0x010da, 0x010db, 0x010dc, 0x010dd, 0x010de, 0x010df,
+ 0x010e0, 0x010e1, 0x010e2, 0x010e3, 0x010e4, 0x010e5, 0x010e6, 0x010e7,
+ 0x010e8, 0x010e9, 0x010ea, 0x010eb, 0x010ec, 0x010ed, 0x010ee, 0x010ef,
+ 0x010f0, 0x010f1, 0x010f2, 0x010f3, 0x010f4, 0x010f5, 0x010f6, 0x010f7,
+ 0x010f8, 0x010f9, 0x010fa, 0x010fb, 0x010fc, 0x010fd, 0x010fe, 0x010ff,
+};
+
+
+static const uint32_t nxt_unicode_block_03c[128] nxt_aligned(64) = {
+ 0x01e01, 0x01e01, 0x01e03, 0x01e03, 0x01e05, 0x01e05, 0x01e07, 0x01e07,
+ 0x01e09, 0x01e09, 0x01e0b, 0x01e0b, 0x01e0d, 0x01e0d, 0x01e0f, 0x01e0f,
+ 0x01e11, 0x01e11, 0x01e13, 0x01e13, 0x01e15, 0x01e15, 0x01e17, 0x01e17,
+ 0x01e19, 0x01e19, 0x01e1b, 0x01e1b, 0x01e1d, 0x01e1d, 0x01e1f, 0x01e1f,
+ 0x01e21, 0x01e21, 0x01e23, 0x01e23, 0x01e25, 0x01e25, 0x01e27, 0x01e27,
+ 0x01e29, 0x01e29, 0x01e2b, 0x01e2b, 0x01e2d, 0x01e2d, 0x01e2f, 0x01e2f,
+ 0x01e31, 0x01e31, 0x01e33, 0x01e33, 0x01e35, 0x01e35, 0x01e37, 0x01e37,
+ 0x01e39, 0x01e39, 0x01e3b, 0x01e3b, 0x01e3d, 0x01e3d, 0x01e3f, 0x01e3f,
+ 0x01e41, 0x01e41, 0x01e43, 0x01e43, 0x01e45, 0x01e45, 0x01e47, 0x01e47,
+ 0x01e49, 0x01e49, 0x01e4b, 0x01e4b, 0x01e4d, 0x01e4d, 0x01e4f, 0x01e4f,
+ 0x01e51, 0x01e51, 0x01e53, 0x01e53, 0x01e55, 0x01e55, 0x01e57, 0x01e57,
+ 0x01e59, 0x01e59, 0x01e5b, 0x01e5b, 0x01e5d, 0x01e5d, 0x01e5f, 0x01e5f,
+ 0x01e61, 0x01e61, 0x01e63, 0x01e63, 0x01e65, 0x01e65, 0x01e67, 0x01e67,
+ 0x01e69, 0x01e69, 0x01e6b, 0x01e6b, 0x01e6d, 0x01e6d, 0x01e6f, 0x01e6f,
+ 0x01e71, 0x01e71, 0x01e73, 0x01e73, 0x01e75, 0x01e75, 0x01e77, 0x01e77,
+ 0x01e79, 0x01e79, 0x01e7b, 0x01e7b, 0x01e7d, 0x01e7d, 0x01e7f, 0x01e7f,
+};
+
+
+static const uint32_t nxt_unicode_block_03d[128] nxt_aligned(64) = {
+ 0x01e81, 0x01e81, 0x01e83, 0x01e83, 0x01e85, 0x01e85, 0x01e87, 0x01e87,
+ 0x01e89, 0x01e89, 0x01e8b, 0x01e8b, 0x01e8d, 0x01e8d, 0x01e8f, 0x01e8f,
+ 0x01e91, 0x01e91, 0x01e93, 0x01e93, 0x01e95, 0x01e95, 0x01e96, 0x01e97,
+ 0x01e98, 0x01e99, 0x01e9a, 0x01e61, 0x01e9c, 0x01e9d, 0x000df, 0x01e9f,
+ 0x01ea1, 0x01ea1, 0x01ea3, 0x01ea3, 0x01ea5, 0x01ea5, 0x01ea7, 0x01ea7,
+ 0x01ea9, 0x01ea9, 0x01eab, 0x01eab, 0x01ead, 0x01ead, 0x01eaf, 0x01eaf,
+ 0x01eb1, 0x01eb1, 0x01eb3, 0x01eb3, 0x01eb5, 0x01eb5, 0x01eb7, 0x01eb7,
+ 0x01eb9, 0x01eb9, 0x01ebb, 0x01ebb, 0x01ebd, 0x01ebd, 0x01ebf, 0x01ebf,
+ 0x01ec1, 0x01ec1, 0x01ec3, 0x01ec3, 0x01ec5, 0x01ec5, 0x01ec7, 0x01ec7,
+ 0x01ec9, 0x01ec9, 0x01ecb, 0x01ecb, 0x01ecd, 0x01ecd, 0x01ecf, 0x01ecf,
+ 0x01ed1, 0x01ed1, 0x01ed3, 0x01ed3, 0x01ed5, 0x01ed5, 0x01ed7, 0x01ed7,
+ 0x01ed9, 0x01ed9, 0x01edb, 0x01edb, 0x01edd, 0x01edd, 0x01edf, 0x01edf,
+ 0x01ee1, 0x01ee1, 0x01ee3, 0x01ee3, 0x01ee5, 0x01ee5, 0x01ee7, 0x01ee7,
+ 0x01ee9, 0x01ee9, 0x01eeb, 0x01eeb, 0x01eed, 0x01eed, 0x01eef, 0x01eef,
+ 0x01ef1, 0x01ef1, 0x01ef3, 0x01ef3, 0x01ef5, 0x01ef5, 0x01ef7, 0x01ef7,
+ 0x01ef9, 0x01ef9, 0x01efb, 0x01efb, 0x01efd, 0x01efd, 0x01eff, 0x01eff,
+};
+
+
+static const uint32_t nxt_unicode_block_03e[128] nxt_aligned(64) = {
+ 0x01f00, 0x01f01, 0x01f02, 0x01f03, 0x01f04, 0x01f05, 0x01f06, 0x01f07,
+ 0x01f00, 0x01f01, 0x01f02, 0x01f03, 0x01f04, 0x01f05, 0x01f06, 0x01f07,
+ 0x01f10, 0x01f11, 0x01f12, 0x01f13, 0x01f14, 0x01f15, 0x01f16, 0x01f17,
+ 0x01f10, 0x01f11, 0x01f12, 0x01f13, 0x01f14, 0x01f15, 0x01f1e, 0x01f1f,
+ 0x01f20, 0x01f21, 0x01f22, 0x01f23, 0x01f24, 0x01f25, 0x01f26, 0x01f27,
+ 0x01f20, 0x01f21, 0x01f22, 0x01f23, 0x01f24, 0x01f25, 0x01f26, 0x01f27,
+ 0x01f30, 0x01f31, 0x01f32, 0x01f33, 0x01f34, 0x01f35, 0x01f36, 0x01f37,
+ 0x01f30, 0x01f31, 0x01f32, 0x01f33, 0x01f34, 0x01f35, 0x01f36, 0x01f37,
+ 0x01f40, 0x01f41, 0x01f42, 0x01f43, 0x01f44, 0x01f45, 0x01f46, 0x01f47,
+ 0x01f40, 0x01f41, 0x01f42, 0x01f43, 0x01f44, 0x01f45, 0x01f4e, 0x01f4f,
+ 0x01f50, 0x01f51, 0x01f52, 0x01f53, 0x01f54, 0x01f55, 0x01f56, 0x01f57,
+ 0x01f58, 0x01f51, 0x01f5a, 0x01f53, 0x01f5c, 0x01f55, 0x01f5e, 0x01f57,
+ 0x01f60, 0x01f61, 0x01f62, 0x01f63, 0x01f64, 0x01f65, 0x01f66, 0x01f67,
+ 0x01f60, 0x01f61, 0x01f62, 0x01f63, 0x01f64, 0x01f65, 0x01f66, 0x01f67,
+ 0x01f70, 0x01f71, 0x01f72, 0x01f73, 0x01f74, 0x01f75, 0x01f76, 0x01f77,
+ 0x01f78, 0x01f79, 0x01f7a, 0x01f7b, 0x01f7c, 0x01f7d, 0x01f7e, 0x01f7f,
+};
+
+
+static const uint32_t nxt_unicode_block_03f[128] nxt_aligned(64) = {
+ 0x01f80, 0x01f81, 0x01f82, 0x01f83, 0x01f84, 0x01f85, 0x01f86, 0x01f87,
+ 0x01f80, 0x01f81, 0x01f82, 0x01f83, 0x01f84, 0x01f85, 0x01f86, 0x01f87,
+ 0x01f90, 0x01f91, 0x01f92, 0x01f93, 0x01f94, 0x01f95, 0x01f96, 0x01f97,
+ 0x01f90, 0x01f91, 0x01f92, 0x01f93, 0x01f94, 0x01f95, 0x01f96, 0x01f97,
+ 0x01fa0, 0x01fa1, 0x01fa2, 0x01fa3, 0x01fa4, 0x01fa5, 0x01fa6, 0x01fa7,
+ 0x01fa0, 0x01fa1, 0x01fa2, 0x01fa3, 0x01fa4, 0x01fa5, 0x01fa6, 0x01fa7,
+ 0x01fb0, 0x01fb1, 0x01fb2, 0x01fb3, 0x01fb4, 0x01fb5, 0x01fb6, 0x01fb7,
+ 0x01fb0, 0x01fb1, 0x01f70, 0x01f71, 0x01fb3, 0x01fbd, 0x003b9, 0x01fbf,
+ 0x01fc0, 0x01fc1, 0x01fc2, 0x01fc3, 0x01fc4, 0x01fc5, 0x01fc6, 0x01fc7,
+ 0x01f72, 0x01f73, 0x01f74, 0x01f75, 0x01fc3, 0x01fcd, 0x01fce, 0x01fcf,
+ 0x01fd0, 0x01fd1, 0x01fd2, 0x01fd3, 0x01fd4, 0x01fd5, 0x01fd6, 0x01fd7,
+ 0x01fd0, 0x01fd1, 0x01f76, 0x01f77, 0x01fdc, 0x01fdd, 0x01fde, 0x01fdf,
+ 0x01fe0, 0x01fe1, 0x01fe2, 0x01fe3, 0x01fe4, 0x01fe5, 0x01fe6, 0x01fe7,
+ 0x01fe0, 0x01fe1, 0x01f7a, 0x01f7b, 0x01fe5, 0x01fed, 0x01fee, 0x01fef,
+ 0x01ff0, 0x01ff1, 0x01ff2, 0x01ff3, 0x01ff4, 0x01ff5, 0x01ff6, 0x01ff7,
+ 0x01f78, 0x01f79, 0x01f7c, 0x01f7d, 0x01ff3, 0x01ffd, 0x01ffe, 0x01fff,
+};
+
+
+static const uint32_t nxt_unicode_block_042[128] nxt_aligned(64) = {
+ 0x02100, 0x02101, 0x02102, 0x02103, 0x02104, 0x02105, 0x02106, 0x02107,
+ 0x02108, 0x02109, 0x0210a, 0x0210b, 0x0210c, 0x0210d, 0x0210e, 0x0210f,
+ 0x02110, 0x02111, 0x02112, 0x02113, 0x02114, 0x02115, 0x02116, 0x02117,
+ 0x02118, 0x02119, 0x0211a, 0x0211b, 0x0211c, 0x0211d, 0x0211e, 0x0211f,
+ 0x02120, 0x02121, 0x02122, 0x02123, 0x02124, 0x02125, 0x003c9, 0x02127,
+ 0x02128, 0x02129, 0x0006b, 0x000e5, 0x0212c, 0x0212d, 0x0212e, 0x0212f,
+ 0x02130, 0x02131, 0x0214e, 0x02133, 0x02134, 0x02135, 0x02136, 0x02137,
+ 0x02138, 0x02139, 0x0213a, 0x0213b, 0x0213c, 0x0213d, 0x0213e, 0x0213f,
+ 0x02140, 0x02141, 0x02142, 0x02143, 0x02144, 0x02145, 0x02146, 0x02147,
+ 0x02148, 0x02149, 0x0214a, 0x0214b, 0x0214c, 0x0214d, 0x0214e, 0x0214f,
+ 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157,
+ 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f,
+ 0x02170, 0x02171, 0x02172, 0x02173, 0x02174, 0x02175, 0x02176, 0x02177,
+ 0x02178, 0x02179, 0x0217a, 0x0217b, 0x0217c, 0x0217d, 0x0217e, 0x0217f,
+ 0x02170, 0x02171, 0x02172, 0x02173, 0x02174, 0x02175, 0x02176, 0x02177,
+ 0x02178, 0x02179, 0x0217a, 0x0217b, 0x0217c, 0x0217d, 0x0217e, 0x0217f,
+};
+
+
+static const uint32_t nxt_unicode_block_043[128] nxt_aligned(64) = {
+ 0x02180, 0x02181, 0x02182, 0x02184, 0x02184, 0x02185, 0x02186, 0x02187,
+ 0x02188, 0x02189, 0x0218a, 0x0218b, 0x0218c, 0x0218d, 0x0218e, 0x0218f,
+ 0x02190, 0x02191, 0x02192, 0x02193, 0x02194, 0x02195, 0x02196, 0x02197,
+ 0x02198, 0x02199, 0x0219a, 0x0219b, 0x0219c, 0x0219d, 0x0219e, 0x0219f,
+ 0x021a0, 0x021a1, 0x021a2, 0x021a3, 0x021a4, 0x021a5, 0x021a6, 0x021a7,
+ 0x021a8, 0x021a9, 0x021aa, 0x021ab, 0x021ac, 0x021ad, 0x021ae, 0x021af,
+ 0x021b0, 0x021b1, 0x021b2, 0x021b3, 0x021b4, 0x021b5, 0x021b6, 0x021b7,
+ 0x021b8, 0x021b9, 0x021ba, 0x021bb, 0x021bc, 0x021bd, 0x021be, 0x021bf,
+ 0x021c0, 0x021c1, 0x021c2, 0x021c3, 0x021c4, 0x021c5, 0x021c6, 0x021c7,
+ 0x021c8, 0x021c9, 0x021ca, 0x021cb, 0x021cc, 0x021cd, 0x021ce, 0x021cf,
+ 0x021d0, 0x021d1, 0x021d2, 0x021d3, 0x021d4, 0x021d5, 0x021d6, 0x021d7,
+ 0x021d8, 0x021d9, 0x021da, 0x021db, 0x021dc, 0x021dd, 0x021de, 0x021df,
+ 0x021e0, 0x021e1, 0x021e2, 0x021e3, 0x021e4, 0x021e5, 0x021e6, 0x021e7,
+ 0x021e8, 0x021e9, 0x021ea, 0x021eb, 0x021ec, 0x021ed, 0x021ee, 0x021ef,
+ 0x021f0, 0x021f1, 0x021f2, 0x021f3, 0x021f4, 0x021f5, 0x021f6, 0x021f7,
+ 0x021f8, 0x021f9, 0x021fa, 0x021fb, 0x021fc, 0x021fd, 0x021fe, 0x021ff,
+};
+
+
+static const uint32_t nxt_unicode_block_049[128] nxt_aligned(64) = {
+ 0x02480, 0x02481, 0x02482, 0x02483, 0x02484, 0x02485, 0x02486, 0x02487,
+ 0x02488, 0x02489, 0x0248a, 0x0248b, 0x0248c, 0x0248d, 0x0248e, 0x0248f,
+ 0x02490, 0x02491, 0x02492, 0x02493, 0x02494, 0x02495, 0x02496, 0x02497,
+ 0x02498, 0x02499, 0x0249a, 0x0249b, 0x0249c, 0x0249d, 0x0249e, 0x0249f,
+ 0x024a0, 0x024a1, 0x024a2, 0x024a3, 0x024a4, 0x024a5, 0x024a6, 0x024a7,
+ 0x024a8, 0x024a9, 0x024aa, 0x024ab, 0x024ac, 0x024ad, 0x024ae, 0x024af,
+ 0x024b0, 0x024b1, 0x024b2, 0x024b3, 0x024b4, 0x024b5, 0x024d0, 0x024d1,
+ 0x024d2, 0x024d3, 0x024d4, 0x024d5, 0x024d6, 0x024d7, 0x024d8, 0x024d9,
+ 0x024da, 0x024db, 0x024dc, 0x024dd, 0x024de, 0x024df, 0x024e0, 0x024e1,
+ 0x024e2, 0x024e3, 0x024e4, 0x024e5, 0x024e6, 0x024e7, 0x024e8, 0x024e9,
+ 0x024d0, 0x024d1, 0x024d2, 0x024d3, 0x024d4, 0x024d5, 0x024d6, 0x024d7,
+ 0x024d8, 0x024d9, 0x024da, 0x024db, 0x024dc, 0x024dd, 0x024de, 0x024df,
+ 0x024e0, 0x024e1, 0x024e2, 0x024e3, 0x024e4, 0x024e5, 0x024e6, 0x024e7,
+ 0x024e8, 0x024e9, 0x024ea, 0x024eb, 0x024ec, 0x024ed, 0x024ee, 0x024ef,
+ 0x024f0, 0x024f1, 0x024f2, 0x024f3, 0x024f4, 0x024f5, 0x024f6, 0x024f7,
+ 0x024f8, 0x024f9, 0x024fa, 0x024fb, 0x024fc, 0x024fd, 0x024fe, 0x024ff,
+};
+
+
+static const uint32_t nxt_unicode_block_058[128] nxt_aligned(64) = {
+ 0x02c30, 0x02c31, 0x02c32, 0x02c33, 0x02c34, 0x02c35, 0x02c36, 0x02c37,
+ 0x02c38, 0x02c39, 0x02c3a, 0x02c3b, 0x02c3c, 0x02c3d, 0x02c3e, 0x02c3f,
+ 0x02c40, 0x02c41, 0x02c42, 0x02c43, 0x02c44, 0x02c45, 0x02c46, 0x02c47,
+ 0x02c48, 0x02c49, 0x02c4a, 0x02c4b, 0x02c4c, 0x02c4d, 0x02c4e, 0x02c4f,
+ 0x02c50, 0x02c51, 0x02c52, 0x02c53, 0x02c54, 0x02c55, 0x02c56, 0x02c57,
+ 0x02c58, 0x02c59, 0x02c5a, 0x02c5b, 0x02c5c, 0x02c5d, 0x02c5e, 0x02c2f,
+ 0x02c30, 0x02c31, 0x02c32, 0x02c33, 0x02c34, 0x02c35, 0x02c36, 0x02c37,
+ 0x02c38, 0x02c39, 0x02c3a, 0x02c3b, 0x02c3c, 0x02c3d, 0x02c3e, 0x02c3f,
+ 0x02c40, 0x02c41, 0x02c42, 0x02c43, 0x02c44, 0x02c45, 0x02c46, 0x02c47,
+ 0x02c48, 0x02c49, 0x02c4a, 0x02c4b, 0x02c4c, 0x02c4d, 0x02c4e, 0x02c4f,
+ 0x02c50, 0x02c51, 0x02c52, 0x02c53, 0x02c54, 0x02c55, 0x02c56, 0x02c57,
+ 0x02c58, 0x02c59, 0x02c5a, 0x02c5b, 0x02c5c, 0x02c5d, 0x02c5e, 0x02c5f,
+ 0x02c61, 0x02c61, 0x0026b, 0x01d7d, 0x0027d, 0x02c65, 0x02c66, 0x02c68,
+ 0x02c68, 0x02c6a, 0x02c6a, 0x02c6c, 0x02c6c, 0x00251, 0x00271, 0x00250,
+ 0x00252, 0x02c71, 0x02c73, 0x02c73, 0x02c74, 0x02c76, 0x02c76, 0x02c77,
+ 0x02c78, 0x02c79, 0x02c7a, 0x02c7b, 0x02c7c, 0x02c7d, 0x0023f, 0x00240,
+};
+
+
+static const uint32_t nxt_unicode_block_059[128] nxt_aligned(64) = {
+ 0x02c81, 0x02c81, 0x02c83, 0x02c83, 0x02c85, 0x02c85, 0x02c87, 0x02c87,
+ 0x02c89, 0x02c89, 0x02c8b, 0x02c8b, 0x02c8d, 0x02c8d, 0x02c8f, 0x02c8f,
+ 0x02c91, 0x02c91, 0x02c93, 0x02c93, 0x02c95, 0x02c95, 0x02c97, 0x02c97,
+ 0x02c99, 0x02c99, 0x02c9b, 0x02c9b, 0x02c9d, 0x02c9d, 0x02c9f, 0x02c9f,
+ 0x02ca1, 0x02ca1, 0x02ca3, 0x02ca3, 0x02ca5, 0x02ca5, 0x02ca7, 0x02ca7,
+ 0x02ca9, 0x02ca9, 0x02cab, 0x02cab, 0x02cad, 0x02cad, 0x02caf, 0x02caf,
+ 0x02cb1, 0x02cb1, 0x02cb3, 0x02cb3, 0x02cb5, 0x02cb5, 0x02cb7, 0x02cb7,
+ 0x02cb9, 0x02cb9, 0x02cbb, 0x02cbb, 0x02cbd, 0x02cbd, 0x02cbf, 0x02cbf,
+ 0x02cc1, 0x02cc1, 0x02cc3, 0x02cc3, 0x02cc5, 0x02cc5, 0x02cc7, 0x02cc7,
+ 0x02cc9, 0x02cc9, 0x02ccb, 0x02ccb, 0x02ccd, 0x02ccd, 0x02ccf, 0x02ccf,
+ 0x02cd1, 0x02cd1, 0x02cd3, 0x02cd3, 0x02cd5, 0x02cd5, 0x02cd7, 0x02cd7,
+ 0x02cd9, 0x02cd9, 0x02cdb, 0x02cdb, 0x02cdd, 0x02cdd, 0x02cdf, 0x02cdf,
+ 0x02ce1, 0x02ce1, 0x02ce3, 0x02ce3, 0x02ce4, 0x02ce5, 0x02ce6, 0x02ce7,
+ 0x02ce8, 0x02ce9, 0x02cea, 0x02cec, 0x02cec, 0x02cee, 0x02cee, 0x02cef,
+ 0x02cf0, 0x02cf1, 0x02cf3, 0x02cf3, 0x02cf4, 0x02cf5, 0x02cf6, 0x02cf7,
+ 0x02cf8, 0x02cf9, 0x02cfa, 0x02cfb, 0x02cfc, 0x02cfd, 0x02cfe, 0x02cff,
+};
+
+
+static const uint32_t nxt_unicode_block_14c[128] nxt_aligned(64) = {
+ 0x0a600, 0x0a601, 0x0a602, 0x0a603, 0x0a604, 0x0a605, 0x0a606, 0x0a607,
+ 0x0a608, 0x0a609, 0x0a60a, 0x0a60b, 0x0a60c, 0x0a60d, 0x0a60e, 0x0a60f,
+ 0x0a610, 0x0a611, 0x0a612, 0x0a613, 0x0a614, 0x0a615, 0x0a616, 0x0a617,
+ 0x0a618, 0x0a619, 0x0a61a, 0x0a61b, 0x0a61c, 0x0a61d, 0x0a61e, 0x0a61f,
+ 0x0a620, 0x0a621, 0x0a622, 0x0a623, 0x0a624, 0x0a625, 0x0a626, 0x0a627,
+ 0x0a628, 0x0a629, 0x0a62a, 0x0a62b, 0x0a62c, 0x0a62d, 0x0a62e, 0x0a62f,
+ 0x0a630, 0x0a631, 0x0a632, 0x0a633, 0x0a634, 0x0a635, 0x0a636, 0x0a637,
+ 0x0a638, 0x0a639, 0x0a63a, 0x0a63b, 0x0a63c, 0x0a63d, 0x0a63e, 0x0a63f,
+ 0x0a641, 0x0a641, 0x0a643, 0x0a643, 0x0a645, 0x0a645, 0x0a647, 0x0a647,
+ 0x0a649, 0x0a649, 0x0a64b, 0x0a64b, 0x0a64d, 0x0a64d, 0x0a64f, 0x0a64f,
+ 0x0a651, 0x0a651, 0x0a653, 0x0a653, 0x0a655, 0x0a655, 0x0a657, 0x0a657,
+ 0x0a659, 0x0a659, 0x0a65b, 0x0a65b, 0x0a65d, 0x0a65d, 0x0a65f, 0x0a65f,
+ 0x0a661, 0x0a661, 0x0a663, 0x0a663, 0x0a665, 0x0a665, 0x0a667, 0x0a667,
+ 0x0a669, 0x0a669, 0x0a66b, 0x0a66b, 0x0a66d, 0x0a66d, 0x0a66e, 0x0a66f,
+ 0x0a670, 0x0a671, 0x0a672, 0x0a673, 0x0a674, 0x0a675, 0x0a676, 0x0a677,
+ 0x0a678, 0x0a679, 0x0a67a, 0x0a67b, 0x0a67c, 0x0a67d, 0x0a67e, 0x0a67f,
+};
+
+
+static const uint32_t nxt_unicode_block_14d[128] nxt_aligned(64) = {
+ 0x0a681, 0x0a681, 0x0a683, 0x0a683, 0x0a685, 0x0a685, 0x0a687, 0x0a687,
+ 0x0a689, 0x0a689, 0x0a68b, 0x0a68b, 0x0a68d, 0x0a68d, 0x0a68f, 0x0a68f,
+ 0x0a691, 0x0a691, 0x0a693, 0x0a693, 0x0a695, 0x0a695, 0x0a697, 0x0a697,
+ 0x0a698, 0x0a699, 0x0a69a, 0x0a69b, 0x0a69c, 0x0a69d, 0x0a69e, 0x0a69f,
+ 0x0a6a0, 0x0a6a1, 0x0a6a2, 0x0a6a3, 0x0a6a4, 0x0a6a5, 0x0a6a6, 0x0a6a7,
+ 0x0a6a8, 0x0a6a9, 0x0a6aa, 0x0a6ab, 0x0a6ac, 0x0a6ad, 0x0a6ae, 0x0a6af,
+ 0x0a6b0, 0x0a6b1, 0x0a6b2, 0x0a6b3, 0x0a6b4, 0x0a6b5, 0x0a6b6, 0x0a6b7,
+ 0x0a6b8, 0x0a6b9, 0x0a6ba, 0x0a6bb, 0x0a6bc, 0x0a6bd, 0x0a6be, 0x0a6bf,
+ 0x0a6c0, 0x0a6c1, 0x0a6c2, 0x0a6c3, 0x0a6c4, 0x0a6c5, 0x0a6c6, 0x0a6c7,
+ 0x0a6c8, 0x0a6c9, 0x0a6ca, 0x0a6cb, 0x0a6cc, 0x0a6cd, 0x0a6ce, 0x0a6cf,
+ 0x0a6d0, 0x0a6d1, 0x0a6d2, 0x0a6d3, 0x0a6d4, 0x0a6d5, 0x0a6d6, 0x0a6d7,
+ 0x0a6d8, 0x0a6d9, 0x0a6da, 0x0a6db, 0x0a6dc, 0x0a6dd, 0x0a6de, 0x0a6df,
+ 0x0a6e0, 0x0a6e1, 0x0a6e2, 0x0a6e3, 0x0a6e4, 0x0a6e5, 0x0a6e6, 0x0a6e7,
+ 0x0a6e8, 0x0a6e9, 0x0a6ea, 0x0a6eb, 0x0a6ec, 0x0a6ed, 0x0a6ee, 0x0a6ef,
+ 0x0a6f0, 0x0a6f1, 0x0a6f2, 0x0a6f3, 0x0a6f4, 0x0a6f5, 0x0a6f6, 0x0a6f7,
+ 0x0a6f8, 0x0a6f9, 0x0a6fa, 0x0a6fb, 0x0a6fc, 0x0a6fd, 0x0a6fe, 0x0a6ff,
+};
+
+
+static const uint32_t nxt_unicode_block_14e[128] nxt_aligned(64) = {
+ 0x0a700, 0x0a701, 0x0a702, 0x0a703, 0x0a704, 0x0a705, 0x0a706, 0x0a707,
+ 0x0a708, 0x0a709, 0x0a70a, 0x0a70b, 0x0a70c, 0x0a70d, 0x0a70e, 0x0a70f,
+ 0x0a710, 0x0a711, 0x0a712, 0x0a713, 0x0a714, 0x0a715, 0x0a716, 0x0a717,
+ 0x0a718, 0x0a719, 0x0a71a, 0x0a71b, 0x0a71c, 0x0a71d, 0x0a71e, 0x0a71f,
+ 0x0a720, 0x0a721, 0x0a723, 0x0a723, 0x0a725, 0x0a725, 0x0a727, 0x0a727,
+ 0x0a729, 0x0a729, 0x0a72b, 0x0a72b, 0x0a72d, 0x0a72d, 0x0a72f, 0x0a72f,
+ 0x0a730, 0x0a731, 0x0a733, 0x0a733, 0x0a735, 0x0a735, 0x0a737, 0x0a737,
+ 0x0a739, 0x0a739, 0x0a73b, 0x0a73b, 0x0a73d, 0x0a73d, 0x0a73f, 0x0a73f,
+ 0x0a741, 0x0a741, 0x0a743, 0x0a743, 0x0a745, 0x0a745, 0x0a747, 0x0a747,
+ 0x0a749, 0x0a749, 0x0a74b, 0x0a74b, 0x0a74d, 0x0a74d, 0x0a74f, 0x0a74f,
+ 0x0a751, 0x0a751, 0x0a753, 0x0a753, 0x0a755, 0x0a755, 0x0a757, 0x0a757,
+ 0x0a759, 0x0a759, 0x0a75b, 0x0a75b, 0x0a75d, 0x0a75d, 0x0a75f, 0x0a75f,
+ 0x0a761, 0x0a761, 0x0a763, 0x0a763, 0x0a765, 0x0a765, 0x0a767, 0x0a767,
+ 0x0a769, 0x0a769, 0x0a76b, 0x0a76b, 0x0a76d, 0x0a76d, 0x0a76f, 0x0a76f,
+ 0x0a770, 0x0a771, 0x0a772, 0x0a773, 0x0a774, 0x0a775, 0x0a776, 0x0a777,
+ 0x0a778, 0x0a77a, 0x0a77a, 0x0a77c, 0x0a77c, 0x01d79, 0x0a77f, 0x0a77f,
+};
+
+
+static const uint32_t nxt_unicode_block_14f[128] nxt_aligned(64) = {
+ 0x0a781, 0x0a781, 0x0a783, 0x0a783, 0x0a785, 0x0a785, 0x0a787, 0x0a787,
+ 0x0a788, 0x0a789, 0x0a78a, 0x0a78c, 0x0a78c, 0x00265, 0x0a78e, 0x0a78f,
+ 0x0a791, 0x0a791, 0x0a793, 0x0a793, 0x0a794, 0x0a795, 0x0a796, 0x0a797,
+ 0x0a798, 0x0a799, 0x0a79a, 0x0a79b, 0x0a79c, 0x0a79d, 0x0a79e, 0x0a79f,
+ 0x0a7a1, 0x0a7a1, 0x0a7a3, 0x0a7a3, 0x0a7a5, 0x0a7a5, 0x0a7a7, 0x0a7a7,
+ 0x0a7a9, 0x0a7a9, 0x00266, 0x0a7ab, 0x0a7ac, 0x0a7ad, 0x0a7ae, 0x0a7af,
+ 0x0a7b0, 0x0a7b1, 0x0a7b2, 0x0a7b3, 0x0a7b4, 0x0a7b5, 0x0a7b6, 0x0a7b7,
+ 0x0a7b8, 0x0a7b9, 0x0a7ba, 0x0a7bb, 0x0a7bc, 0x0a7bd, 0x0a7be, 0x0a7bf,
+ 0x0a7c0, 0x0a7c1, 0x0a7c2, 0x0a7c3, 0x0a7c4, 0x0a7c5, 0x0a7c6, 0x0a7c7,
+ 0x0a7c8, 0x0a7c9, 0x0a7ca, 0x0a7cb, 0x0a7cc, 0x0a7cd, 0x0a7ce, 0x0a7cf,
+ 0x0a7d0, 0x0a7d1, 0x0a7d2, 0x0a7d3, 0x0a7d4, 0x0a7d5, 0x0a7d6, 0x0a7d7,
+ 0x0a7d8, 0x0a7d9, 0x0a7da, 0x0a7db, 0x0a7dc, 0x0a7dd, 0x0a7de, 0x0a7df,
+ 0x0a7e0, 0x0a7e1, 0x0a7e2, 0x0a7e3, 0x0a7e4, 0x0a7e5, 0x0a7e6, 0x0a7e7,
+ 0x0a7e8, 0x0a7e9, 0x0a7ea, 0x0a7eb, 0x0a7ec, 0x0a7ed, 0x0a7ee, 0x0a7ef,
+ 0x0a7f0, 0x0a7f1, 0x0a7f2, 0x0a7f3, 0x0a7f4, 0x0a7f5, 0x0a7f6, 0x0a7f7,
+ 0x0a7f8, 0x0a7f9, 0x0a7fa, 0x0a7fb, 0x0a7fc, 0x0a7fd, 0x0a7fe, 0x0a7ff,
+};
+
+
+static const uint32_t nxt_unicode_block_1fe[128] nxt_aligned(64) = {
+ 0x0ff00, 0x0ff01, 0x0ff02, 0x0ff03, 0x0ff04, 0x0ff05, 0x0ff06, 0x0ff07,
+ 0x0ff08, 0x0ff09, 0x0ff0a, 0x0ff0b, 0x0ff0c, 0x0ff0d, 0x0ff0e, 0x0ff0f,
+ 0x0ff10, 0x0ff11, 0x0ff12, 0x0ff13, 0x0ff14, 0x0ff15, 0x0ff16, 0x0ff17,
+ 0x0ff18, 0x0ff19, 0x0ff1a, 0x0ff1b, 0x0ff1c, 0x0ff1d, 0x0ff1e, 0x0ff1f,
+ 0x0ff20, 0x0ff41, 0x0ff42, 0x0ff43, 0x0ff44, 0x0ff45, 0x0ff46, 0x0ff47,
+ 0x0ff48, 0x0ff49, 0x0ff4a, 0x0ff4b, 0x0ff4c, 0x0ff4d, 0x0ff4e, 0x0ff4f,
+ 0x0ff50, 0x0ff51, 0x0ff52, 0x0ff53, 0x0ff54, 0x0ff55, 0x0ff56, 0x0ff57,
+ 0x0ff58, 0x0ff59, 0x0ff5a, 0x0ff3b, 0x0ff3c, 0x0ff3d, 0x0ff3e, 0x0ff3f,
+ 0x0ff40, 0x0ff41, 0x0ff42, 0x0ff43, 0x0ff44, 0x0ff45, 0x0ff46, 0x0ff47,
+ 0x0ff48, 0x0ff49, 0x0ff4a, 0x0ff4b, 0x0ff4c, 0x0ff4d, 0x0ff4e, 0x0ff4f,
+ 0x0ff50, 0x0ff51, 0x0ff52, 0x0ff53, 0x0ff54, 0x0ff55, 0x0ff56, 0x0ff57,
+ 0x0ff58, 0x0ff59, 0x0ff5a, 0x0ff5b, 0x0ff5c, 0x0ff5d, 0x0ff5e, 0x0ff5f,
+ 0x0ff60, 0x0ff61, 0x0ff62, 0x0ff63, 0x0ff64, 0x0ff65, 0x0ff66, 0x0ff67,
+ 0x0ff68, 0x0ff69, 0x0ff6a, 0x0ff6b, 0x0ff6c, 0x0ff6d, 0x0ff6e, 0x0ff6f,
+ 0x0ff70, 0x0ff71, 0x0ff72, 0x0ff73, 0x0ff74, 0x0ff75, 0x0ff76, 0x0ff77,
+ 0x0ff78, 0x0ff79, 0x0ff7a, 0x0ff7b, 0x0ff7c, 0x0ff7d, 0x0ff7e, 0x0ff7f,
+};
+
+
+static const uint32_t nxt_unicode_block_208[40] nxt_aligned(64) = {
+ 0x10428, 0x10429, 0x1042a, 0x1042b, 0x1042c, 0x1042d, 0x1042e, 0x1042f,
+ 0x10430, 0x10431, 0x10432, 0x10433, 0x10434, 0x10435, 0x10436, 0x10437,
+ 0x10438, 0x10439, 0x1043a, 0x1043b, 0x1043c, 0x1043d, 0x1043e, 0x1043f,
+ 0x10440, 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447,
+ 0x10448, 0x10449, 0x1044a, 0x1044b, 0x1044c, 0x1044d, 0x1044e, 0x1044f,
+};
+
+
+static const uint32_t *nxt_unicode_blocks[] nxt_aligned(64) = {
+ nxt_unicode_block_000,
+ nxt_unicode_block_001,
+ nxt_unicode_block_002,
+ nxt_unicode_block_003,
+ nxt_unicode_block_004,
+ NULL,
+ nxt_unicode_block_006,
+ nxt_unicode_block_007,
+ nxt_unicode_block_008,
+ nxt_unicode_block_009,
+ nxt_unicode_block_00a,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_021,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_03c,
+ nxt_unicode_block_03d,
+ nxt_unicode_block_03e,
+ nxt_unicode_block_03f,
+ NULL,
+ NULL,
+ nxt_unicode_block_042,
+ nxt_unicode_block_043,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_049,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_058,
+ nxt_unicode_block_059,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_14c,
+ nxt_unicode_block_14d,
+ nxt_unicode_block_14e,
+ nxt_unicode_block_14f,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_1fe,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nxt_unicode_block_208,
+};
--- /dev/null
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+# BLOCK_SIZE should be 128, 256, 512, etc. The value 128 provides
+# the minimum memory footprint for both 32-bit and 64-bit platforms.
+use constant BLOCK_SIZE => 128;
+
+my %lowcase;
+my %blocks;
+my $max_block = 0;
+my $max_lowcase = 0;
+
+while (<>) {
+ if (/^(\w+); (C|S); (\w+);/) {
+ my ($symbol, $folding) = (hex $1, hex $3);
+ $lowcase{$symbol} = $folding;
+ $blocks{int($symbol / BLOCK_SIZE)} = 1;
+
+ if ($max_lowcase < $symbol) {
+ $max_lowcase = $symbol;
+ }
+ }
+}
+
+
+my $last_block_size = $max_lowcase % BLOCK_SIZE + 1;
+
+
+for my $block (sort { $a <=> $b } keys %blocks) {
+ if ($max_block < $block) {
+ $max_block = $block;
+ }
+}
+
+
+my $blocks = scalar keys %blocks;
+
+printf("\n/*\n" .
+ " * %d %s-bytes blocks, %d pointers.\n" .
+ " * %d bytes on 32-bit platforms, %d bytes on 64-bit platforms.\n" .
+ " */\n\n",
+ $blocks, BLOCK_SIZE, $max_block + 1,
+ ($blocks - 1) * BLOCK_SIZE * 4 + $last_block_size + $max_block * 4,
+ ($blocks - 1) * BLOCK_SIZE * 4 + $last_block_size + $max_block * 8);
+
+printf("#define NXT_UNICODE_MAX_LOWCASE 0x%05x\n\n", $max_lowcase);
+printf("#define NXT_UNICODE_BLOCK_SIZE %d\n\n\n", BLOCK_SIZE);
+
+
+for my $block (sort { $a <=> $b } keys %blocks) {
+ my $block_size = ($block != $max_block) ? BLOCK_SIZE : $last_block_size;
+
+ print "static const uint32_t ";
+ printf("nxt_unicode_block_%03x[%d] nxt_aligned(64) = {",
+ $block, $block_size);
+
+ for my $c (0 .. $block_size - 1) {
+ printf "\n " if $c % 8 == 0;
+
+ my $n = $block * BLOCK_SIZE + $c;
+
+ if (exists $lowcase{$n}) {
+ printf(" 0x%05x,", $lowcase{$n});
+
+ } else {
+ #print " .......,";
+ printf(" 0x%05x,", $n);
+ }
+ }
+
+ print "\n};\n\n\n";
+}
+
+
+print "static const uint32_t *nxt_unicode_blocks[] nxt_aligned(64) = {\n";
+
+for my $block (0 .. $max_block) {
+ if (exists($blocks{$block})) {
+ printf(" nxt_unicode_block_%03x,\n", $block);
+
+ } else {
+ print " NULL,\n";
+ }
+}
+
+print "};\n";
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_utf8.h>
+
+/*
+ * The nxt_unicode_lowcase.h file is the auto-generated file from
+ * the CaseFolding-6.3.0.txt file provided by Unicode, Inc.:
+ *
+ * ./nxt_unicode_lowcase.pl CaseFolding-6.3.0.txt
+ *
+ * This file should be copied to system specific nxt_unicode_SYSTEM_lowcase.h
+ * file and utf8_file_name_test should be built with this file.
+ * Then a correct system specific file should be generated:
+ *
+ * ./build/utf8_file_name_test | ./nxt_unicode_lowcase.pl
+ *
+ * Only common and simple case foldings are supported. Full case foldings
+ * is not supported. Combined characters are also not supported.
+ */
+
+#include <nxt_unicode_lowcase.h>
+
+
+u_char *
+nxt_utf8_encode(u_char *p, uint32_t u)
+{
+ if (u < 0x80) {
+ *p++ = (u_char) (u & 0xFF);
+ return p;
+ }
+
+ if (u < 0x0800) {
+ *p++ = (u_char) (( u >> 6) | 0xC0);
+ *p++ = (u_char) (( u & 0x3f) | 0x80);
+ return p;
+ }
+
+ if (u < 0x10000) {
+ *p++ = (u_char) ( (u >> 12) | 0xE0);
+ *p++ = (u_char) (((u >> 6) & 0x3F) | 0x80);
+ *p++ = (u_char) (( u & 0x3F) | 0x80);
+ return p;
+ }
+
+ if (u < 0x110000) {
+ *p++ = (u_char) ( (u >> 18) | 0xF0);
+ *p++ = (u_char) (((u >> 12) & 0x3F) | 0x80);
+ *p++ = (u_char) (((u >> 6) & 0x3F) | 0x80);
+ *p++ = (u_char) (( u & 0x3F) | 0x80);
+ return p;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * nxt_utf8_decode() decodes UTF-8 sequences and returns a valid
+ * character 0x00 - 0x10FFFF, or 0xFFFFFFFF for invalid or overlong
+ * UTF-8 sequence.
+ */
+
+uint32_t
+nxt_utf8_decode(const u_char **start, const u_char *end)
+{
+ uint32_t u;
+
+ u = (uint32_t) **start;
+
+ if (u < 0x80) {
+ (*start)++;
+ return u;
+ }
+
+ return nxt_utf8_decode2(start, end);
+}
+
+
+/*
+ * nxt_utf8_decode2() decodes two and more bytes UTF-8 sequences only
+ * and returns a valid character 0x80 - 0x10FFFF, OR 0xFFFFFFFF for
+ * invalid or overlong UTF-8 sequence.
+ */
+
+uint32_t
+nxt_utf8_decode2(const u_char **start, const u_char *end)
+{
+ u_char c;
+ size_t n;
+ uint32_t u, overlong;
+ const u_char *p;
+
+ p = *start;
+ u = (uint32_t) *p;
+
+ if (u >= 0xE0) {
+
+ if (u >= 0xF0) {
+
+ if (nxt_slow_path(u > 0xF4)) {
+ /*
+ * The maximum valid Unicode character is 0x10FFFF
+ * which is encoded as 0xF4 0x8F 0xBF 0xBF.
+ */
+ return 0xFFFFFFFF;
+ }
+
+ u &= 0x07;
+ overlong = 0x00FFFF;
+ n = 3;
+
+ } else {
+ u &= 0x0F;
+ overlong = 0x07FF;
+ n = 2;
+ }
+
+ } else if (u >= 0xC2) {
+
+ /* 0x80 is encoded as 0xC2 0x80. */
+
+ u &= 0x1F;
+ overlong = 0x007F;
+ n = 1;
+
+ } else {
+ /* u <= 0xC2 */
+ return 0xFFFFFFFF;
+ }
+
+ p++;
+
+ if (nxt_fast_path(p + n <= end)) {
+
+ do {
+ c = *p++;
+ /*
+ * The byte must in the 0x80 - 0xBF range.
+ * Values below 0x80 become >= 0x80.
+ */
+ c = c - 0x80;
+
+ if (nxt_slow_path(c > 0x3F)) {
+ return 0xFFFFFFFF;
+ }
+
+ u = (u << 6) | c;
+ n--;
+
+ } while (n != 0);
+
+ if (overlong < u && u < 0x110000) {
+ *start = p;
+ return u;
+ }
+ }
+
+ return 0xFFFFFFFF;
+}
+
+
+/*
+ * nxt_utf8_casecmp() tests only up to the minimum of given lengths, but
+ * requires lengths of both strings because otherwise nxt_utf8_decode2()
+ * may fail due to incomplete sequence.
+ */
+
+nxt_int_t
+nxt_utf8_casecmp(const u_char *start1, const u_char *start2, size_t len1,
+ size_t len2)
+{
+ int32_t n;
+ uint32_t u1, u2;
+ const u_char *end1, *end2;
+
+ end1 = start1 + len1;
+ end2 = start2 + len2;
+
+ while (start1 < end1 && start2 < end2) {
+
+ u1 = nxt_utf8_lowcase(&start1, end1);
+
+ u2 = nxt_utf8_lowcase(&start2, end2);
+
+ if (nxt_slow_path((u1 | u2) == 0xFFFFFFFF)) {
+ return NXT_UTF8_SORT_INVALID;
+ }
+
+ n = u1 - u2;
+
+ if (n != 0) {
+ return (nxt_int_t) n;
+ }
+ }
+
+ return 0;
+}
+
+
+uint32_t
+nxt_utf8_lowcase(const u_char **start, const u_char *end)
+{
+ uint32_t u;
+ const uint32_t *block;
+
+ u = (uint32_t) **start;
+
+ if (nxt_fast_path(u < 0x80)) {
+ (*start)++;
+
+ return nxt_unicode_block_000[u];
+ }
+
+ u = nxt_utf8_decode2(start, end);
+
+ if (u <= NXT_UNICODE_MAX_LOWCASE) {
+ block = nxt_unicode_blocks[u / NXT_UNICODE_BLOCK_SIZE];
+
+ if (block != NULL) {
+ return block[u % NXT_UNICODE_BLOCK_SIZE];
+ }
+ }
+
+ return u;
+}
+
+
+ssize_t
+nxt_utf8_length(const u_char *p, size_t len)
+{
+ ssize_t length;
+ const u_char *end;
+
+ length = 0;
+
+ end = p + len;
+
+ while (p < end) {
+ if (nxt_slow_path(nxt_utf8_decode(&p, end) == 0xffffffff)) {
+ return -1;
+ }
+
+ length++;
+ }
+
+ return length;
+}
+
+
+nxt_bool_t
+nxt_utf8_is_valid(const u_char *p, size_t len)
+{
+ const u_char *end;
+
+ end = p + len;
+
+ while (p < end) {
+ if (nxt_slow_path(nxt_utf8_decode(&p, end) == 0xffffffff)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_UTF8_H_INCLUDED_
+#define _NXT_UTF8_H_INCLUDED_
+
+
+/*
+ * Since the maximum valid Unicode character is 0x0010FFFF, the maximum
+ * difference between Unicode characters is lesser 0x0010FFFF and
+ * 0x0EEE0EEE can be used as value to indicate UTF-8 encoding error.
+ */
+#define NXT_UTF8_SORT_INVALID 0x0EEE0EEE
+
+
+NXT_EXPORT u_char *nxt_utf8_encode(u_char *p, uint32_t u);
+NXT_EXPORT uint32_t nxt_utf8_decode(const u_char **start, const u_char *end);
+NXT_EXPORT uint32_t nxt_utf8_decode2(const u_char **start, const u_char *end);
+NXT_EXPORT nxt_int_t nxt_utf8_casecmp(const u_char *start1,
+ const u_char *start2, size_t len1, size_t len2);
+NXT_EXPORT uint32_t nxt_utf8_lowcase(const u_char **start, const u_char *end);
+NXT_EXPORT ssize_t nxt_utf8_length(const u_char *p, size_t len);
+NXT_EXPORT nxt_bool_t nxt_utf8_is_valid(const u_char *p, size_t len);
+
+
+/* nxt_utf8_next() expects a valid UTF-8 string. */
+
+nxt_inline const u_char *
+nxt_utf8_next(const u_char *p, const u_char *end)
+{
+ u_char c;
+
+ c = *p++;
+
+ if ((c & 0x80) != 0) {
+
+ do {
+ /*
+ * The first UTF-8 byte is either 0xxxxxxx or 11xxxxxx.
+ * The next UTF-8 bytes are 10xxxxxx.
+ */
+ c = *p;
+
+ if ((c & 0xC0) != 0x80) {
+ return p;
+ }
+
+ p++;
+
+ } while (p < end);
+ }
+
+ return p;
+}
+
+
+#endif /* _NXT_UTF8_H_INCLUDED_ */
--- /dev/null
+
+lib_test: \
+ $(NXT_BUILDDIR)/rbtree_unit_test \
+ $(NXT_BUILDDIR)/lvlhsh_unit_test \
+ $(NXT_BUILDDIR)/utf8_unit_test \
+
+ $(NXT_BUILDDIR)/rbtree_unit_test
+ $(NXT_BUILDDIR)/lvlhsh_unit_test
+ $(NXT_BUILDDIR)/utf8_unit_test
+
+$(NXT_BUILDDIR)/utf8_unit_test: \
+ $(NXT_BUILDDIR)/nxt_utf8.o \
+ $(NXT_LIB)/test/utf8_unit_test.c \
+
+ $(NXT_CC) -o $(NXT_BUILDDIR)/utf8_unit_test $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/test/utf8_unit_test.c \
+ $(NXT_BUILDDIR)/nxt_utf8.o
+
+$(NXT_BUILDDIR)/rbtree_unit_test: \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_murmur_hash.o \
+ $(NXT_LIB)/test/rbtree_unit_test.c \
+
+ $(NXT_CC) -o $(NXT_BUILDDIR)/rbtree_unit_test $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/test/rbtree_unit_test.c \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_murmur_hash.o
+
+$(NXT_BUILDDIR)/lvlhsh_unit_test: \
+ $(NXT_BUILDDIR)/nxt_lvlhsh.o \
+ $(NXT_BUILDDIR)/nxt_murmur_hash.o \
+ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
+ $(NXT_BUILDDIR)/nxt_malloc.o \
+ $(NXT_LIB)/test/lvlhsh_unit_test.c \
+
+ $(NXT_CC) -o $(NXT_BUILDDIR)/lvlhsh_unit_test $(NXT_CFLAGS) \
+ -I$(NXT_LIB) \
+ $(NXT_LIB)/test/lvlhsh_unit_test.c \
+ $(NXT_BUILDDIR)/nxt_lvlhsh.o \
+ $(NXT_BUILDDIR)/nxt_rbtree.o \
+ $(NXT_BUILDDIR)/nxt_murmur_hash.o \
+ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
+ $(NXT_BUILDDIR)/nxt_malloc.o
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_malloc.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_murmur_hash.h>
+#include <nxt_mem_cache_pool.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+
+static nxt_int_t
+lvlhsh_unit_test_key_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ if (*(uintptr_t *) lhq->key.data == (uintptr_t) data) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static void *
+lvlhsh_unit_test_pool_alloc(void *pool, size_t size, nxt_uint_t nalloc)
+{
+ return nxt_mem_cache_align(pool, size, size);
+}
+
+
+static void
+lvlhsh_unit_test_pool_free(void *pool, void *p, size_t size)
+{
+ nxt_mem_cache_free(pool, p);
+}
+
+
+static const nxt_lvlhsh_proto_t lvlhsh_proto nxt_aligned(64) = {
+ NXT_LVLHSH_LARGE_SLAB,
+ 0,
+ lvlhsh_unit_test_key_test,
+ lvlhsh_unit_test_pool_alloc,
+ lvlhsh_unit_test_pool_free,
+};
+
+
+static nxt_int_t
+lvlhsh_unit_test_add(nxt_lvlhsh_t *lh, const nxt_lvlhsh_proto_t *proto,
+ void *pool, uintptr_t key)
+{
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = key;
+ lhq.replace = 0;
+ lhq.key.len = sizeof(uintptr_t);
+ lhq.key.data = (u_char *) &key;
+ lhq.value = (void *) key;
+ lhq.proto = proto;
+ lhq.pool = pool;
+
+ switch (nxt_lvlhsh_insert(lh, &lhq)) {
+
+ case NXT_OK:
+ return NXT_OK;
+
+ case NXT_DECLINED:
+ printf("lvlhsh unit test failed: key %08lX is already in hash\n",
+ (long) key);
+
+ default:
+ return NXT_ERROR;
+ }
+}
+
+
+static nxt_int_t
+lvlhsh_unit_test_get(nxt_lvlhsh_t *lh, const nxt_lvlhsh_proto_t *proto,
+ uintptr_t key)
+{
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = key;
+ lhq.key.len = sizeof(uintptr_t);
+ lhq.key.data = (u_char *) &key;
+ lhq.proto = proto;
+
+ if (nxt_lvlhsh_find(lh, &lhq) == NXT_OK) {
+
+ if (key == (uintptr_t) lhq.value) {
+ return NXT_OK;
+ }
+ }
+
+ printf("lvlhsh unit test failed: key %08lX not found in hash\n",
+ (long) key);
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+lvlhsh_unit_test_delete(nxt_lvlhsh_t *lh, const nxt_lvlhsh_proto_t *proto,
+ void *pool, uintptr_t key)
+{
+ nxt_int_t ret;
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = key;
+ lhq.key.len = sizeof(uintptr_t);
+ lhq.key.data = (u_char *) &key;
+ lhq.proto = proto;
+ lhq.pool = pool;
+
+ ret = nxt_lvlhsh_delete(lh, &lhq);
+
+ if (ret != NXT_OK) {
+ printf("lvlhsh unit test failed: key %08lX not found in hash\n",
+ (long) key);
+ }
+
+ return ret;
+}
+
+
+static void *
+lvlhsh_malloc(void *mem, size_t size)
+{
+ return nxt_malloc(size);
+}
+
+
+static void *
+lvlhsh_zalloc(void *mem, size_t size)
+{
+ void *p;
+
+ p = nxt_malloc(size);
+
+ if (p != NULL) {
+ memset(p, 0, size);
+ }
+
+ return p;
+}
+
+
+static void *
+lvlhsh_align(void *mem, size_t alignment, size_t size)
+{
+ return nxt_memalign(alignment, size);
+}
+
+
+static void
+lvlhsh_free(void *mem, void *p)
+{
+ nxt_free(p);
+}
+
+
+static void
+lvlhsh_alert(void *mem, const char *fmt, ...)
+{
+ int n;
+ va_list args;
+ char buf[1024];
+
+ va_start(args, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ (void) printf("alert: \"%.*s\"\n", n, buf);
+}
+
+
+static const nxt_mem_proto_t mem_cache_pool_proto = {
+ lvlhsh_malloc,
+ lvlhsh_zalloc,
+ lvlhsh_align,
+ NULL,
+ lvlhsh_free,
+ lvlhsh_alert,
+ NULL,
+};
+
+
+static nxt_int_t
+lvlhsh_unit_test(nxt_uint_t n)
+{
+ uintptr_t key;
+ nxt_uint_t i;
+ nxt_lvlhsh_t lh;
+ nxt_lvlhsh_each_t lhe;
+ nxt_mem_cache_pool_t *pool;
+
+ const size_t min_chunk_size = 32;
+ const size_t page_size = 1024;
+ const size_t page_alignment = 128;
+ const size_t cluster_size = 4096;
+
+ pool = nxt_mem_cache_pool_create(&mem_cache_pool_proto, NULL, NULL,
+ cluster_size, page_alignment,
+ page_size, min_chunk_size);
+ if (pool == NULL) {
+ return NXT_ERROR;
+ }
+
+ printf("lvlhsh unit test started: %ld items\n", (long) n);
+
+ memset(&lh, 0, sizeof(nxt_lvlhsh_t));
+
+ key = 0;
+ for (i = 0; i < n; i++) {
+ key = nxt_murmur_hash2(&key, sizeof(uint32_t));
+
+ if (lvlhsh_unit_test_add(&lh, &lvlhsh_proto, pool, key) != NXT_OK) {
+ printf("lvlhsh add unit test failed at %ld\n", (long) i);
+ return NXT_ERROR;
+ }
+ }
+
+ key = 0;
+ for (i = 0; i < n; i++) {
+ key = nxt_murmur_hash2(&key, sizeof(uint32_t));
+
+ if (lvlhsh_unit_test_get(&lh, &lvlhsh_proto, key) != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+
+ memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t));
+ lhe.proto = &lvlhsh_proto;
+
+ for (i = 0; i < n + 1; i++) {
+ if (nxt_lvlhsh_each(&lh, &lhe) == NULL) {
+ break;
+ }
+ }
+
+ if (i != n) {
+ printf("lvlhsh each unit test failed at %ld of %ld\n",
+ (long) i, (long) n);
+ return NXT_ERROR;
+ }
+
+ key = 0;
+ for (i = 0; i < n; i++) {
+ key = nxt_murmur_hash2(&key, sizeof(uint32_t));
+
+ if (lvlhsh_unit_test_delete(&lh, &lvlhsh_proto, pool, key) != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+
+ if (!nxt_mem_cache_pool_is_empty(pool)) {
+ printf("mem cache pool is not empty\n");
+ return NXT_ERROR;
+ }
+
+ nxt_mem_cache_pool_destroy(pool);
+
+ printf("lvlhsh unit test passed\n");
+
+ return NXT_OK;
+}
+
+
+int
+main(void)
+{
+ return lvlhsh_unit_test(1000 * 1000);
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_rbtree.h>
+#include <nxt_murmur_hash.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+typedef struct {
+ NXT_RBTREE_NODE (node);
+ uint32_t key;
+} nxt_rbtree_test_t;
+
+
+static nxt_int_t rbtree_unit_test_comparison(nxt_rbtree_node_t *node1,
+ nxt_rbtree_node_t *node2);
+static nxt_int_t rbtree_unit_test_compare(uint32_t key1, uint32_t key2);
+static int nxt_cdecl rbtree_unit_test_sort_cmp(const void *one,
+ const void *two);
+
+
+static nxt_int_t
+rbtree_unit_test(nxt_uint_t n)
+{
+ void *mark;
+ uint32_t key, *keys;
+ nxt_uint_t i;
+ nxt_rbtree_t tree;
+ nxt_rbtree_node_t *node;
+ nxt_rbtree_test_t *items, *item;
+
+ printf("rbtree unit test started: %ld nodes\n", (long) n);
+
+ nxt_rbtree_init(&tree, rbtree_unit_test_comparison);
+
+ mark = tree.sentinel.right;
+
+ items = malloc(n * sizeof(nxt_rbtree_test_t));
+ if (items == NULL) {
+ return NXT_ERROR;
+ }
+
+ keys = malloc(n * sizeof(uint32_t));
+ if (keys == NULL) {
+ free(keys);
+ return NXT_ERROR;
+ }
+
+ key = 0;
+
+ for (i = 0; i < n; i++) {
+ key = nxt_murmur_hash2(&key, sizeof(uint32_t));
+ keys[i] = key;
+ items[i].key = key;
+ }
+
+ qsort(keys, n, sizeof(uint32_t), rbtree_unit_test_sort_cmp);
+
+ for (i = 0; i < n; i++) {
+ nxt_rbtree_insert(&tree, &items[i].node);
+ }
+
+ for (i = 0; i < n; i++) {
+ node = nxt_rbtree_find(&tree, &items[i].node);
+
+ if (node != (nxt_rbtree_node_t *) &items[i].node) {
+ printf("rbtree unit test failed: %08X not found\n", items[i].key);
+ goto fail;
+ }
+ }
+
+ i = 0;
+ node = nxt_rbtree_min(&tree);
+
+ while (nxt_rbtree_is_there_successor(&tree, node)) {
+
+ item = (nxt_rbtree_test_t *) node;
+
+ if (keys[i] != item->key) {
+ printf("rbtree unit test failed: %ld: %08X %08X\n",
+ (long) i, keys[i], item->key);
+ goto fail;
+ }
+
+ i++;
+ node = nxt_rbtree_node_successor(&tree, node);
+ }
+
+ if (i != n) {
+ printf("rbtree unit test failed: %ld\n", (long) i);
+ goto fail;
+ }
+
+ for (i = 0; i < n; i++) {
+ nxt_rbtree_delete(&tree, &items[i].node);
+ memset(&items[i], 0xA5, sizeof(nxt_rbtree_test_t));
+ }
+
+ if (!nxt_rbtree_is_empty(&tree)) {
+ printf("rbtree unit test failed: tree is not empty\n");
+ goto fail;
+ }
+
+ /* Check that the sentinel callback was not modified. */
+
+ if (mark != tree.sentinel.right) {
+ printf("rbtree sentinel unit test failed\n");
+ goto fail;
+ }
+
+ free(keys);
+ free(items);
+
+ printf("rbtree unit test passed\n");
+
+ return NXT_OK;
+
+fail:
+
+ free(keys);
+ free(items);
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+rbtree_unit_test_comparison(nxt_rbtree_node_t *node1,
+ nxt_rbtree_node_t *node2)
+{
+ nxt_rbtree_test_t *item1, *item2;
+
+ item1 = (nxt_rbtree_test_t *) node1;
+ item2 = (nxt_rbtree_test_t *) node2;
+
+ return rbtree_unit_test_compare(item1->key, item2->key);
+}
+
+
+/*
+ * Subtraction cannot be used in these comparison functions because
+ * the key values are spread uniform in whole 0 .. 2^32 range but are
+ * not grouped around some value as timeout values are.
+ */
+
+static nxt_int_t
+rbtree_unit_test_compare(uint32_t key1, uint32_t key2)
+{
+ if (key1 < key2) {
+ return -1;
+ }
+
+ if (key1 == key2) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int nxt_cdecl
+rbtree_unit_test_sort_cmp(const void *one, const void *two)
+{
+ const uint32_t *first, *second;
+
+ first = one;
+ second = two;
+
+ if (*first < *second) {
+ return -1;
+ }
+
+ if (*first == *second) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+main(void)
+{
+ return rbtree_unit_test(1000 * 1000);
+}
--- /dev/null
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_stub.h>
+#include <nxt_utf8.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#define NXT_UTF8_START_TEST 0xC2
+//#define NXT_UTF8_START_TEST 0
+
+
+static u_char invalid[] = {
+
+ /* Invalid first byte less than 0xC2. */
+ 1, 0x80, 0x00, 0x00, 0x00,
+ 1, 0xC0, 0x00, 0x00, 0x00,
+ 2, 0xC0, 0x00, 0x00, 0x00,
+ 3, 0xC0, 0x00, 0x00, 0x00,
+ 4, 0xC0, 0x00, 0x00, 0x00,
+
+ /* Invalid 0x0x110000 value. */
+ 4, 0xF4, 0x90, 0x80, 0x80,
+
+ /* Incomplete length. */
+ 2, 0xE0, 0xAF, 0xB5, 0x00,
+
+ /* Overlong values. */
+ 2, 0xC0, 0x80, 0x00, 0x00,
+ 2, 0xC1, 0xB3, 0x00, 0x00,
+ 3, 0xE0, 0x80, 0x80, 0x00,
+ 3, 0xE0, 0x81, 0xB3, 0x00,
+ 3, 0xE0, 0x90, 0x9A, 0x00,
+ 4, 0xF0, 0x80, 0x8A, 0x80,
+ 4, 0xF0, 0x80, 0x81, 0xB3,
+ 4, 0xF0, 0x80, 0xAF, 0xB5,
+};
+
+
+static nxt_int_t
+utf8_overlong(u_char *overlong, size_t len)
+{
+ u_char *p, utf8[4];
+ size_t size;
+ uint32_t u, d;
+ nxt_uint_t i;
+ const u_char *pp;
+
+ pp = overlong;
+
+ d = nxt_utf8_decode(&pp, overlong + len);
+
+ len = pp - overlong;
+
+ if (d != 0xFFFFFFFF) {
+ p = nxt_utf8_encode(utf8, d);
+
+ size = (p != NULL) ? p - utf8 : 0;
+
+ if (len != size || memcmp(overlong, utf8, size) != 0) {
+
+ u = 0;
+ for (i = 0; i < len; i++) {
+ u = (u << 8) + overlong[i];
+ }
+
+ printf("nxt_utf8_decode(%05Xd, %zd) failed: %05Xd, %zd\n",
+ u, len, d, size);
+
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+utf8_unit_test(void)
+{
+ u_char *p, utf8[4];
+ size_t len;
+ int32_t n;
+ uint32_t u, d;
+ nxt_uint_t i, k, l, m;
+ const u_char *pp;
+
+ printf("utf8 unit test started\n");
+
+ /* Test valid UTF-8. */
+
+ for (u = 0; u < 0x110000; u++) {
+
+ p = nxt_utf8_encode(utf8, u);
+
+ if (p == NULL) {
+ printf("nxt_utf8_encode(%05Xd) failed\n", u);
+ return NXT_ERROR;
+ }
+
+ pp = utf8;
+
+ d = nxt_utf8_decode(&pp, p);
+
+ if (u != d) {
+ printf("nxt_utf8_decode(%05Xd) failed: %05uxD\n", u, d);
+ return NXT_ERROR;
+ }
+ }
+
+ /* Test some invalid UTF-8. */
+
+ for (i = 0; i < sizeof(invalid); i += 5) {
+
+ len = invalid[i];
+ utf8[0] = invalid[i + 1];
+ utf8[1] = invalid[i + 2];
+ utf8[2] = invalid[i + 3];
+ utf8[3] = invalid[i + 4];
+
+ pp = utf8;
+
+ d = nxt_utf8_decode(&pp, utf8 + len);
+
+ if (d != 0xFFFFFFFF) {
+
+ u = 0;
+ for (i = 0; i < len; i++) {
+ u = (u << 8) + utf8[i];
+ }
+
+ printf("nxt_utf8_decode(%05Xd, %zd) failed: %05Xd\n", u, len, d);
+ return NXT_ERROR;
+ }
+ }
+
+ /* Test all overlong UTF-8. */
+
+ for (i = NXT_UTF8_START_TEST; i < 256; i++) {
+ utf8[0] = i;
+
+ if (utf8_overlong(utf8, 1) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ for (k = 0; k < 256; k++) {
+ utf8[1] = k;
+
+ if (utf8_overlong(utf8, 2) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ for (l = 0; l < 256; l++) {
+ utf8[2] = l;
+
+ if (utf8_overlong(utf8, 3) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+ for (m = 0; m < 256; m++) {
+ utf8[3] = m;
+
+ if (utf8_overlong(utf8, 4) != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+ }
+ }
+ }
+
+ n = nxt_utf8_casecmp((u_char *) "ABC АБВ ΑΒΓ",
+ (u_char *) "abc абв αβγ",
+ sizeof("ABC АБВ ΑΒΓ") - 1,
+ sizeof("abc абв αβγ") - 1);
+
+ if (n != 0) {
+ printf("nxt_utf8_casecmp() failed\n");
+ return NXT_ERROR;
+ }
+
+ printf("utf8 unit test passed\n");
+ return NXT_OK;
+}
+
+
+int
+main(void)
+{
+ return utf8_unit_test();
+}